diff --git a/rabi_splitter_WPF/RabiRibiDisplay.cs b/rabi_splitter_WPF/RabiRibiDisplay.cs
index 2d32e9f..8f482dc 100644
--- a/rabi_splitter_WPF/RabiRibiDisplay.cs
+++ b/rabi_splitter_WPF/RabiRibiDisplay.cs
@@ -61,14 +61,18 @@ namespace rabi_splitter_WPF
private void UpdateVariableExport()
{
- variableExportContext.CheckForUpdates();
long currentFrameMillisecond = (long)(DateTime.Now - UNIX_START).TotalMilliseconds;
var diff = currentFrameMillisecond - lastUpdateMillisecond;
if (diff >= 1000)
{
if (diff >= 2000) lastUpdateMillisecond = currentFrameMillisecond;
else lastUpdateMillisecond += 1000;
- variableExportContext.OutputUpdates();
+ variableExportContext.UpdateVariables(true);
+ }
+ else
+ {
+ // Don't update files.
+ variableExportContext.UpdateVariables(false);
}
}
diff --git a/rabi_splitter_WPF/StringInjectExtension.cs b/rabi_splitter_WPF/StringInjectExtension.cs
new file mode 100644
index 0000000..dcbdae0
--- /dev/null
+++ b/rabi_splitter_WPF/StringInjectExtension.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Text.RegularExpressions;
+using System.Collections;
+using System.Globalization;
+using System.ComponentModel;
+
+/// Source: http://mo.notono.us/2008/07/c-stringinject-format-strings-by-key.html
+
+[assembly: CLSCompliant(true)]
+namespace StringInject
+{
+ public static class StringInjectExtension
+ {
+ ///
+ /// Extension method that replaces keys in a string with the values of matching object properties.
+ /// Uses internally; custom formats should match those used for that method.
+ ///
+ /// The format string, containing keys like {foo} and {foo:SomeFormat}.
+ /// The object whose properties should be injected in the string
+ /// A version of the formatString string with keys replaced by (formatted) key values.
+ public static string Inject(this string formatString, object injectionObject)
+ {
+ return formatString.Inject(GetPropertyHash(injectionObject));
+ }
+
+ ///
+ /// Extension method that replaces keys in a string with the values of matching dictionary entries.
+ /// Uses internally; custom formats should match those used for that method.
+ ///
+ /// The format string, containing keys like {foo} and {foo:SomeFormat}.
+ /// An with keys and values to inject into the string
+ /// A version of the formatString string with dictionary keys replaced by (formatted) key values.
+ public static string Inject(this string formatString, IDictionary dictionary)
+ {
+ return formatString.Inject(new Hashtable(dictionary));
+ }
+
+ ///
+ /// Extension method that replaces keys in a string with the values of matching hashtable entries.
+ /// Uses internally; custom formats should match those used for that method.
+ ///
+ /// The format string, containing keys like {foo} and {foo:SomeFormat}.
+ /// A with keys and values to inject into the string
+ /// A version of the formatString string with hastable keys replaced by (formatted) key values.
+ public static string Inject(this string formatString, Hashtable attributes)
+ {
+ string result = formatString;
+ if (attributes == null || formatString == null)
+ return result;
+
+ foreach (string attributeKey in attributes.Keys)
+ {
+ result = result.InjectSingleValue(attributeKey, attributes[attributeKey]);
+ }
+ return result;
+ }
+
+ ///
+ /// Replaces all instances of a 'key' (e.g. {foo} or {foo:SomeFormat}) in a string with an optionally formatted value, and returns the result.
+ ///
+ /// The string containing the key; unformatted ({foo}), or formatted ({foo:SomeFormat})
+ /// The key name (foo)
+ /// The replacement value; if null is replaced with an empty string
+ /// The input string with any instances of the key replaced with the replacement value
+ public static string InjectSingleValue(this string formatString, string key, object replacementValue)
+ {
+ string result = formatString;
+ //regex replacement of key with value, where the generic key format is:
+ //Regex foo = new Regex("{(foo)(?:}|(?::(.[^}]*)}))");
+ Regex attributeRegex = new Regex("{(" + key + ")(?:}|(?::(.[^}]*)}))"); //for key = foo, matches {foo} and {foo:SomeFormat}
+
+ //loop through matches, since each key may be used more than once (and with a different format string)
+ foreach (Match m in attributeRegex.Matches(formatString))
+ {
+ string replacement = m.ToString();
+ if (m.Groups[2].Length > 0) //matched {foo:SomeFormat}
+ {
+ //do a double string.Format - first to build the proper format string, and then to format the replacement value
+ string attributeFormatString = string.Format(CultureInfo.InvariantCulture, "{{0:{0}}}", m.Groups[2]);
+ replacement = string.Format(CultureInfo.CurrentCulture, attributeFormatString, replacementValue);
+ }
+ else //matched {foo}
+ {
+ replacement = (replacementValue ?? string.Empty).ToString();
+ }
+ //perform replacements, one match at a time
+ result = result.Replace(m.ToString(), replacement); //attributeRegex.Replace(result, replacement, 1);
+ }
+ return result;
+
+ }
+
+
+ ///
+ /// Creates a HashTable based on current object state.
+ /// Copied from the MVCToolkit HtmlExtensionUtility class
+ ///
+ /// The object from which to get the properties
+ /// A containing the object instance's property names and their values
+ private static Hashtable GetPropertyHash(object properties)
+ {
+ Hashtable values = null;
+ if (properties != null)
+ {
+ values = new Hashtable();
+ PropertyDescriptorCollection props = TypeDescriptor.GetProperties(properties);
+ foreach (PropertyDescriptor prop in props)
+ {
+ values.Add(prop.Name, prop.GetValue(properties));
+ }
+ }
+ return values;
+ }
+
+ }
+}
diff --git a/rabi_splitter_WPF/VariableExportConfig.cs b/rabi_splitter_WPF/VariableExportConfig.cs
index 39e1d11..cfcf58f 100644
--- a/rabi_splitter_WPF/VariableExportConfig.cs
+++ b/rabi_splitter_WPF/VariableExportConfig.cs
@@ -9,95 +9,108 @@ namespace rabi_splitter_WPF
{
void ConfigureVariableExports()
{
- ExportableVariable.DefineVariableExports(new ExportableVariable[] {
+ variableExportContext.DefineVariableExports(new ExportableVariable[] {
ExportVariable (
+ handle: "playtime",
displayName: "Playtime",
tracker: () => snapshot.playtime
),
ExportVariable (
+ handle: "blackness",
displayName: "Blackness",
tracker: () => snapshot.blackness
),
ExportVariable (
+ handle: "mapid",
displayName: "Map Id",
tracker: () => snapshot.mapid
),
ExportVariable (
+ handle: "map",
displayName: "Map",
tracker: () => StaticData.GetMapName(snapshot.mapid)
),
ExportVariable (
+ handle: "musicid",
displayName: "Music Id",
tracker: () => snapshot.musicid
),
ExportVariable (
+ handle: "music",
displayName: "Music",
tracker: () => StaticData.GetMusicName(snapshot.musicid)
),
ExportVariable (
+ handle: "hp",
displayName: "HP",
tracker: () => snapshot.hp
),
ExportVariable (
+ handle: "amulet",
displayName: "Amulet",
tracker: () => snapshot.amulet
),
ExportVariable (
+ handle: "boost",
displayName: "Boost",
tracker: () => snapshot.boost
),
ExportVariable (
+ handle: "mana",
displayName: "MP",
tracker: () => snapshot.mana
),
ExportVariable (
+ handle: "stamina",
displayName: "SP",
tracker: () => snapshot.stamina
),
ExportVariable (
+ handle: "x",
displayName: "x",
tracker: () => snapshot.px
),
ExportVariable (
+ handle: "y",
displayName: "y",
tracker: () => snapshot.py
),
ExportVariable (
+ handle: "mapTile",
displayName: "Map Tile",
tracker: () => snapshot.mapTile
),
ExportVariable (
+ handle: "nDeaths",
displayName: "Deaths",
tracker: () => inGameState.nDeaths
),
ExportVariable (
+ handle: "nRestarts",
displayName: "Restarts",
tracker: () => inGameState.nRestarts
),
});
-
-
- variableExportContext.NotifyExportableVariableUpdate();
}
- private ExportableVariable ExportVariable(string displayName, Func tracker)
+ private ExportableVariable ExportVariable(string handle, string displayName, Func tracker)
{
- return new ExportableVariable(displayName, tracker);
+ return new ExportableVariable(handle, displayName, tracker);
}
}
}
diff --git a/rabi_splitter_WPF/VariableExportContext.cs b/rabi_splitter_WPF/VariableExportContext.cs
index ea37216..24efff7 100644
--- a/rabi_splitter_WPF/VariableExportContext.cs
+++ b/rabi_splitter_WPF/VariableExportContext.cs
@@ -5,55 +5,65 @@ using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
+using System.Windows.Controls;
namespace rabi_splitter_WPF
{
public class VariableExportContext : INotifyPropertyChanged
{
private List _variableExportSettings;
- private HashSet pendingUpdates;
+ private List _variables;
+ private Dictionary variableValues;
+
+ private ItemCollection variableListBoxItems;
+ private ItemCollection variableExportListBoxItems;
public VariableExportContext()
{
_variableExportSettings = new List();
- pendingUpdates = new HashSet();
+ _variables = new List();
+ variableValues = new Dictionary();
}
#region Update Logic
- public void OutputUpdates()
+ public void UpdateVariables(bool updateFile)
{
- foreach (var ves in pendingUpdates)
+ foreach (var variable in _variables) {
+ variable.UpdateValue();
+ variableValues[variable.Handle] = variable.Value;
+ }
+
+ foreach (var ves in _variableExportSettings)
{
- ves.OutputUpdate();
+ ves.OutputUpdate(variableValues, updateFile);
}
- pendingUpdates.Clear();
}
- private void RegisterUpdate(VariableExportSetting ves)
+ internal void SetItemControls(ItemCollection variableListBoxItems, ItemCollection variableExportListBoxItems)
{
- pendingUpdates.Add(ves);
+ this.variableListBoxItems = variableListBoxItems;
+ this.variableExportListBoxItems = variableExportListBoxItems;
}
- public void CheckForUpdates()
+ public void DefineVariableExports(ExportableVariable[] exports)
{
- foreach (var ves in _variableExportSettings)
- {
- bool hasUpdate = ves.CheckForUpdate();
- if (hasUpdate) RegisterUpdate(ves);
- }
+ Variables = exports.ToList();
+ variableValues.Clear();
}
+ #endregion
- public void NotifyExportableVariableUpdate()
+ #region Properties
+ public List Variables
{
- foreach (var ves in _variableExportSettings)
+ get { return _variables; }
+ private set
{
- ves.NotifyExportableVariableUpdate();
+ _variables = value;
+ variableListBoxItems.Refresh();
}
}
- #endregion
- #region Variables
public List VariableExportSettings
{
get { return _variableExportSettings; }
@@ -62,12 +72,13 @@ namespace rabi_splitter_WPF
internal void Add(VariableExportSetting ves)
{
_variableExportSettings.Add(ves);
+ variableExportListBoxItems.Refresh();
}
internal void Delete(VariableExportSetting ves)
{
_variableExportSettings.Remove(ves);
- pendingUpdates.Remove(ves);
+ variableExportListBoxItems.Refresh();
}
#endregion
diff --git a/rabi_splitter_WPF/VariableExportSetting.cs b/rabi_splitter_WPF/VariableExportSetting.cs
index 2002dfa..d4d95c2 100644
--- a/rabi_splitter_WPF/VariableExportSetting.cs
+++ b/rabi_splitter_WPF/VariableExportSetting.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
+using StringInject;
namespace rabi_splitter_WPF
{
@@ -11,29 +12,28 @@ namespace rabi_splitter_WPF
{
private readonly Func tracker;
- public ExportableVariable(string displayName, Func tracker) : base(displayName)
+ public ExportableVariable(string handle, string displayName, Func tracker) : base(handle, displayName)
{
this.tracker = tracker;
}
- internal override VariableTracker GetTracker()
+ public override void UpdateValue()
{
- return new VariableTracker(tracker);
+ Value = tracker();
}
}
- public abstract class ExportableVariable
+ public abstract class ExportableVariable : INotifyPropertyChanged
{
- private static int nextAvailableId = 0;
- private static List _variableExports;
- private static Dictionary _variableCaptions = new Dictionary();
-
private readonly int _id;
private readonly string _displayName;
+ private readonly string _handle;
+
+ private object _value;
- protected ExportableVariable(string displayName)
+ protected ExportableVariable(string handle, string displayName)
{
- _id = nextAvailableId++;
+ _handle = handle;
_displayName = displayName;
}
@@ -47,81 +47,36 @@ namespace rabi_splitter_WPF
get { return _displayName; }
}
- internal abstract VariableTracker GetTracker();
-
- public static void DefineVariableExports(ExportableVariable[] exports)
- {
- _variableExports = exports.ToList();
- _variableCaptions = exports.ToDictionary(ev => ev, ev => ev.DisplayName);
- }
-
- public static Dictionary VariableCaptions
- {
- get { return _variableCaptions; }
- }
-
- #region Equals, GetHashCode
- public override bool Equals(object obj)
- {
- var otherValue = obj as ExportableVariable;
- if (otherValue == null) return false;
- return _id.Equals(otherValue.Id);
- }
-
- public override int GetHashCode()
- {
- return _id.GetHashCode();
- }
- #endregion
- }
-
- public class VariableTracker : VariableTracker
- {
- private readonly Func tracker;
- private T currentValue;
-
- public VariableTracker(Func tracker)
+ public string Handle
{
- this.tracker = tracker;
- forceUpdate = true;
+ get { return _handle; }
}
- public override bool CheckForUpdate()
+ public object Value
{
- T newValue = tracker();
-
- if (forceUpdate || !newValue.Equals(currentValue))
+ get { return _value; }
+ protected set
{
- currentValue = newValue;
- forceUpdate = false;
- return true;
+ if (value.Equals(_value)) return;
+ _value = value;
+ OnPropertyChanged(nameof(Value));
}
- return false;
}
+
+ public abstract void UpdateValue();
- public override object GetValue()
- {
- return currentValue;
- }
- }
-
- public abstract class VariableTracker
- {
- protected bool forceUpdate;
+ public event PropertyChangedEventHandler PropertyChanged;
- public void FormatChanged()
+ [NotifyPropertyChangedInvocator]
+ protected virtual void OnPropertyChanged(string propertyName)
{
- forceUpdate = true;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
-
- public abstract bool CheckForUpdate();
- public abstract object GetValue();
}
public class VariableExportSetting : INotifyPropertyChanged
{
- private ExportableVariable _selectedVariable;
- private VariableTracker _variableTracker;
+
private string _outputFileName;
private string _outputFormat;
private string _formatPreview;
@@ -131,7 +86,6 @@ namespace rabi_splitter_WPF
public VariableExportSetting()
{
// Default values
- _selectedVariable = null;
_outputFileName = "";
_outputFormat = "";
_isExporting = false;
@@ -139,11 +93,11 @@ namespace rabi_splitter_WPF
}
#region Logic
- private string FormatOutput()
+ private string FormatOutput(Dictionary variableValues)
{
try
{
- return string.Format(_outputFormat, _variableTracker.GetValue());
+ return _outputFormat.Inject(variableValues);
}
catch (FormatException e)
{
@@ -151,60 +105,21 @@ namespace rabi_splitter_WPF
}
}
- internal void OutputUpdate()
+ internal void OutputUpdate(Dictionary variableValues, bool updateFile)
{
- if (_variableTracker == null) return;
- var formattedOutput = FormatOutput();
+ var formattedOutput = FormatOutput(variableValues);
+ if (formattedOutput == FormatPreview) return;
FormatPreview = formattedOutput;
- // TODO: Write to file
- }
-
- internal bool CheckForUpdate()
- {
- if (_variableTracker == null) return false;
- return _variableTracker.CheckForUpdate();
- }
-
- public void NotifyExportableVariableUpdate()
- {
- OnPropertyChanged(nameof(VariableCaptions));
- }
- #endregion
-
- #region Dictionaries
- public Dictionary VariableCaptions
- {
- get { return ExportableVariable.VariableCaptions; }
- }
-
- internal void DefaultButton_Click()
- {
- if (_selectedVariable == null)
- {
- OutputFormat = "Variable not set.";
- }
- else
+ if (updateFile)
{
- OutputFormat = $"{_selectedVariable.DisplayName}: {{0}}";
+ // TODO: Write to file
+
}
}
-
#endregion
#region Parameters
- public ExportableVariable SelectedVariable
- {
- get { return _selectedVariable; }
- set
- {
- if (value.Equals(_selectedVariable)) return;
- _selectedVariable = value;
- _variableTracker = _selectedVariable.GetTracker();
- OnPropertyChanged(nameof(SelectedVariable));
- }
- }
-
public string OutputFileName
{
get { return _outputFileName; }
@@ -223,7 +138,6 @@ namespace rabi_splitter_WPF
{
if (value.Equals(_outputFormat)) return;
_outputFormat = value;
- if (_variableTracker != null) _variableTracker.FormatChanged();
OnPropertyChanged(nameof(OutputFormat));
}
}
@@ -261,9 +175,7 @@ namespace rabi_splitter_WPF
}
}
#endregion
-
- // Note: DO NOT OVERRIDE Equals and GetHashCode. We compare by reference equality.
-
+
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
diff --git a/rabi_splitter_WPF/VariableExportTab.xaml b/rabi_splitter_WPF/VariableExportTab.xaml
index da3785a..d7fc2a4 100644
--- a/rabi_splitter_WPF/VariableExportTab.xaml
+++ b/rabi_splitter_WPF/VariableExportTab.xaml
@@ -5,48 +5,70 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:rabi_splitter_WPF"
mc:Ignorable="d"
- d:DesignHeight="500" d:DesignWidth="540">
+ d:DesignHeight="500" d:DesignWidth="780">
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
diff --git a/rabi_splitter_WPF/VariableExportTab.xaml.cs b/rabi_splitter_WPF/VariableExportTab.xaml.cs
index 2548281..050e507 100644
--- a/rabi_splitter_WPF/VariableExportTab.xaml.cs
+++ b/rabi_splitter_WPF/VariableExportTab.xaml.cs
@@ -33,6 +33,7 @@ namespace rabi_splitter_WPF
{
this.debugContext = debugContext;
this.variableExportContext = variableExportContext;
+ variableExportContext.SetItemControls(VariableListBox.Items, VariableExportListBox.Items);
}
private void AddButton_Click(object sender, RoutedEventArgs e)
@@ -40,20 +41,12 @@ namespace rabi_splitter_WPF
var ves = new VariableExportSetting();
ves.OutputFileName = "Hello.txt";
variableExportContext.Add(ves);
- VariableExportListBox.Items.Refresh();
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
var variableExportSetting = (sender as Button).DataContext as VariableExportSetting;
variableExportContext.Delete(variableExportSetting);
- VariableExportListBox.Items.Refresh();
- }
-
- private void DefaultButton_Click(object sender, RoutedEventArgs e)
- {
- var variableExportSetting = (sender as Button).DataContext as VariableExportSetting;
- variableExportSetting.DefaultButton_Click();
}
}
}
diff --git a/rabi_splitter_WPF/rabi_display.csproj b/rabi_splitter_WPF/rabi_display.csproj
index 9aa6d1b..29bca2c 100644
--- a/rabi_splitter_WPF/rabi_display.csproj
+++ b/rabi_splitter_WPF/rabi_display.csproj
@@ -66,6 +66,7 @@
+