From 4d23ecf213758963f727cb8e1141c67dbf425d21 Mon Sep 17 00:00:00 2001 From: mauer01 <17260044+mauer01@users.noreply.github.com> Date: Sun, 17 Aug 2025 17:20:44 +0200 Subject: [PATCH 1/4] feat: add single byte support feat: add MemoryLocation for the byte that the undo move counter gets reduced by add(Program) optionshandler add(ManageFleetingOptionsAndTriggers) feat(MemoryUtil.WriteValue): Int32[] support feat(ManageFleetingOptionsAndTriggers): SettingsValuePrimitive support rename: OptionsHandler Factory method to CreateNew feat(Datainterface): MemoryLocation joiningRoomCodeArray points at the first integer the integers are mapped with the buttons (:white_pawn: being 00 00 00 00) with index 6 being the count of already inputted integers feat(OptionsHandler): AddOption to insert LobbyCode refactor:name joiningRoomCodeArray -> capital letters for convention feat(DataInterface): MemoryLocations for end of game behaviours feat(TriggersAndFleetingOptions): Class Trigger that acts as a primitive feat(OptionsHandler): new option with the Trigger as a settingsvalue so that it runs without further input fix: formatting --- .../ManageFleetingOptionsAndTriggers.cs | 78 +++++++++++++++++++ .../OptionsHandler.cs | 75 ++++++++++++++++++ .../TriggersAndFleetingOptions/Trigger.cs | 10 +++ DataInterfaceConsole/Program.cs | 74 ++++++------------ FiveDChessDataInterface/DataInterface.cs | 16 +++- .../MemoryHelpers/MemoryUtil.cs | 47 ++++++----- 6 files changed, 225 insertions(+), 75 deletions(-) create mode 100644 DataInterfaceConsole/Actions/TriggersAndFleetingOptions/ManageFleetingOptionsAndTriggers.cs create mode 100644 DataInterfaceConsole/Actions/TriggersAndFleetingOptions/OptionsHandler.cs create mode 100644 DataInterfaceConsole/Actions/TriggersAndFleetingOptions/Trigger.cs diff --git a/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/ManageFleetingOptionsAndTriggers.cs b/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/ManageFleetingOptionsAndTriggers.cs new file mode 100644 index 0000000..c4ea176 --- /dev/null +++ b/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/ManageFleetingOptionsAndTriggers.cs @@ -0,0 +1,78 @@ +using DataInterfaceConsole.Actions.Settings; +using DataInterfaceConsole.Actions.TriggersAndFleetingOptions; +using DataInterfaceConsole.Types; +using System; +using System.Linq; + +namespace DataInterfaceConsole.Actions +{ + class ManageFleetingOptionsAndTriggers : BaseAction + { + public override string Name => "Fleeting Options and Triggers"; + + protected override void Run() + { + WriteLineIndented("Select one of the Options you want to do:"); + var options = Program.instance.oh.GetOptions(); + var width = (int)Math.Log10(options.Length) + 1; + WriteLineIndented(options.SelectMany((s, i) => $"[{(i + 1).ToString().PadLeft(width)}] {s.Name}: {s.Description}\n\tCurrent value: {s.GetValueAsString()}".Split("\n"))); + + if (int.TryParse(ConsoleNonBlocking.ReadLineBlocking(), out int input) && input > 0 && input <= options.Length) + { + var chosenSetting = options[input - 1]; + if (chosenSetting is SettingsValueWhitelisted sv) + { + WriteLineIndented("The following values are available. Please select the new desired value, and press enter:", 2); + + var allowedValues = sv.AllowedValues; + var width2 = (int)Math.Log10(allowedValues.Length) + 1; + WriteLineIndented(allowedValues.Select((s, i) => $"[{(i + 1).ToString().PadLeft(width2)}] {s}"), 2); + if (int.TryParse(ConsoleNonBlocking.ReadLineBlocking(), out int input2) && input2 > 0 && input2 <= allowedValues.Length) + { + sv.SetValue(allowedValues[input2 - 1]); + } + else + { + WriteLineIndented("Invalid input. Setting was left unchanged.", 2); + return; + } + } + else if (chosenSetting is SettingsValuePrimitive sv2) + { + WriteLineIndented($"Please enter an integer to be set as the new value, or enter 'reset' to reset the setting:"); + var str = ConsoleNonBlocking.ReadLineBlocking(); + if (int.TryParse(str, out int input2)) + { + sv2.SetPrimitive(input2); + } + else if (str.Replace("\"", null).Replace("'", null).ToLowerInvariant() == "reset") + { + sv2.SetPrimitive(null); + } + else + { + WriteLineIndented("Invalid input format. Setting was left unchanged.", 2); + return; + } + } + else if (chosenSetting is SettingsValuePrimitive sv3) + { + WriteLineIndented($"Please enter the new Data:"); + var str = ConsoleNonBlocking.ReadLineBlocking(); + sv3.SetPrimitive(str); + + } + else if (chosenSetting is SettingsValuePrimitive sv4) { } + else + { + throw new NotImplementedException("This setting type has not been implemented yet!"); + } + Program.instance.oh.WriteOptionIntoMemory(chosenSetting); + } + else + { + WriteLineIndented("Invalid setting chosen. Aborting."); + } + } + } +} diff --git a/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/OptionsHandler.cs b/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/OptionsHandler.cs new file mode 100644 index 0000000..9c282d0 --- /dev/null +++ b/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/OptionsHandler.cs @@ -0,0 +1,75 @@ +using DataInterfaceConsole.Actions.Settings; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace DataInterfaceConsole.Actions.TriggersAndFleetingOptions +{ + class OptionsHandler + { + + public ISettingsValue[] GetOptions() => this.OptionsStore.Values.ToArray(); + + private readonly Dictionary OptionsStore = new Dictionary(); // string : OptionsValue + private void AddOption(ISettingsValue s) + { + this.OptionsStore.Add(s.Id, s); + } + + private ISettingsValue GetOption(string Id) => this.OptionsStore[Id]; + + + public OptionsHandler() + { + AddOption(new SettingsValueWhitelisted("UndoSubmittedMoves", "Undo Submitted Moves", "unlocks the ability to Undo submitted moves in Single/Local Games", + new[] { "on", "off" }, "off")); + AddOption(new SettingsValuePrimitive("PasteLobbyCode", "paste the lobby code", "auto inserts the lobby code of a private match", "")); + AddOption(new SettingsValuePrimitive("ResumeGame", "Resumes finished Games", "Changes values so that the game can be resumed even though it already ended", new Trigger())); + } + + public void WriteOptionIntoMemory(ISettingsValue changedOption) + { + var di = Program.instance.di; + switch (changedOption.Id) + { + case "UndoSubmittedMoves": + di.MemLocUndoMoveReducedByValue.SetValue((byte)(changedOption.GetValueAsString() == "off" ? 0xFF : 0x00)); + break; + case "PasteLobbyCode": + string[] pieceMap = new string[] + { + ":pawn_white:", + ":knight_white:", + ":bishop_white:", + ":rook_white:", + ":queen_white:", + ":king_white:", + ":pawn_black:", + ":knight_black:", + ":bishop_black:", + ":rook_black:", + ":queen_black:", + ":king_black:" + }; + string[] value = changedOption.GetValueAsString().Trim().Replace("\\", "").Replace("\"", "").Split(" "); + int[] newValue = value.Select((curVal) => + { + return Array.IndexOf(pieceMap, curVal); + }).Append(6).ToArray(); + di.JoiningRoomCodeArray.SetValue(newValue); + break; + case "ResumeGame": + var a = new[] { di.FinishGameButton, di.EndOfGameDesc, di.BackgroundColorChange, di.PropertyAtEndOfGame }; + foreach (var curItem in a) + { + curItem.SetValue(0); + } + break; + } + } + public static OptionsHandler CreateNew() + { + return new OptionsHandler(); + } + } +} diff --git a/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/Trigger.cs b/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/Trigger.cs new file mode 100644 index 0000000..28a1e0d --- /dev/null +++ b/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/Trigger.cs @@ -0,0 +1,10 @@ +namespace DataInterfaceConsole.Actions.TriggersAndFleetingOptions +{ + public class Trigger + { + public override string ToString() + { + return "Trigger"; + } + } +} diff --git a/DataInterfaceConsole/Program.cs b/DataInterfaceConsole/Program.cs index f9b1c0b..e9085ad 100644 --- a/DataInterfaceConsole/Program.cs +++ b/DataInterfaceConsole/Program.cs @@ -1,5 +1,6 @@ using DataInterfaceConsole.Actions; using DataInterfaceConsole.Actions.Settings; +using DataInterfaceConsole.Actions.TriggersAndFleetingOptions; using DataInterfaceConsole.Types; using DataInterfaceConsole.Types.Exceptions; using FiveDChessDataInterface; @@ -10,27 +11,25 @@ namespace DataInterfaceConsole { - class Program - { + internal class Program { internal static Program instance = new Program(); private Thread backgroundThread; public DataInterface di; public SettingsHandler sh; + public OptionsHandler oh; - static void Main() + private static void Main() { ConsoleNonBlocking.Init(); Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); instance.Run(); } - private void Run() - { + private void Run() { Console.WriteLine("Some output will occasionally be provided via the console title."); this.sh = SettingsHandler.LoadOrCreateNew(); - - this.backgroundThread = new Thread(BackgroundThreadRun) - { + this.oh = OptionsHandler.CreateNew(); + this.backgroundThread = new Thread(BackgroundThreadRun) { Name = "BackgroundThread" }; this.backgroundThread.Start(); @@ -39,81 +38,59 @@ private void Run() var idWidth = (int)Math.Log10(actions.Count) + 1; Thread.Sleep(200); - while (true) - { + while (true) { if (this.di?.IsValid() != true) Console.WriteLine("Please launch the game..."); SpinWait.SpinUntil(() => this.di?.IsValid() == true); // wait till the datainterface instance is valid Console.WriteLine($"Select an action from the following (by typing the number to the left and pressing enter):\n" + $"{string.Join("\n", actions.Select(a => $"[{a.Key.ToString().PadLeft(idWidth)}] {a.Value.Name}"))}"); - try - { - while (true) - { + try { + while (true) { var choice = Util.ConsoleReadLineThrowIfDiInvalid(this.di); - if (int.TryParse(choice, out int res) && res > 0 && res <= actions.Count) - { + if (int.TryParse(choice, out int res) && res > 0 && res <= actions.Count) { var a = actions[res]; var header = $"====== Executing action: {a.Name} ======"; Console.WriteLine(header); - try - { + try { a.Run(this.di); Console.WriteLine($"Action executed. Returning to menu...\n{new string('=', header.Length)}"); Thread.Sleep(1000); - } - catch (DataInterfaceClosedException ex) - { + } catch (DataInterfaceClosedException ex) { Util.WriteColored($"Execution of the current action '{a.Name}' was aborted because the game closed or crashed: \n{ex.ToSanitizedString()}", ConsoleColor.Red); ConsoleNonBlocking.ClearInputLines(); } break; - } - else - { + } else { Console.WriteLine("Invalid number entered. Please try again."); } } - } - catch (DataInterfaceClosedException ex) - { + } catch (DataInterfaceClosedException ex) { Util.WriteColored($"Execution of the current action selection was aborted because the game closed or crashed: \n{ex.ToSanitizedString()}", ConsoleColor.Red); ConsoleNonBlocking.ClearInputLines(); } } } - private void BackgroundThreadRun() - { - while (true) - { + private void BackgroundThreadRun() { + while (true) { bool tooManyProcesses = false; - if (this.di?.IsValid() != true) - { - if (!DataInterface.TryCreateAutomatically(out this.di, out int procCnt)) - { - if (procCnt > 1) - { + if (this.di?.IsValid() != true) { + if (!DataInterface.TryCreateAutomatically(out this.di, out int procCnt)) { + if (procCnt > 1) { tooManyProcesses = true; } - } - else - { + } else { this.di.Initialize(); } } - if (tooManyProcesses) - { + if (tooManyProcesses) { SetConsoleTitleWithPrefix($"Too many game instances found!"); - } - else - { + } else { var isValid = this.di?.IsValid(); - string gameStatus = isValid switch - { + string gameStatus = isValid switch { true => $"Running - ProcessId: {this.di.GameProcess.Id}", false => "closed", null => "Not found" @@ -128,8 +105,7 @@ private void BackgroundThreadRun() } } - private void SetConsoleTitleWithPrefix(string s) - { + private void SetConsoleTitleWithPrefix(string s) { Console.Title = $"5D Data Interface Console - {s}"; } } diff --git a/FiveDChessDataInterface/DataInterface.cs b/FiveDChessDataInterface/DataInterface.cs index f55dcec..74fc67a 100644 --- a/FiveDChessDataInterface/DataInterface.cs +++ b/FiveDChessDataInterface/DataInterface.cs @@ -58,6 +58,15 @@ public class DataInterface public MemoryLocation MemLocTimeTravelAnimationEnabled { get; private set; } [RequiredForSave("CosmeticTurnOffset")] + + public MemoryLocation MemLocUndoMoveReducedByValue { get; private set; } + public MemoryLocation JoiningRoomCodeArray { get; private set; } + public MemoryLocation EndOfGameDesc { get; private set; } + public MemoryLocation FinishGameButton { get; private set; } + public MemoryLocation BackgroundColorChange { get; private set; } //This changes the Background Color sometimes when end of game is reached + public MemoryLocation PropertyAtEndOfGame { get; private set; } //this changes at end of game for some reason, not sure yet. + + public MemoryLocation MemLocCosmeticTurnOffset { get; private set; } @@ -228,7 +237,12 @@ private void CalculatePointers() this.MemLocWhiteIncrement = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0x1B0); this.MemLocBlackIncrement = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0x1B4); this.MemLocTimeTravelAnimationEnabled = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0x3E8); - + this.MemLocUndoMoveReducedByValue = new MemoryLocation(GetGameHandle(), chessboardPointerLocation - 0xE8A23); + this.JoiningRoomCodeArray = new MemoryLocation(GetGameHandle(), chessboardPointerLocation - 0xF8); + this.FinishGameButton = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0xC8); + this.EndOfGameDesc = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0xD0); + this.BackgroundColorChange = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0x3DC); + this.PropertyAtEndOfGame = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0xD4); // set to -1 for even starting timeline cnt, and to 0 for odd starting timeline cnt this.MemLocTimelineValueOffset = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, -0x34); this.MemLocWhiteTimelineCountInternal = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, -0x30); diff --git a/FiveDChessDataInterface/MemoryHelpers/MemoryUtil.cs b/FiveDChessDataInterface/MemoryHelpers/MemoryUtil.cs index ded3e24..f0f550e 100644 --- a/FiveDChessDataInterface/MemoryHelpers/MemoryUtil.cs +++ b/FiveDChessDataInterface/MemoryHelpers/MemoryUtil.cs @@ -4,22 +4,18 @@ using System.Reflection; using System.Runtime.InteropServices; -namespace FiveDChessDataInterface.MemoryHelpers -{ - public static class MemoryUtil - { +namespace FiveDChessDataInterface.MemoryHelpers{ + public static class MemoryUtil{ [Obsolete("use the overload which accepts byte?[] as a paramter instead!")] private static Dictionary FindMemoryInternal(IntPtr gameHandle, IntPtr start, uint length, byte[] bytesToFind, bool treatNop90AsWildcard) => FindMemoryInternal(gameHandle, start, length, bytesToFind.Select(x => (treatNop90AsWildcard && x == 0x90) ? (byte?)null : x).ToArray()); - private static Dictionary FindMemoryInternal(IntPtr gameHandle, IntPtr start, uint length, byte?[] bytesToFind) - { + private static Dictionary FindMemoryInternal(IntPtr gameHandle, IntPtr start, uint length, byte?[] bytesToFind){ var foundElements = new Dictionary(); var bytes = KernelMethods.ReadMemory(gameHandle, start, length); - for (int basePos = 0; basePos < bytes.Length - bytesToFind.Length; basePos++) - { + for (int basePos = 0; basePos < bytes.Length - bytesToFind.Length; basePos++){ bool found = true; for (int i = 0; i < bytesToFind.Length; i++) { @@ -53,13 +49,11 @@ public static Dictionary FindMemoryWithWildcards(IntPtr gameHand => FindMemoryInternal(gameHandle, start, length, bytesToFind); - public static T ReadValue(IntPtr gameHandle, IntPtr location) - { + public static T ReadValue(IntPtr gameHandle, IntPtr location){ var bytes = KernelMethods.ReadMemory(gameHandle, location, (uint)Marshal.SizeOf()); var t = typeof(T); - switch (Type.GetTypeCode(t)) - { + switch (Type.GetTypeCode(t)){ case TypeCode.Byte: return (dynamic)bytes[0]; @@ -94,12 +88,13 @@ public static T ReadValue(IntPtr gameHandle, IntPtr location) } } - public static void WriteValue(IntPtr handle, IntPtr location, T newValue) - { + public static void WriteValue(IntPtr handle, IntPtr location, T newValue){ var t = typeof(T); byte[] bytesToWrite = null; - switch (Type.GetTypeCode(t)) - { + switch (Type.GetTypeCode(t)){ + case TypeCode.Byte: + bytesToWrite = new byte[1] { (byte)(object)newValue }; ; + break; case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: @@ -108,27 +103,29 @@ public static void WriteValue(IntPtr handle, IntPtr location, T newValue) case TypeCode.UInt64: case TypeCode.Single: case TypeCode.Double: - bytesToWrite = BitConverter.GetBytes((dynamic)newValue); break; - + bytesToWrite = BitConverter.GetBytes((dynamic)newValue); + break; case TypeCode.Object: - switch (t.FullName) - { + switch (t.FullName){ case "System.IntPtr": bytesToWrite = BitConverter.GetBytes(((IntPtr)(object)newValue).ToInt64()); break; + case "System.Int32[]": + var intArray = (int[])(object)newValue; + for (int i = 0; i < intArray.Count(); i++) + { + KernelMethods.WriteMemory(handle, location + (i * 4), BitConverter.GetBytes(intArray[i])); + } + return; default: throw new NotImplementedException("Invalid obj type"); } break; - default: throw new NotImplementedException("Invalid type"); } - KernelMethods.WriteMemory(handle, location, bytesToWrite); } - - public static IntPtr LoadFunctionIntoMemory(string assembly, string functionName) - { + public static IntPtr LoadFunctionIntoMemory(string assembly, string functionName){ var handle = KernelMethods.LoadLibrary(assembly); return KernelMethods.GetProcAddress(handle, functionName); } From cc98a34dcb73d25c6e1b1bcf007541eae419c4cb Mon Sep 17 00:00:00 2001 From: GHXX Date: Wed, 24 Sep 2025 15:48:19 +0200 Subject: [PATCH 2/4] fix code style config --- .editorconfig | 4 ++++ FiveDChessDataInterface.sln | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0566270 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# IDE0305: Simplify collection initialization +dotnet_style_prefer_collection_expression = never diff --git a/FiveDChessDataInterface.sln b/FiveDChessDataInterface.sln index 668c17a..b2f4d39 100644 --- a/FiveDChessDataInterface.sln +++ b/FiveDChessDataInterface.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30503.244 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36518.9 d17.14 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FiveDChessDataInterface", "FiveDChessDataInterface\FiveDChessDataInterface.csproj", "{EEEE52A7-F66A-4BFC-B6C5-E36F0A852774}" EndProject @@ -18,6 +18,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataInterfaceConsole", "Dat {EEEE52A7-F66A-4BFC-B6C5-E36F0A852774} = {EEEE52A7-F66A-4BFC-B6C5-E36F0A852774} EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From b614e8d484e72b33efe04c11330878ef984e7a93 Mon Sep 17 00:00:00 2001 From: GHXX Date: Wed, 24 Sep 2025 15:50:46 +0200 Subject: [PATCH 3/4] reorganize, replace MemoryUtil.WriteValue with dedicated InlineMemoryArray class --- ...eSettings.cs => BaseManageSettingsMenu.cs} | 34 +++++--- .../EphermalSettingsContainer.cs | 51 ++++++++++++ .../ManageEphermalSettings.cs | 9 +++ .../Actions/EphermalSettings/Trigger.cs | 16 ++++ .../Actions/Settings/ISettingsContainer.cs | 5 ++ .../Actions/Settings/ISettingsValue.cs | 1 + .../Settings/ManagePersistentSettings.cs | 12 +++ ...dler.cs => PersistentSettingsContainer.cs} | 11 +-- .../Actions/Settings/SettingsValue.cs | 1 + .../Settings/SettingsValuePrimitive.cs | 9 ++- .../Settings/SettingsValueWhitelisted.cs | 8 +- .../ManageFleetingOptionsAndTriggers.cs | 78 ------------------- .../OptionsHandler.cs | 75 ------------------ .../TriggersAndFleetingOptions/Trigger.cs | 10 --- DataInterfaceConsole/Program.cs | 13 ++-- FiveDChessDataInterface/DataInterface.cs | 28 ++++--- .../MemoryHelpers/MemoryUtil.cs | 20 +++-- .../Types/InlineMemoryArray.cs | 15 ++++ 18 files changed, 187 insertions(+), 209 deletions(-) rename DataInterfaceConsole/Actions/{Settings/ManageSettings.cs => BaseManageSettingsMenu.cs} (58%) create mode 100644 DataInterfaceConsole/Actions/EphermalSettings/EphermalSettingsContainer.cs create mode 100644 DataInterfaceConsole/Actions/EphermalSettings/ManageEphermalSettings.cs create mode 100644 DataInterfaceConsole/Actions/EphermalSettings/Trigger.cs create mode 100644 DataInterfaceConsole/Actions/Settings/ISettingsContainer.cs create mode 100644 DataInterfaceConsole/Actions/Settings/ManagePersistentSettings.cs rename DataInterfaceConsole/Actions/Settings/{SettingsHandler.cs => PersistentSettingsContainer.cs} (93%) delete mode 100644 DataInterfaceConsole/Actions/TriggersAndFleetingOptions/ManageFleetingOptionsAndTriggers.cs delete mode 100644 DataInterfaceConsole/Actions/TriggersAndFleetingOptions/OptionsHandler.cs delete mode 100644 DataInterfaceConsole/Actions/TriggersAndFleetingOptions/Trigger.cs create mode 100644 FiveDChessDataInterface/Types/InlineMemoryArray.cs diff --git a/DataInterfaceConsole/Actions/Settings/ManageSettings.cs b/DataInterfaceConsole/Actions/BaseManageSettingsMenu.cs similarity index 58% rename from DataInterfaceConsole/Actions/Settings/ManageSettings.cs rename to DataInterfaceConsole/Actions/BaseManageSettingsMenu.cs index 1fdaebb..3ea9664 100644 --- a/DataInterfaceConsole/Actions/Settings/ManageSettings.cs +++ b/DataInterfaceConsole/Actions/BaseManageSettingsMenu.cs @@ -1,18 +1,24 @@ -using DataInterfaceConsole.Actions.Settings; +using DataInterfaceConsole.Actions.EphermalSettings; +using DataInterfaceConsole.Actions.Settings; using DataInterfaceConsole.Types; using System; using System.Linq; namespace DataInterfaceConsole.Actions; - -internal class ManageSettings : BaseAction { - public override string Name => "Manage Persistent Settings"; +internal abstract class BaseManageSettingsMenu : BaseAction { + public abstract ISettingsContainer SettingsHandler { get; } protected override void Run() { - WriteLineIndented("Select one of the settings that can be changed:"); - var settings = Program.instance.sh.GetSettings(); + WriteLineIndented("Select one of the following settings that you would like to change:"); + var settings = SettingsHandler.GetSettings(); var width = (int)Math.Log10(settings.Length) + 1; - WriteLineIndented(settings.SelectMany((s, i) => $"[{(i + 1).ToString().PadLeft(width)}] {s.Name}: {s.Description}\n\tCurrent value: {s.GetValueAsString()}".Split("\n"))); + WriteLineIndented(settings.SelectMany((s, i) => { + var rv = $"[{(i + 1).ToString().PadLeft(width)}] {s.Name}: {s.Description}"; + if (!s.HideOutputValue && s is not SettingsValuePrimitive) { + rv += $"\n\tCurrent value: {s.GetValueAsString()}"; + } + return rv.Split("\n"); + })); if (int.TryParse(ConsoleNonBlocking.ReadLineBlocking(), out int input) && input > 0 && input <= settings.Length) { var chosenSetting = settings[input - 1]; @@ -24,6 +30,7 @@ protected override void Run() { WriteLineIndented(allowedValues.Select((s, i) => $"[{(i + 1).ToString().PadLeft(width2)}] {s}"), 2); if (int.TryParse(ConsoleNonBlocking.ReadLineBlocking(), out int input2) && input2 > 0 && input2 <= allowedValues.Length) { sv.SetValue(allowedValues[input2 - 1]); + sv.OnSettingChanged(); } else { WriteLineIndented("Invalid input. Setting was left unchanged.", 2); return; @@ -33,16 +40,25 @@ protected override void Run() { var str = ConsoleNonBlocking.ReadLineBlocking(); if (int.TryParse(str, out int input2)) { sv2.SetPrimitive(input2); - } else if (str.Replace("\"", null).Replace("'", null).ToLowerInvariant() == "reset") { + sv2.OnSettingChanged(); + } else if (str.Replace("\"", null).Replace("'", null).Equals("reset", StringComparison.InvariantCultureIgnoreCase)) { sv2.SetPrimitive(null); + sv2.OnSettingChanged(); } else { WriteLineIndented("Invalid input format. Setting was left unchanged.", 2); return; } + } else if (chosenSetting is SettingsValuePrimitive sv3) { + WriteLineIndented($"Please enter a string to be set as the new value, or enter 'reset' to reset the setting:"); + var str = ConsoleNonBlocking.ReadLineBlocking(); + sv3.SetPrimitive(str); + sv3.OnSettingChanged(); + } else if (chosenSetting is SettingsValuePrimitive sv4) { + sv4.Value.Run(); } else { throw new NotImplementedException("This setting type has not been implemented yet!"); } - Program.instance.sh.Save(); + SettingsHandler.OnSettingsChanged(); } else { WriteLineIndented("Invalid setting chosen. Aborting."); } diff --git a/DataInterfaceConsole/Actions/EphermalSettings/EphermalSettingsContainer.cs b/DataInterfaceConsole/Actions/EphermalSettings/EphermalSettingsContainer.cs new file mode 100644 index 0000000..2902b73 --- /dev/null +++ b/DataInterfaceConsole/Actions/EphermalSettings/EphermalSettingsContainer.cs @@ -0,0 +1,51 @@ +using DataInterfaceConsole.Actions.Settings; +using FiveDChessDataInterface; +using FiveDChessDataInterface.Types; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace DataInterfaceConsole.Actions.EphermalSettings; +internal class EphermalSettingsContainer : ISettingsContainer { + public ISettingsValue[] GetSettings() => this.OptionsStore.Values.ToArray(); + + private readonly Dictionary OptionsStore = new Dictionary(); // string : OptionsValue + private void AddOption(ISettingsValue s) { + this.OptionsStore.Add(s.Id, s); + } + + private DataInterface Di => Program.instance.di; + + public EphermalSettingsContainer() { + AddOption(new SettingsValueWhitelisted("UndoSubmittedMoves", "Allow Undoing Submitted Moves", "Allows Undoing submitted moves in Singleplayer Games (Local/CPU)", + ["on", "off"], "off", @this => Di.MemLocUndoMoveReducedByValue.SetValue((byte)(@this.GetValueAsString() == "off" ? 0xFF : 0x00)))); + AddOption(new SettingsValuePrimitive("PasteLobbyCode", "Set private match code", "Sets the ingame private match code used for joining someone else's game", "", + @this => { + string[] pieceMap = [ + ":pawn_white:", + ":knight_white:", + ":bishop_white:", + ":rook_white:", + ":queen_white:", + ":king_white:", + ":pawn_black:", + ":knight_black:", + ":bishop_black:", + ":rook_black:", + ":queen_black:", + ":king_black:" + ]; + + // this last integer (6) is the amount of already entered digits + int[] value = @this.GetValueAsString().Trim().Replace("\\", "").Replace("\"", "").Split(" ").Select(x => Array.IndexOf(pieceMap, x)).Append(6).ToArray(); + Di.MemLocJoiningRoomCodeArray.SetValue(new InlineMemoryArray(value)); + @this.SetPrimitive(""); + }) { HideOutputValue = true }); + AddOption(new SettingsValuePrimitive("ResumeGame", "Resume finished Game", "Restores the game to a state where an already finished game can be continued as if it never ended", + new Trigger(() => { + foreach (var curItem in new[] { Di.MemLocShowFinishGameButton, Di.MemLocShowEndOfGameDesc, Di.MemLocBackgroundColorChange, Di.MemLocPropertyAtEndOfGame }) { + curItem.SetValue(0); + } + }))); + } +} diff --git a/DataInterfaceConsole/Actions/EphermalSettings/ManageEphermalSettings.cs b/DataInterfaceConsole/Actions/EphermalSettings/ManageEphermalSettings.cs new file mode 100644 index 0000000..644c8ed --- /dev/null +++ b/DataInterfaceConsole/Actions/EphermalSettings/ManageEphermalSettings.cs @@ -0,0 +1,9 @@ +using DataInterfaceConsole.Actions.Settings; + +namespace DataInterfaceConsole.Actions.EphermalSettings; + +internal class ManageEphermalSettings : BaseManageSettingsMenu { + public override string Name => "Ephermal Settings and Triggers"; + + public override ISettingsContainer SettingsHandler => Program.instance.eh; +} diff --git a/DataInterfaceConsole/Actions/EphermalSettings/Trigger.cs b/DataInterfaceConsole/Actions/EphermalSettings/Trigger.cs new file mode 100644 index 0000000..5513376 --- /dev/null +++ b/DataInterfaceConsole/Actions/EphermalSettings/Trigger.cs @@ -0,0 +1,16 @@ +using System; + +namespace DataInterfaceConsole.Actions.EphermalSettings; +public class Trigger { + private readonly Action action; + + public override string ToString() { + return "Trigger"; + } + + public Trigger(Action action) { + this.action = action; + } + + public void Run() => action.Invoke(); +} diff --git a/DataInterfaceConsole/Actions/Settings/ISettingsContainer.cs b/DataInterfaceConsole/Actions/Settings/ISettingsContainer.cs new file mode 100644 index 0000000..01dc4e2 --- /dev/null +++ b/DataInterfaceConsole/Actions/Settings/ISettingsContainer.cs @@ -0,0 +1,5 @@ +namespace DataInterfaceConsole.Actions.Settings; +internal interface ISettingsContainer { + ISettingsValue[] GetSettings(); + void OnSettingsChanged() { } +} diff --git a/DataInterfaceConsole/Actions/Settings/ISettingsValue.cs b/DataInterfaceConsole/Actions/Settings/ISettingsValue.cs index 77fa502..f33e54a 100644 --- a/DataInterfaceConsole/Actions/Settings/ISettingsValue.cs +++ b/DataInterfaceConsole/Actions/Settings/ISettingsValue.cs @@ -9,4 +9,5 @@ public interface ISettingsValue { void SetValueDirect(JToken newValue); object GetValue(); string GetValueAsString(); + bool HideOutputValue { get; } } diff --git a/DataInterfaceConsole/Actions/Settings/ManagePersistentSettings.cs b/DataInterfaceConsole/Actions/Settings/ManagePersistentSettings.cs new file mode 100644 index 0000000..913cbfe --- /dev/null +++ b/DataInterfaceConsole/Actions/Settings/ManagePersistentSettings.cs @@ -0,0 +1,12 @@ +using DataInterfaceConsole.Actions.Settings; +using DataInterfaceConsole.Types; +using System; +using System.Linq; + +namespace DataInterfaceConsole.Actions; + +internal class ManagePersistentSettings : BaseManageSettingsMenu { + public override string Name => "Manage Persistent Settings"; + + public override ISettingsContainer SettingsHandler => Program.instance.sh; +} diff --git a/DataInterfaceConsole/Actions/Settings/SettingsHandler.cs b/DataInterfaceConsole/Actions/Settings/PersistentSettingsContainer.cs similarity index 93% rename from DataInterfaceConsole/Actions/Settings/SettingsHandler.cs rename to DataInterfaceConsole/Actions/Settings/PersistentSettingsContainer.cs index 53ae976..3fab74f 100644 --- a/DataInterfaceConsole/Actions/Settings/SettingsHandler.cs +++ b/DataInterfaceConsole/Actions/Settings/PersistentSettingsContainer.cs @@ -7,7 +7,7 @@ namespace DataInterfaceConsole.Actions.Settings; -internal class SettingsHandler { +internal class PersistentSettingsContainer : ISettingsContainer { private const string settingsFilePath = "settings.json"; public ISettingsValue[] GetSettings() => this.settingsStore.Values.ToArray(); @@ -20,9 +20,9 @@ private void AddSetting(ISettingsValue s) { private ISettingsValue GetSetting(string Id) => this.settingsStore[Id]; - public SettingsHandler() { + public PersistentSettingsContainer() { AddSetting(new SettingsValueWhitelisted("ForceTimetravelAnimationValue", "Force timetravel animation value", "Whether or not to force the timetravel button to a certain state", - new[] { "ignore", "always_on", "always_off" }, "ignore")); + ["ignore", "always_on", "always_off"], "ignore")); AddSetting(new SettingsValuePrimitive("Clock1BaseTime", "Short Timer Base Time", "The base time of the first clock in total seconds", null)); AddSetting(new SettingsValuePrimitive("Clock1Increment", "Short Timer Increment", "The increment of the first clock in seconds", null)); @@ -33,8 +33,8 @@ public SettingsHandler() { } - public static SettingsHandler LoadOrCreateNew() { - var sh = new SettingsHandler(); + public static PersistentSettingsContainer LoadOrCreateNew() { + var sh = new PersistentSettingsContainer(); if (File.Exists(settingsFilePath)) { var str = File.ReadAllText(settingsFilePath); @@ -49,6 +49,7 @@ public static SettingsHandler LoadOrCreateNew() { return sh; } + void ISettingsContainer.OnSettingsChanged() => Save(); public void Save() { var str = JsonConvert.SerializeObject(this.settingsStore.ToDictionary(x => x.Key, x => x.Value.GetValue()), Formatting.Indented); File.WriteAllText(settingsFilePath, str); diff --git a/DataInterfaceConsole/Actions/Settings/SettingsValue.cs b/DataInterfaceConsole/Actions/Settings/SettingsValue.cs index eae5f16..2909a7c 100644 --- a/DataInterfaceConsole/Actions/Settings/SettingsValue.cs +++ b/DataInterfaceConsole/Actions/Settings/SettingsValue.cs @@ -16,6 +16,7 @@ internal sealed class SettingsValue : ISettingsValue { public string Description { get; } public T Value { get; private set; } + public bool HideOutputValue { get; init; } = false; private SettingsValue(string id, string name, string description) { Id = id; diff --git a/DataInterfaceConsole/Actions/Settings/SettingsValuePrimitive.cs b/DataInterfaceConsole/Actions/Settings/SettingsValuePrimitive.cs index 8da5678..2b620f9 100644 --- a/DataInterfaceConsole/Actions/Settings/SettingsValuePrimitive.cs +++ b/DataInterfaceConsole/Actions/Settings/SettingsValuePrimitive.cs @@ -1,19 +1,24 @@ using Newtonsoft.Json.Linq; +using System; namespace DataInterfaceConsole.Actions.Settings; internal class SettingsValuePrimitive : ISettingsValue { + private readonly Action> onSettingChanged; + public string Id { get; } public string Name { get; } public string Description { get; } public T Value { get; private set; } + public bool HideOutputValue { get; init; } = false; - public SettingsValuePrimitive(string id, string name, string description, T defaultValue) { + public SettingsValuePrimitive(string id, string name, string description, T defaultValue, Action> onSettingChanged = null) { Id = id; Name = name; Description = description; Value = defaultValue; + this.onSettingChanged = onSettingChanged; } public object GetValue() { @@ -29,4 +34,6 @@ public string GetValueAsString() { public void SetValueDirect(JToken newValue) { Value = newValue.Value(); } + + public void OnSettingChanged() => this.onSettingChanged?.Invoke(this); } diff --git a/DataInterfaceConsole/Actions/Settings/SettingsValueWhitelisted.cs b/DataInterfaceConsole/Actions/Settings/SettingsValueWhitelisted.cs index 7a094ef..3487d21 100644 --- a/DataInterfaceConsole/Actions/Settings/SettingsValueWhitelisted.cs +++ b/DataInterfaceConsole/Actions/Settings/SettingsValueWhitelisted.cs @@ -5,6 +5,8 @@ namespace DataInterfaceConsole.Actions.Settings; internal class SettingsValueWhitelisted : ISettingsValue { + private readonly Action> onSettingChanged; + public string Id { get; } public string Name { get; } public string Description { get; } @@ -12,6 +14,7 @@ internal class SettingsValueWhitelisted : ISettingsValue { public T[] AllowedValues { get; private set; } public T Value { get; private set; } + public bool HideOutputValue { get; init; } = false; public object GetValue() { return Value; @@ -33,11 +36,14 @@ public string GetValueAsString() { return Value.ToString(); } - public SettingsValueWhitelisted(string id, string name, string description, T[] allowedValues, T defaultValue) { + public SettingsValueWhitelisted(string id, string name, string description, T[] allowedValues, T defaultValue, Action> onSettingChanged = null) { Id = id; Name = name; Description = description; AllowedValues = allowedValues; + this.onSettingChanged = onSettingChanged; SetValue(defaultValue); } + + public void OnSettingChanged() => this.onSettingChanged?.Invoke(this); } diff --git a/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/ManageFleetingOptionsAndTriggers.cs b/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/ManageFleetingOptionsAndTriggers.cs deleted file mode 100644 index c4ea176..0000000 --- a/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/ManageFleetingOptionsAndTriggers.cs +++ /dev/null @@ -1,78 +0,0 @@ -using DataInterfaceConsole.Actions.Settings; -using DataInterfaceConsole.Actions.TriggersAndFleetingOptions; -using DataInterfaceConsole.Types; -using System; -using System.Linq; - -namespace DataInterfaceConsole.Actions -{ - class ManageFleetingOptionsAndTriggers : BaseAction - { - public override string Name => "Fleeting Options and Triggers"; - - protected override void Run() - { - WriteLineIndented("Select one of the Options you want to do:"); - var options = Program.instance.oh.GetOptions(); - var width = (int)Math.Log10(options.Length) + 1; - WriteLineIndented(options.SelectMany((s, i) => $"[{(i + 1).ToString().PadLeft(width)}] {s.Name}: {s.Description}\n\tCurrent value: {s.GetValueAsString()}".Split("\n"))); - - if (int.TryParse(ConsoleNonBlocking.ReadLineBlocking(), out int input) && input > 0 && input <= options.Length) - { - var chosenSetting = options[input - 1]; - if (chosenSetting is SettingsValueWhitelisted sv) - { - WriteLineIndented("The following values are available. Please select the new desired value, and press enter:", 2); - - var allowedValues = sv.AllowedValues; - var width2 = (int)Math.Log10(allowedValues.Length) + 1; - WriteLineIndented(allowedValues.Select((s, i) => $"[{(i + 1).ToString().PadLeft(width2)}] {s}"), 2); - if (int.TryParse(ConsoleNonBlocking.ReadLineBlocking(), out int input2) && input2 > 0 && input2 <= allowedValues.Length) - { - sv.SetValue(allowedValues[input2 - 1]); - } - else - { - WriteLineIndented("Invalid input. Setting was left unchanged.", 2); - return; - } - } - else if (chosenSetting is SettingsValuePrimitive sv2) - { - WriteLineIndented($"Please enter an integer to be set as the new value, or enter 'reset' to reset the setting:"); - var str = ConsoleNonBlocking.ReadLineBlocking(); - if (int.TryParse(str, out int input2)) - { - sv2.SetPrimitive(input2); - } - else if (str.Replace("\"", null).Replace("'", null).ToLowerInvariant() == "reset") - { - sv2.SetPrimitive(null); - } - else - { - WriteLineIndented("Invalid input format. Setting was left unchanged.", 2); - return; - } - } - else if (chosenSetting is SettingsValuePrimitive sv3) - { - WriteLineIndented($"Please enter the new Data:"); - var str = ConsoleNonBlocking.ReadLineBlocking(); - sv3.SetPrimitive(str); - - } - else if (chosenSetting is SettingsValuePrimitive sv4) { } - else - { - throw new NotImplementedException("This setting type has not been implemented yet!"); - } - Program.instance.oh.WriteOptionIntoMemory(chosenSetting); - } - else - { - WriteLineIndented("Invalid setting chosen. Aborting."); - } - } - } -} diff --git a/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/OptionsHandler.cs b/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/OptionsHandler.cs deleted file mode 100644 index 9c282d0..0000000 --- a/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/OptionsHandler.cs +++ /dev/null @@ -1,75 +0,0 @@ -using DataInterfaceConsole.Actions.Settings; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace DataInterfaceConsole.Actions.TriggersAndFleetingOptions -{ - class OptionsHandler - { - - public ISettingsValue[] GetOptions() => this.OptionsStore.Values.ToArray(); - - private readonly Dictionary OptionsStore = new Dictionary(); // string : OptionsValue - private void AddOption(ISettingsValue s) - { - this.OptionsStore.Add(s.Id, s); - } - - private ISettingsValue GetOption(string Id) => this.OptionsStore[Id]; - - - public OptionsHandler() - { - AddOption(new SettingsValueWhitelisted("UndoSubmittedMoves", "Undo Submitted Moves", "unlocks the ability to Undo submitted moves in Single/Local Games", - new[] { "on", "off" }, "off")); - AddOption(new SettingsValuePrimitive("PasteLobbyCode", "paste the lobby code", "auto inserts the lobby code of a private match", "")); - AddOption(new SettingsValuePrimitive("ResumeGame", "Resumes finished Games", "Changes values so that the game can be resumed even though it already ended", new Trigger())); - } - - public void WriteOptionIntoMemory(ISettingsValue changedOption) - { - var di = Program.instance.di; - switch (changedOption.Id) - { - case "UndoSubmittedMoves": - di.MemLocUndoMoveReducedByValue.SetValue((byte)(changedOption.GetValueAsString() == "off" ? 0xFF : 0x00)); - break; - case "PasteLobbyCode": - string[] pieceMap = new string[] - { - ":pawn_white:", - ":knight_white:", - ":bishop_white:", - ":rook_white:", - ":queen_white:", - ":king_white:", - ":pawn_black:", - ":knight_black:", - ":bishop_black:", - ":rook_black:", - ":queen_black:", - ":king_black:" - }; - string[] value = changedOption.GetValueAsString().Trim().Replace("\\", "").Replace("\"", "").Split(" "); - int[] newValue = value.Select((curVal) => - { - return Array.IndexOf(pieceMap, curVal); - }).Append(6).ToArray(); - di.JoiningRoomCodeArray.SetValue(newValue); - break; - case "ResumeGame": - var a = new[] { di.FinishGameButton, di.EndOfGameDesc, di.BackgroundColorChange, di.PropertyAtEndOfGame }; - foreach (var curItem in a) - { - curItem.SetValue(0); - } - break; - } - } - public static OptionsHandler CreateNew() - { - return new OptionsHandler(); - } - } -} diff --git a/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/Trigger.cs b/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/Trigger.cs deleted file mode 100644 index 28a1e0d..0000000 --- a/DataInterfaceConsole/Actions/TriggersAndFleetingOptions/Trigger.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace DataInterfaceConsole.Actions.TriggersAndFleetingOptions -{ - public class Trigger - { - public override string ToString() - { - return "Trigger"; - } - } -} diff --git a/DataInterfaceConsole/Program.cs b/DataInterfaceConsole/Program.cs index 3e27226..592e83c 100644 --- a/DataInterfaceConsole/Program.cs +++ b/DataInterfaceConsole/Program.cs @@ -1,6 +1,6 @@ using DataInterfaceConsole.Actions; +using DataInterfaceConsole.Actions.EphermalSettings; using DataInterfaceConsole.Actions.Settings; -using DataInterfaceConsole.Actions.TriggersAndFleetingOptions; using DataInterfaceConsole.Types; using DataInterfaceConsole.Types.Exceptions; using FiveDChessDataInterface; @@ -15,11 +15,10 @@ internal class Program { internal static Program instance = new Program(); private Thread backgroundThread; public DataInterface di; - public SettingsHandler sh; - public OptionsHandler oh; + public PersistentSettingsContainer sh; + public EphermalSettingsContainer eh; - private static void Main() - { + private static void Main() { ConsoleNonBlocking.Init(); Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); instance.Run(); @@ -27,8 +26,8 @@ private static void Main() private void Run() { Console.WriteLine("Some output will occasionally be provided via the console title."); - this.sh = SettingsHandler.LoadOrCreateNew(); - this.oh = OptionsHandler.CreateNew(); + this.sh = PersistentSettingsContainer.LoadOrCreateNew(); + this.eh = new EphermalSettingsContainer(); this.backgroundThread = new Thread(BackgroundThreadRun) { Name = "BackgroundThread" diff --git a/FiveDChessDataInterface/DataInterface.cs b/FiveDChessDataInterface/DataInterface.cs index fd44f04..64c1123 100644 --- a/FiveDChessDataInterface/DataInterface.cs +++ b/FiveDChessDataInterface/DataInterface.cs @@ -55,16 +55,20 @@ public class DataInterface { public MemoryLocation MemLocBlackIncrement { get; private set; } public MemoryLocation MemLocTimeTravelAnimationEnabled { get; private set; } - [RequiredForSave("CosmeticTurnOffset")] - public MemoryLocation MemLocUndoMoveReducedByValue { get; private set; } - public MemoryLocation JoiningRoomCodeArray { get; private set; } - public MemoryLocation EndOfGameDesc { get; private set; } - public MemoryLocation FinishGameButton { get; private set; } - public MemoryLocation BackgroundColorChange { get; private set; } //This changes the Background Color sometimes when end of game is reached - public MemoryLocation PropertyAtEndOfGame { get; private set; } //this changes at end of game for some reason, not sure yet. + public MemoryLocation MemLocUndoMoveReducedByValue { get; private set; } // setting this to 0 allows pressing Undo an arbitrary amount of times + /// + /// Sequential, inline (no derefs) storage of the 6 piece values that make up the code, followed by an int32 specifying the numberof digits that were already entered + /// + public MemoryLocation> MemLocJoiningRoomCodeArray { get; private set; } + public MemoryLocation MemLocShowEndOfGameDesc { get; private set; } // Whether to show the text that states "game finished, black/white won", or "puzzle incomplete/complete" + public MemoryLocation MemLocShowFinishGameButton { get; private set; } + public MemoryLocation MemLocBackgroundColorChange { get; private set; } // This changes the Background Color sometimes when end of game is reached + public MemoryLocation MemLocPropertyAtEndOfGame { get; private set; } // Some property that also changes when the game ends, though currently unknown what it actually does + + [RequiredForSave("CosmeticTurnOffset")] public MemoryLocation MemLocCosmeticTurnOffset { get; private set; } @@ -232,11 +236,11 @@ private void CalculatePointers() { MemLocBlackIncrement = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0x1B4); MemLocTimeTravelAnimationEnabled = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0x3E8); MemLocUndoMoveReducedByValue = new MemoryLocation(GetGameHandle(), chessboardPointerLocation - 0xE8A23); - JoiningRoomCodeArray = new MemoryLocation(GetGameHandle(), chessboardPointerLocation - 0xF8); - FinishGameButton = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0xC8); - EndOfGameDesc = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0xD0); - BackgroundColorChange = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0x3DC); - PropertyAtEndOfGame = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0xD4); + MemLocJoiningRoomCodeArray = new MemoryLocation>(GetGameHandle(), chessboardPointerLocation - 0xF8); + MemLocShowFinishGameButton = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0xC8); + MemLocShowEndOfGameDesc = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0xD0); + MemLocBackgroundColorChange = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0x3DC); + MemLocPropertyAtEndOfGame = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, 0xD4); // set to -1 for even starting timeline cnt, and to 0 for odd starting timeline cnt MemLocTimelineValueOffset = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, -0x34); MemLocWhiteTimelineCountInternal = new MemoryLocation(GetGameHandle(), chessboardPointerLocation, -0x30); diff --git a/FiveDChessDataInterface/MemoryHelpers/MemoryUtil.cs b/FiveDChessDataInterface/MemoryHelpers/MemoryUtil.cs index 5ccdf77..157ab1f 100644 --- a/FiveDChessDataInterface/MemoryHelpers/MemoryUtil.cs +++ b/FiveDChessDataInterface/MemoryHelpers/MemoryUtil.cs @@ -1,4 +1,5 @@ -using System; +using FiveDChessDataInterface.Types; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; @@ -101,16 +102,13 @@ public static void WriteValue(IntPtr handle, IntPtr location, T newValue) { bytesToWrite = BitConverter.GetBytes((dynamic)newValue); break; case TypeCode.Object: - switch (t.FullName) { - case "System.IntPtr": - bytesToWrite = BitConverter.GetBytes(((IntPtr)(object)newValue).ToInt64()); break; - case "System.Int32[]": - var intArray = (int[])(object)newValue; - for (int i = 0; i < intArray.Count(); i++) - { - KernelMethods.WriteMemory(handle, location + (i * 4), BitConverter.GetBytes(intArray[i])); - } - return; + switch (newValue) { + case IntPtr v: + bytesToWrite = BitConverter.GetBytes(v.ToInt64()); + break; + case InlineMemoryArray v: + bytesToWrite = v.backing.SelectMany(x => BitConverter.GetBytes(x)).ToArray(); + break; default: throw new NotImplementedException("Invalid obj type"); } diff --git a/FiveDChessDataInterface/Types/InlineMemoryArray.cs b/FiveDChessDataInterface/Types/InlineMemoryArray.cs new file mode 100644 index 0000000..4ff9a2d --- /dev/null +++ b/FiveDChessDataInterface/Types/InlineMemoryArray.cs @@ -0,0 +1,15 @@ +using System; + +namespace FiveDChessDataInterface.Types { + /// + /// Used by , + /// Represents a series of elements of type T, stored sequentially, located DIRECTLY at the position and not representing a by-ref-array. + /// + public class InlineMemoryArray { + internal readonly T[] backing; + + public InlineMemoryArray(T[] backing) { + this.backing = backing; + } + } +} From d89a405a1798557712e339c415e2c6cfa6b346ca Mon Sep 17 00:00:00 2001 From: GHXX Date: Wed, 24 Sep 2025 16:05:06 +0200 Subject: [PATCH 4/4] fix spelling --- DataInterfaceConsole/Actions/BaseManageSettingsMenu.cs | 2 +- .../EphemeralSettingsContainer.cs} | 6 +++--- .../Actions/EphemeralSettings/ManageEphemeralSettings.cs | 9 +++++++++ .../{EphermalSettings => EphemeralSettings}/Trigger.cs | 2 +- .../Actions/EphermalSettings/ManageEphermalSettings.cs | 9 --------- DataInterfaceConsole/Program.cs | 6 +++--- 6 files changed, 17 insertions(+), 17 deletions(-) rename DataInterfaceConsole/Actions/{EphermalSettings/EphermalSettingsContainer.cs => EphemeralSettings/EphemeralSettingsContainer.cs} (93%) create mode 100644 DataInterfaceConsole/Actions/EphemeralSettings/ManageEphemeralSettings.cs rename DataInterfaceConsole/Actions/{EphermalSettings => EphemeralSettings}/Trigger.cs (82%) delete mode 100644 DataInterfaceConsole/Actions/EphermalSettings/ManageEphermalSettings.cs diff --git a/DataInterfaceConsole/Actions/BaseManageSettingsMenu.cs b/DataInterfaceConsole/Actions/BaseManageSettingsMenu.cs index 3ea9664..f2dc3ab 100644 --- a/DataInterfaceConsole/Actions/BaseManageSettingsMenu.cs +++ b/DataInterfaceConsole/Actions/BaseManageSettingsMenu.cs @@ -1,4 +1,4 @@ -using DataInterfaceConsole.Actions.EphermalSettings; +using DataInterfaceConsole.Actions.EphemeralSettings; using DataInterfaceConsole.Actions.Settings; using DataInterfaceConsole.Types; using System; diff --git a/DataInterfaceConsole/Actions/EphermalSettings/EphermalSettingsContainer.cs b/DataInterfaceConsole/Actions/EphemeralSettings/EphemeralSettingsContainer.cs similarity index 93% rename from DataInterfaceConsole/Actions/EphermalSettings/EphermalSettingsContainer.cs rename to DataInterfaceConsole/Actions/EphemeralSettings/EphemeralSettingsContainer.cs index 2902b73..97562e5 100644 --- a/DataInterfaceConsole/Actions/EphermalSettings/EphermalSettingsContainer.cs +++ b/DataInterfaceConsole/Actions/EphemeralSettings/EphemeralSettingsContainer.cs @@ -5,8 +5,8 @@ using System.Collections.Generic; using System.Linq; -namespace DataInterfaceConsole.Actions.EphermalSettings; -internal class EphermalSettingsContainer : ISettingsContainer { +namespace DataInterfaceConsole.Actions.EphemeralSettings; +internal class EphemeralSettingsContainer : ISettingsContainer { public ISettingsValue[] GetSettings() => this.OptionsStore.Values.ToArray(); private readonly Dictionary OptionsStore = new Dictionary(); // string : OptionsValue @@ -16,7 +16,7 @@ private void AddOption(ISettingsValue s) { private DataInterface Di => Program.instance.di; - public EphermalSettingsContainer() { + public EphemeralSettingsContainer() { AddOption(new SettingsValueWhitelisted("UndoSubmittedMoves", "Allow Undoing Submitted Moves", "Allows Undoing submitted moves in Singleplayer Games (Local/CPU)", ["on", "off"], "off", @this => Di.MemLocUndoMoveReducedByValue.SetValue((byte)(@this.GetValueAsString() == "off" ? 0xFF : 0x00)))); AddOption(new SettingsValuePrimitive("PasteLobbyCode", "Set private match code", "Sets the ingame private match code used for joining someone else's game", "", diff --git a/DataInterfaceConsole/Actions/EphemeralSettings/ManageEphemeralSettings.cs b/DataInterfaceConsole/Actions/EphemeralSettings/ManageEphemeralSettings.cs new file mode 100644 index 0000000..063e329 --- /dev/null +++ b/DataInterfaceConsole/Actions/EphemeralSettings/ManageEphemeralSettings.cs @@ -0,0 +1,9 @@ +using DataInterfaceConsole.Actions.Settings; + +namespace DataInterfaceConsole.Actions.EphemeralSettings; + +internal class ManageEphemeralSettings : BaseManageSettingsMenu { + public override string Name => "Ephemeral Settings and Triggers"; + + public override ISettingsContainer SettingsHandler => Program.instance.eh; +} diff --git a/DataInterfaceConsole/Actions/EphermalSettings/Trigger.cs b/DataInterfaceConsole/Actions/EphemeralSettings/Trigger.cs similarity index 82% rename from DataInterfaceConsole/Actions/EphermalSettings/Trigger.cs rename to DataInterfaceConsole/Actions/EphemeralSettings/Trigger.cs index 5513376..a0ed283 100644 --- a/DataInterfaceConsole/Actions/EphermalSettings/Trigger.cs +++ b/DataInterfaceConsole/Actions/EphemeralSettings/Trigger.cs @@ -1,6 +1,6 @@ using System; -namespace DataInterfaceConsole.Actions.EphermalSettings; +namespace DataInterfaceConsole.Actions.EphemeralSettings; public class Trigger { private readonly Action action; diff --git a/DataInterfaceConsole/Actions/EphermalSettings/ManageEphermalSettings.cs b/DataInterfaceConsole/Actions/EphermalSettings/ManageEphermalSettings.cs deleted file mode 100644 index 644c8ed..0000000 --- a/DataInterfaceConsole/Actions/EphermalSettings/ManageEphermalSettings.cs +++ /dev/null @@ -1,9 +0,0 @@ -using DataInterfaceConsole.Actions.Settings; - -namespace DataInterfaceConsole.Actions.EphermalSettings; - -internal class ManageEphermalSettings : BaseManageSettingsMenu { - public override string Name => "Ephermal Settings and Triggers"; - - public override ISettingsContainer SettingsHandler => Program.instance.eh; -} diff --git a/DataInterfaceConsole/Program.cs b/DataInterfaceConsole/Program.cs index 592e83c..15cb1bc 100644 --- a/DataInterfaceConsole/Program.cs +++ b/DataInterfaceConsole/Program.cs @@ -1,5 +1,5 @@ using DataInterfaceConsole.Actions; -using DataInterfaceConsole.Actions.EphermalSettings; +using DataInterfaceConsole.Actions.EphemeralSettings; using DataInterfaceConsole.Actions.Settings; using DataInterfaceConsole.Types; using DataInterfaceConsole.Types.Exceptions; @@ -16,7 +16,7 @@ internal class Program { private Thread backgroundThread; public DataInterface di; public PersistentSettingsContainer sh; - public EphermalSettingsContainer eh; + public EphemeralSettingsContainer eh; private static void Main() { ConsoleNonBlocking.Init(); @@ -27,7 +27,7 @@ private static void Main() { private void Run() { Console.WriteLine("Some output will occasionally be provided via the console title."); this.sh = PersistentSettingsContainer.LoadOrCreateNew(); - this.eh = new EphermalSettingsContainer(); + this.eh = new EphemeralSettingsContainer(); this.backgroundThread = new Thread(BackgroundThreadRun) { Name = "BackgroundThread"