diff --git a/rabi_splitter_WPF/MainContext.cs b/rabi_splitter_WPF/MainContext.cs
index 366471f..5e89d7d 100644
--- a/rabi_splitter_WPF/MainContext.cs
+++ b/rabi_splitter_WPF/MainContext.cs
@@ -6,9 +6,38 @@ using System.Linq;
using System.Text;
using System.Windows.Data;
using rabi_splitter_WPF.Annotations;
+using System.Windows;
namespace rabi_splitter_WPF
{
+ [ValueConversion(typeof(bool), typeof(Visibility))]
+ public class InvertableBooleanToVisibilityConverter : IValueConverter
+ {
+ #region IValueConverter Members
+ // Code taken from http://stackoverflow.com/a/2427307
+ enum Parameter
+ {
+ VisibleWhenTrue, VisibleWhenFalse
+ }
+
+ public object Convert(object value, Type targetType, object parameter,
+ System.Globalization.CultureInfo culture)
+ {
+ var boolValue = (bool)value;
+ var direction = (Parameter)Enum.Parse(typeof(Parameter), (string)parameter);
+
+ if (direction == Parameter.VisibleWhenTrue) return boolValue ? Visibility.Visible : Visibility.Collapsed;
+ else return boolValue ? Visibility.Collapsed : Visibility.Visible;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter,
+ System.Globalization.CultureInfo culture)
+ {
+ return null;
+ }
+ #endregion
+ }
+
[ValueConversion(typeof(bool), typeof(bool))]
public class InverseBooleanConverter : IValueConverter
{
@@ -128,7 +157,7 @@ namespace rabi_splitter_WPF
}
}
- class DebugContext : INotifyPropertyChanged
+ public class DebugContext : INotifyPropertyChanged
{
private int _entityAnalysisIndex;
private bool _bossEvent;
diff --git a/rabi_splitter_WPF/MainWindow.xaml b/rabi_splitter_WPF/MainWindow.xaml
index f1145bd..43f3076 100644
--- a/rabi_splitter_WPF/MainWindow.xaml
+++ b/rabi_splitter_WPF/MainWindow.xaml
@@ -10,107 +10,110 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
diff --git a/rabi_splitter_WPF/MainWindow.xaml.cs b/rabi_splitter_WPF/MainWindow.xaml.cs
index 19bf464..68f2ea5 100644
--- a/rabi_splitter_WPF/MainWindow.xaml.cs
+++ b/rabi_splitter_WPF/MainWindow.xaml.cs
@@ -27,6 +27,7 @@ namespace rabi_splitter_WPF
private MainContext mainContext;
private RabiRibiDisplay rabiRibiDisplay;
private DebugContext debugContext;
+ private VariableExportContext variableExportContext;
private static TcpClient tcpclient;
private static NetworkStream networkStream;
private readonly Regex titleReg = new Regex(@"ver.*?(\d+\.?\d+.*)$");
@@ -110,13 +111,16 @@ namespace rabi_splitter_WPF
InitializeComponent();
mainContext=new MainContext();
debugContext=new DebugContext();
+ variableExportContext = new VariableExportContext();
this.DataContext = mainContext;
DebugPanel.DataContext = debugContext;
this.Grid.ItemsSource = debugContext.BossList;
EntityDataPanel.DataContext = debugContext;
this.EntityStats.ItemsSource = debugContext.EntityStatsListView;
+ this.VariableExportTab.DataContext = variableExportContext;
+ this.VariableExportTab.Initialise(debugContext, variableExportContext);
BossEventDebug.DataContext = debugContext;
- rabiRibiDisplay = new RabiRibiDisplay(mainContext, debugContext, this);
+ rabiRibiDisplay = new RabiRibiDisplay(mainContext, debugContext, variableExportContext, this);
memoryThread = new Thread(() =>
{
while (true)
diff --git a/rabi_splitter_WPF/MemorySnapshot.cs b/rabi_splitter_WPF/MemorySnapshot.cs
index 0c827d4..4cdc61f 100644
--- a/rabi_splitter_WPF/MemorySnapshot.cs
+++ b/rabi_splitter_WPF/MemorySnapshot.cs
@@ -27,6 +27,10 @@ namespace rabi_splitter_WPF
return new MapTileCoordinate(x, y);
}
+ public override string ToString() {
+ return $"({x}, {y})";
+ }
+
#region Equals, Hashcode
// override object.Equals
public override bool Equals(object obj)
@@ -86,6 +90,10 @@ namespace rabi_splitter_WPF
public readonly int ribbonXp;
public readonly float itemPercent;
+ public readonly Tuple nextHammer;
+ public readonly Tuple nextRibbon;
+ public readonly Tuple nextCarrot;
+
public readonly int nAttackUps;
public readonly int nHpUps;
public readonly int nManaUps;
@@ -108,6 +116,9 @@ namespace rabi_splitter_WPF
hammerXp = memoryHelper.GetMemoryValue(0xD654B4);
ribbonXp = memoryHelper.GetMemoryValue(0xD654B8);
itemPercent = memoryHelper.GetMemoryValue(0xA730E8);
+ nextHammer = StaticData.GetNextHammerLevel(hammerXp);
+ nextRibbon = StaticData.GetNextRibbonLevel(ribbonXp);
+ nextCarrot = StaticData.GetNextCarrotLevel(carrotXp);
minimapPosition = memoryHelper.GetMemoryValue(0xA72E08);
diff --git a/rabi_splitter_WPF/RabiRibiDisplay.cs b/rabi_splitter_WPF/RabiRibiDisplay.cs
index 17d5b5f..b305d92 100644
--- a/rabi_splitter_WPF/RabiRibiDisplay.cs
+++ b/rabi_splitter_WPF/RabiRibiDisplay.cs
@@ -6,10 +6,11 @@ using System.Text;
namespace rabi_splitter_WPF
{
- class RabiRibiDisplay
+ partial class RabiRibiDisplay
{
private MainContext mainContext;
private DebugContext debugContext;
+ private VariableExportContext variableExportContext;
private MainWindow mainWindow;
private RabiRibiState rabiRibiState;
@@ -20,19 +21,22 @@ namespace rabi_splitter_WPF
// Variables used for tracking frequency of memory reads.
private static readonly DateTime UNIX_START = new DateTime(1970, 1, 1);
private double readFps = -1;
- long previousFrameMillisecond = -1;
+ private long previousFrameMillisecond = -1;
+ private long lastUpdateMillisecond = -1;
// internal frame counter.
private int memoryReadCount;
- public RabiRibiDisplay(MainContext mainContext, DebugContext debugContext, MainWindow mainWindow)
+ public RabiRibiDisplay(MainContext mainContext, DebugContext debugContext, VariableExportContext variableExportContext, MainWindow mainWindow)
{
this.rabiRibiState = new RabiRibiState();
this.mainContext = mainContext;
this.debugContext = debugContext;
+ this.variableExportContext = variableExportContext;
this.mainWindow = mainWindow;
this.memoryReadCount = 0;
StartNewGame();
+ ConfigureVariableExports();
}
public void ReadMemory(Process process)
@@ -47,12 +51,30 @@ namespace rabi_splitter_WPF
UpdateDebugArea(memoryHelper);
UpdateEntityData(memoryHelper);
UpdateFps();
+ UpdateVariableExport();
if (snapshot.musicid >= 0) rabiRibiState.lastValidMusicId = snapshot.musicid;
prevSnapshot = snapshot;
memoryHelper.Dispose();
}
+
+ private void UpdateVariableExport()
+ {
+ long currentFrameMillisecond = (long)(DateTime.Now - UNIX_START).TotalMilliseconds;
+ var diff = currentFrameMillisecond - lastUpdateMillisecond;
+ if (diff >= 1000)
+ {
+ if (diff >= 2000) lastUpdateMillisecond = currentFrameMillisecond;
+ else lastUpdateMillisecond += 1000;
+ variableExportContext.UpdateVariables(true);
+ }
+ else
+ {
+ // Don't update files.
+ variableExportContext.UpdateVariables(false);
+ }
+ }
private void UpdateFps()
{
@@ -95,7 +117,6 @@ namespace rabi_splitter_WPF
if (InGame())
{
inGameState.nRestarts++;
- UpdateTextFile();
}
DebugLog("Reload Game! " + snapshot.playtime + " <- " + inGameState.lastNonZeroPlayTime);
}
@@ -175,7 +196,6 @@ namespace rabi_splitter_WPF
if (InGame())
{
inGameState.nDeaths++;
- UpdateTextFile();
}
DebugLog("Death!");
}
@@ -274,16 +294,7 @@ namespace rabi_splitter_WPF
mainContext.Text20 = bosstext;
}
}
-
- private void UpdateTextFile()
- {
- //return;
- string text = $"Deaths: {inGameState.nDeaths}\nResets: {inGameState.nRestarts}";
- System.IO.StreamWriter file = new System.IO.StreamWriter("deaths_restarts.txt");
- file.WriteLine(text);
- file.Close();
- }
-
+
private void StartNewGame()
{
inGameState = new InGameState();
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
new file mode 100644
index 0000000..fc1456f
--- /dev/null
+++ b/rabi_splitter_WPF/VariableExportConfig.cs
@@ -0,0 +1,228 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace rabi_splitter_WPF
+{
+ partial class RabiRibiDisplay
+ {
+ void ConfigureVariableExports()
+ {
+ variableExportContext.DefineVariableExports(new ExportableVariable[] {
+ ExportVariable (
+ handle: "deaths",
+ displayName: "Deaths",
+ tracker: () => inGameState == null ? 0 : inGameState.nDeaths
+ ),
+
+ ExportVariable (
+ handle: "restarts",
+ displayName: "Restarts",
+ tracker: () => inGameState == null ? 0 : inGameState.nRestarts
+ ),
+
+ ExportVariable (
+ handle: "currentBoss",
+ displayName: "Current Boss Fight",
+ tracker: () => (inGameState == null || !inGameState.CurrentActivityIs(InGameActivity.BOSS_BATTLE)) ?
+ "None" :
+ inGameState.currentBossFight.displayName
+ ),
+
+ ExportVariable (
+ handle: "currentBossTime",
+ displayName: "Current Boss Time",
+ tracker: () => (inGameState == null || !inGameState.CurrentActivityIs(InGameActivity.BOSS_BATTLE)) ?
+ TimeSpan.Zero :
+ (DateTime.Now - inGameState.currentBossStartTime)
+ ),
+
+ ExportVariable (
+ handle: "lastBoss",
+ displayName: "Last Boss Fight",
+ tracker: () => inGameState?.lastBossFight == null ? "None" : inGameState.lastBossFight.displayName
+ ),
+
+ ExportVariable (
+ handle: "lastBossTime",
+ displayName: "Last Boss Time",
+ tracker: () => inGameState?.lastBossFight == null ? TimeSpan.Zero : inGameState.lastBossFightDuration
+ ),
+
+ ExportVariable (
+ handle: "musicid",
+ displayName: "Music Id",
+ tracker: () => snapshot == null ? 0 : snapshot.musicid
+ ),
+
+ ExportVariable (
+ handle: "music",
+ displayName: "Music",
+ tracker: () => snapshot == null ? "" : StaticData.GetMusicName(snapshot.musicid)
+ ),
+
+ ExportVariable (
+ handle: "mapid",
+ displayName: "Map Id",
+ tracker: () => snapshot == null ? 0 : snapshot.mapid
+ ),
+
+ ExportVariable (
+ handle: "map",
+ displayName: "Map",
+ tracker: () => snapshot == null ? "" : StaticData.GetMapName(snapshot.mapid)
+ ),
+
+ ExportVariable (
+ handle: "mapTile",
+ displayName: "Map Tile",
+ tracker: () => snapshot == null ? "" : snapshot.mapTile.ToString()
+ ),
+
+ ExportVariable (
+ handle: "hp",
+ displayName: "HP",
+ tracker: () => snapshot == null ? 0 : snapshot.hp
+ ),
+
+ ExportVariable (
+ handle: "maxhp",
+ displayName: "Max HP",
+ tracker: () => snapshot == null ? 0 : snapshot.maxhp
+ ),
+
+ ExportVariable (
+ handle: "itempercent",
+ displayName: "Item %",
+ tracker: () => snapshot == null ? 0 : snapshot.itemPercent
+ ),
+
+ ExportVariable (
+ handle: "hammerXp",
+ displayName: "Hammer Exp",
+ tracker: () => snapshot == null ? 0 : snapshot.hammerXp
+ ),
+
+ ExportVariable (
+ handle: "nextHammerExp",
+ displayName: "Next Hammer Level Exp",
+ tracker: () => snapshot == null ? 0 : snapshot.nextHammer.Item1
+ ),
+
+ ExportVariable (
+ handle: "nextHammerName",
+ displayName: "Next Hammer Level Name (Short)",
+ tracker: () => snapshot == null ? "" : snapshot.nextHammer.Item2
+ ),
+
+ ExportVariable (
+ handle: "nextHammerNameLong",
+ displayName: "Next Hammer Level Name (Long)",
+ tracker: () => snapshot == null ? "" : snapshot.nextHammer.Item3
+ ),
+
+ ExportVariable (
+ handle: "ribbonXp",
+ displayName: "Ribbon Exp",
+ tracker: () => snapshot == null ? 0 : snapshot.ribbonXp
+ ),
+
+ ExportVariable (
+ handle: "nextRibbonExp",
+ displayName: "Next Ribbon Level Exp",
+ tracker: () => snapshot == null ? 0 : snapshot.nextRibbon.Item1
+ ),
+
+ ExportVariable (
+ handle: "nextRibbonName",
+ displayName: "Next Ribbon Level Name (Short)",
+ tracker: () => snapshot == null ? "" : snapshot.nextRibbon.Item2
+ ),
+
+ ExportVariable (
+ handle: "nextRibbonNameLong",
+ displayName: "Next Ribbon Level Name (Long)",
+ tracker: () => snapshot == null ? "" : snapshot.nextRibbon.Item3
+ ),
+
+ ExportVariable (
+ handle: "carrotXp",
+ displayName: "Carrot Exp",
+ tracker: () => snapshot == null ? 0 : snapshot.carrotXp
+ ),
+
+ ExportVariable (
+ handle: "nextCarrotExp",
+ displayName: "Next Carrot Level Exp",
+ tracker: () => snapshot == null ? 0 : snapshot.nextCarrot.Item1
+ ),
+
+ ExportVariable (
+ handle: "nextCarrotName",
+ displayName: "Next Carrot Level Name (Short)",
+ tracker: () => snapshot == null ? "" : snapshot.nextCarrot.Item2
+ ),
+
+ ExportVariable (
+ handle: "nextCarrotNameLong",
+ displayName: "Next Carrot Level Name (Long)",
+ tracker: () => snapshot == null ? "" : snapshot.nextCarrot.Item3
+ ),
+
+ ExportVariable (
+ handle: "amulet",
+ displayName: "Amulet",
+ tracker: () => snapshot == null ? 0 : snapshot.amulet
+ ),
+
+ ExportVariable (
+ handle: "boost",
+ displayName: "Boost",
+ tracker: () => snapshot == null ? 0 : snapshot.boost
+ ),
+
+ ExportVariable (
+ handle: "mana",
+ displayName: "MP",
+ tracker: () => snapshot == null ? 0 : snapshot.mana
+ ),
+
+ ExportVariable (
+ handle: "stamina",
+ displayName: "SP",
+ tracker: () => snapshot == null ? 0 : snapshot.stamina
+ ),
+
+ ExportVariable (
+ handle: "x",
+ displayName: "x",
+ tracker: () => snapshot == null ? 0 : snapshot.px
+ ),
+
+ ExportVariable (
+ handle: "y",
+ displayName: "y",
+ tracker: () => snapshot == null ? 0 : snapshot.py
+ ),
+
+ ExportVariable (
+ handle: "playtime",
+ displayName: "Playtime",
+ tracker: () => snapshot == null ? 0 : snapshot.playtime
+ ),
+
+ ExportVariable (
+ handle: "blackness",
+ displayName: "Blackness",
+ tracker: () => snapshot == null ? 0 : snapshot.blackness
+ ),
+ });
+ }
+
+ private ExportableVariable ExportVariable(string handle, string displayName, Func tracker)
+ {
+ return new ExportableVariable(handle, displayName, tracker);
+ }
+ }
+}
diff --git a/rabi_splitter_WPF/VariableExportContext.cs b/rabi_splitter_WPF/VariableExportContext.cs
new file mode 100644
index 0000000..e2b20ac
--- /dev/null
+++ b/rabi_splitter_WPF/VariableExportContext.cs
@@ -0,0 +1,133 @@
+using rabi_splitter_WPF.Annotations;
+using System;
+using System.Collections.Generic;
+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 List _variables;
+ private Dictionary variableValues;
+
+ private ItemCollection variableListBoxItems;
+ private ItemCollection variableExportListBoxItems;
+
+ public VariableExportContext()
+ {
+ _variableExportSettings = DefaultVariableExportSettings();
+ _variables = new List();
+ variableValues = new Dictionary();
+ }
+
+ private List DefaultVariableExportSettings()
+ {
+ return new List
+ {
+ new VariableExportSetting() {
+ OutputFileName = "deaths_restarts.txt",
+ OutputFormat = "Deaths: {deaths}\nRestarts: {restarts}"
+ },
+ new VariableExportSetting() {
+ OutputFileName = "map.txt",
+ OutputFormat = "Map: {map}\nTile: {mapTile}"
+ },
+ new VariableExportSetting() {
+ OutputFileName = "music.txt",
+ OutputFormat = "Music: {music}"
+ },
+ new VariableExportSetting() {
+ OutputFileName = "currentboss.txt",
+ OutputFormat = "Current Boss: {currentBoss}\nTime: {currentBossTime:mm\\:ss\\.ff}"
+ },
+ new VariableExportSetting() {
+ OutputFileName = "lastboss.txt",
+ OutputFormat = "Last Boss: {lastBoss}\nTime: {lastBossTime:mm\\:ss\\.ff}"
+ },
+ new VariableExportSetting() {
+ OutputFileName = "hammer.txt",
+ OutputFormat = "Hammer: {hammerXp}/{nextHammerExp}\nNext: {nextHammerNameLong}"
+ },
+ new VariableExportSetting() {
+ OutputFileName = "ribbon.txt",
+ OutputFormat = "Hammer: {ribbonXp}/{nextRibbonExp}\nNext: {nextRibbonNameLong}"
+ },
+ new VariableExportSetting() {
+ OutputFileName = "carrot.txt",
+ OutputFormat = "Hammer: {carrotXp}/{nextCarrotExp}\nNext: {nextCarrotNameLong}"
+ },
+ };
+ }
+
+ #region Update Logic
+
+ public void UpdateVariables(bool updateFile)
+ {
+ foreach (var variable in _variables) {
+ variable.UpdateValue();
+ variableValues[variable.Handle] = variable.Value;
+ }
+
+ foreach (var ves in _variableExportSettings)
+ {
+ ves.UpdateText(variableValues);
+ if (updateFile) ves.MaybeUpdateFile();
+ }
+ }
+
+ internal void SetItemControls(ItemCollection variableListBoxItems, ItemCollection variableExportListBoxItems)
+ {
+ this.variableListBoxItems = variableListBoxItems;
+ this.variableExportListBoxItems = variableExportListBoxItems;
+ }
+
+ public void DefineVariableExports(ExportableVariable[] exports)
+ {
+ Variables = exports.ToList();
+ variableValues.Clear();
+ }
+ #endregion
+
+ #region Properties
+ public List Variables
+ {
+ get { return _variables; }
+ private set
+ {
+ _variables = value;
+ variableListBoxItems.Refresh();
+ }
+ }
+
+ public List VariableExportSettings
+ {
+ get { return _variableExportSettings; }
+ }
+
+ internal void Add(VariableExportSetting ves)
+ {
+ _variableExportSettings.Add(ves);
+ variableExportListBoxItems.Refresh();
+ }
+
+ internal void Delete(VariableExportSetting ves)
+ {
+ _variableExportSettings.Remove(ves);
+ variableExportListBoxItems.Refresh();
+ }
+ #endregion
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void OnPropertyChanged(string propertyName)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
diff --git a/rabi_splitter_WPF/VariableExportSetting.cs b/rabi_splitter_WPF/VariableExportSetting.cs
new file mode 100644
index 0000000..298dfc5
--- /dev/null
+++ b/rabi_splitter_WPF/VariableExportSetting.cs
@@ -0,0 +1,200 @@
+using rabi_splitter_WPF.Annotations;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using StringInject;
+
+namespace rabi_splitter_WPF
+{
+ public class ExportableVariable : ExportableVariable
+ {
+ private readonly Func tracker;
+
+ public ExportableVariable(string handle, string displayName, Func tracker) : base(handle, displayName)
+ {
+ this.tracker = tracker;
+ }
+
+ public override void UpdateValue()
+ {
+ Value = tracker();
+ }
+ }
+
+ public abstract class ExportableVariable : INotifyPropertyChanged
+ {
+ private readonly int _id;
+ private readonly string _displayName;
+ private readonly string _handle;
+
+ private object _value;
+
+ protected ExportableVariable(string handle, string displayName)
+ {
+ _handle = handle;
+ _displayName = displayName;
+ }
+
+ public int Id
+ {
+ get { return _id; }
+ }
+
+ public string DisplayName
+ {
+ get { return _displayName; }
+ }
+
+ public string Handle
+ {
+ get { return _handle; }
+ }
+
+ public object Value
+ {
+ get { return _value; }
+ protected set
+ {
+ if (value.Equals(_value)) return;
+ _value = value;
+ OnPropertyChanged(nameof(Value));
+ }
+ }
+
+ public abstract void UpdateValue();
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void OnPropertyChanged(string propertyName)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+
+ public class VariableExportSetting : INotifyPropertyChanged
+ {
+
+ private string _outputFileName;
+ private string _outputFormat;
+ private string _formatPreview;
+ private bool _isExporting;
+ private bool _isPreviewingFormat;
+ private bool _hasChangedSinceLastFileOutput;
+
+ public VariableExportSetting()
+ {
+ // Default values
+ _outputFileName = "";
+ _outputFormat = "";
+ _isExporting = false;
+ _isPreviewingFormat = false;
+ _hasChangedSinceLastFileOutput = true;
+ }
+
+ #region Logic
+ private string FormatOutput(Dictionary variableValues)
+ {
+ try
+ {
+ return _outputFormat.Inject(variableValues);
+ }
+ catch (FormatException e)
+ {
+ return e.Message;
+ }
+ }
+
+ internal void UpdateText(Dictionary variableValues)
+ {
+ var formattedOutput = FormatOutput(variableValues);
+ if (formattedOutput != FormatPreview)
+ {
+ FormatPreview = formattedOutput;
+ _hasChangedSinceLastFileOutput = true;
+ }
+ }
+
+ internal void MaybeUpdateFile()
+ {
+ if (!_hasChangedSinceLastFileOutput || !IsExporting) return;
+
+ System.IO.StreamWriter file = new System.IO.StreamWriter(OutputFileName);
+ file.WriteLine(FormatPreview);
+ file.Close();
+
+ _hasChangedSinceLastFileOutput = false;
+ }
+
+ #endregion
+
+ #region Parameters
+
+ public string OutputFileName
+ {
+ get { return _outputFileName; }
+ set
+ {
+ if (value.Equals(_outputFileName)) return;
+ _outputFileName = value;
+ OnPropertyChanged(nameof(OutputFileName));
+ _hasChangedSinceLastFileOutput = true;
+ }
+ }
+
+ public string OutputFormat
+ {
+ get { return _outputFormat; }
+ set
+ {
+ if (value.Equals(_outputFormat)) return;
+ _outputFormat = value;
+ OnPropertyChanged(nameof(OutputFormat));
+ }
+ }
+
+ public string FormatPreview
+ {
+ get { return _formatPreview; }
+ private set
+ {
+ if (value.Equals(_formatPreview)) return;
+ _formatPreview = value;
+ OnPropertyChanged(nameof(FormatPreview));
+ }
+ }
+
+ public bool IsPreviewingFormat
+ {
+ get { return _isPreviewingFormat; }
+ set
+ {
+ if (value.Equals(_isPreviewingFormat)) return;
+ _isPreviewingFormat = value;
+ OnPropertyChanged(nameof(IsPreviewingFormat));
+ }
+ }
+
+ public bool IsExporting
+ {
+ get { return _isExporting; }
+ set
+ {
+ if (value.Equals(_isExporting)) return;
+ _isExporting = value;
+ OnPropertyChanged(nameof(IsExporting));
+ }
+ }
+ #endregion
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void OnPropertyChanged(string propertyName)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
diff --git a/rabi_splitter_WPF/VariableExportTab.xaml b/rabi_splitter_WPF/VariableExportTab.xaml
new file mode 100644
index 0000000..de3f7b8
--- /dev/null
+++ b/rabi_splitter_WPF/VariableExportTab.xaml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rabi_splitter_WPF/VariableExportTab.xaml.cs b/rabi_splitter_WPF/VariableExportTab.xaml.cs
new file mode 100644
index 0000000..050e507
--- /dev/null
+++ b/rabi_splitter_WPF/VariableExportTab.xaml.cs
@@ -0,0 +1,52 @@
+using rabi_splitter_WPF.Annotations;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace rabi_splitter_WPF
+{
+ ///
+ /// Interaction logic for VariableExportTab.xaml
+ ///
+ public partial class VariableExportTab : UserControl
+ {
+ private DebugContext debugContext;
+ private VariableExportContext variableExportContext;
+
+ public VariableExportTab()
+ {
+ InitializeComponent();
+ }
+
+ public void Initialise(DebugContext debugContext, VariableExportContext variableExportContext)
+ {
+ this.debugContext = debugContext;
+ this.variableExportContext = variableExportContext;
+ variableExportContext.SetItemControls(VariableListBox.Items, VariableExportListBox.Items);
+ }
+
+ private void AddButton_Click(object sender, RoutedEventArgs e)
+ {
+ var ves = new VariableExportSetting();
+ ves.OutputFileName = "Hello.txt";
+ variableExportContext.Add(ves);
+ }
+
+ private void CloseButton_Click(object sender, RoutedEventArgs e)
+ {
+ var variableExportSetting = (sender as Button).DataContext as VariableExportSetting;
+ variableExportContext.Delete(variableExportSetting);
+ }
+ }
+}
diff --git a/rabi_splitter_WPF/rabi_display.csproj b/rabi_splitter_WPF/rabi_display.csproj
index 8572f5a..29bca2c 100644
--- a/rabi_splitter_WPF/rabi_display.csproj
+++ b/rabi_splitter_WPF/rabi_display.csproj
@@ -38,6 +38,8 @@
+
+
@@ -64,6 +66,13 @@
+
+
+
+
+
+ VariableExportTab.xaml
+
MSBuild:Compile
Designer
@@ -77,6 +86,10 @@
MainWindow.xaml
Code
+
+ Designer
+ MSBuild:Compile
+