Merge branch 'variableexport' into rabi_display

rabi_display
wcko87 8 years ago
commit 07c2c233f8
  1. 31
      rabi_splitter_WPF/MainContext.cs
  2. 11
      rabi_splitter_WPF/MainWindow.xaml
  3. 6
      rabi_splitter_WPF/MainWindow.xaml.cs
  4. 11
      rabi_splitter_WPF/MemorySnapshot.cs
  5. 39
      rabi_splitter_WPF/RabiRibiDisplay.cs
  6. 116
      rabi_splitter_WPF/StringInjectExtension.cs
  7. 228
      rabi_splitter_WPF/VariableExportConfig.cs
  8. 133
      rabi_splitter_WPF/VariableExportContext.cs
  9. 200
      rabi_splitter_WPF/VariableExportSetting.cs
  10. 78
      rabi_splitter_WPF/VariableExportTab.xaml
  11. 52
      rabi_splitter_WPF/VariableExportTab.xaml.cs
  12. 13
      rabi_splitter_WPF/rabi_display.csproj

@ -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;

@ -10,7 +10,8 @@
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<local:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
</Window.Resources>
<DockPanel>
<TabControl>
<TabItem Header="Display">
<!--<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left">
<TextBlock Text="v0.3.2" Margin="0,0,30,0" VerticalAlignment="Top" />
<TextBlock Text="GitHub" MouseUp="TextBlock_MouseUp" Cursor="Hand" Foreground="Blue" TextDecorations="Underline" VerticalAlignment="Top" />
@ -108,9 +109,11 @@
<TextBox TextWrapping="Wrap" Text="{Binding DebugLog, Mode=TwoWay}" Grid.Row="1" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" TextChanged="TextBox_TextChanged" VerticalScrollBarVisibility="Visible"/>
</Grid>
</DockPanel>
</DockPanel>
</DockPanel>
</TabItem>
<TabItem Header="Variable Export">
<local:VariableExportTab x:Name="VariableExportTab" d:DataContext="{d:DesignData local:PracticeModeContext}"/>
</TabItem>
</TabControl>
</Window>

@ -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)

@ -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<int, string, string> nextHammer;
public readonly Tuple<int, string, string> nextRibbon;
public readonly Tuple<int, string, string> nextCarrot;
public readonly int nAttackUps;
public readonly int nHpUps;
public readonly int nManaUps;
@ -108,6 +116,9 @@ namespace rabi_splitter_WPF
hammerXp = memoryHelper.GetMemoryValue<int>(0xD654B4);
ribbonXp = memoryHelper.GetMemoryValue<int>(0xD654B8);
itemPercent = memoryHelper.GetMemoryValue<float>(0xA730E8);
nextHammer = StaticData.GetNextHammerLevel(hammerXp);
nextRibbon = StaticData.GetNextRibbonLevel(ribbonXp);
nextCarrot = StaticData.GetNextCarrotLevel(carrotXp);
minimapPosition = memoryHelper.GetMemoryValue<int>(0xA72E08);

@ -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,6 +51,7 @@ namespace rabi_splitter_WPF
UpdateDebugArea(memoryHelper);
UpdateEntityData(memoryHelper);
UpdateFps();
UpdateVariableExport();
if (snapshot.musicid >= 0) rabiRibiState.lastValidMusicId = snapshot.musicid;
prevSnapshot = snapshot;
@ -54,6 +59,23 @@ namespace rabi_splitter_WPF
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()
{
long currentFrameMillisecond = (long)(DateTime.Now - UNIX_START).TotalMilliseconds;
@ -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!");
}
@ -275,15 +295,6 @@ namespace rabi_splitter_WPF
}
}
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();

@ -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
{
/// <summary>
/// Extension method that replaces keys in a string with the values of matching object properties.
/// <remarks>Uses <see cref="String.Format()"/> internally; custom formats should match those used for that method.</remarks>
/// </summary>
/// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
/// <param name="injectionObject">The object whose properties should be injected in the string</param>
/// <returns>A version of the formatString string with keys replaced by (formatted) key values.</returns>
public static string Inject(this string formatString, object injectionObject)
{
return formatString.Inject(GetPropertyHash(injectionObject));
}
/// <summary>
/// Extension method that replaces keys in a string with the values of matching dictionary entries.
/// <remarks>Uses <see cref="String.Format()"/> internally; custom formats should match those used for that method.</remarks>
/// </summary>
/// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
/// <param name="dictionary">An <see cref="IDictionary"/> with keys and values to inject into the string</param>
/// <returns>A version of the formatString string with dictionary keys replaced by (formatted) key values.</returns>
public static string Inject(this string formatString, IDictionary dictionary)
{
return formatString.Inject(new Hashtable(dictionary));
}
/// <summary>
/// Extension method that replaces keys in a string with the values of matching hashtable entries.
/// <remarks>Uses <see cref="String.Format()"/> internally; custom formats should match those used for that method.</remarks>
/// </summary>
/// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
/// <param name="attributes">A <see cref="Hashtable"/> with keys and values to inject into the string</param>
/// <returns>A version of the formatString string with hastable keys replaced by (formatted) key values.</returns>
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;
}
/// <summary>
/// Replaces all instances of a 'key' (e.g. {foo} or {foo:SomeFormat}) in a string with an optionally formatted value, and returns the result.
/// </summary>
/// <param name="formatString">The string containing the key; unformatted ({foo}), or formatted ({foo:SomeFormat})</param>
/// <param name="key">The key name (foo)</param>
/// <param name="replacementValue">The replacement value; if null is replaced with an empty string</param>
/// <returns>The input string with any instances of the key replaced with the replacement value</returns>
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;
}
/// <summary>
/// Creates a HashTable based on current object state.
/// <remarks>Copied from the MVCToolkit HtmlExtensionUtility class</remarks>
/// </summary>
/// <param name="properties">The object from which to get the properties</param>
/// <returns>A <see cref="Hashtable"/> containing the object instance's property names and their values</returns>
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;
}
}
}

@ -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<int> (
handle: "deaths",
displayName: "Deaths",
tracker: () => inGameState == null ? 0 : inGameState.nDeaths
),
ExportVariable<int> (
handle: "restarts",
displayName: "Restarts",
tracker: () => inGameState == null ? 0 : inGameState.nRestarts
),
ExportVariable<string> (
handle: "currentBoss",
displayName: "Current Boss Fight",
tracker: () => (inGameState == null || !inGameState.CurrentActivityIs(InGameActivity.BOSS_BATTLE)) ?
"None" :
inGameState.currentBossFight.displayName
),
ExportVariable<TimeSpan> (
handle: "currentBossTime",
displayName: "Current Boss Time",
tracker: () => (inGameState == null || !inGameState.CurrentActivityIs(InGameActivity.BOSS_BATTLE)) ?
TimeSpan.Zero :
(DateTime.Now - inGameState.currentBossStartTime)
),
ExportVariable<string> (
handle: "lastBoss",
displayName: "Last Boss Fight",
tracker: () => inGameState?.lastBossFight == null ? "None" : inGameState.lastBossFight.displayName
),
ExportVariable<TimeSpan> (
handle: "lastBossTime",
displayName: "Last Boss Time",
tracker: () => inGameState?.lastBossFight == null ? TimeSpan.Zero : inGameState.lastBossFightDuration
),
ExportVariable<int> (
handle: "musicid",
displayName: "Music Id",
tracker: () => snapshot == null ? 0 : snapshot.musicid
),
ExportVariable<string> (
handle: "music",
displayName: "Music",
tracker: () => snapshot == null ? "" : StaticData.GetMusicName(snapshot.musicid)
),
ExportVariable<int> (
handle: "mapid",
displayName: "Map Id",
tracker: () => snapshot == null ? 0 : snapshot.mapid
),
ExportVariable<string> (
handle: "map",
displayName: "Map",
tracker: () => snapshot == null ? "" : StaticData.GetMapName(snapshot.mapid)
),
ExportVariable<string> (
handle: "mapTile",
displayName: "Map Tile",
tracker: () => snapshot == null ? "" : snapshot.mapTile.ToString()
),
ExportVariable<int> (
handle: "hp",
displayName: "HP",
tracker: () => snapshot == null ? 0 : snapshot.hp
),
ExportVariable<int> (
handle: "maxhp",
displayName: "Max HP",
tracker: () => snapshot == null ? 0 : snapshot.maxhp
),
ExportVariable<float> (
handle: "itempercent",
displayName: "Item %",
tracker: () => snapshot == null ? 0 : snapshot.itemPercent
),
ExportVariable<int> (
handle: "hammerXp",
displayName: "Hammer Exp",
tracker: () => snapshot == null ? 0 : snapshot.hammerXp
),
ExportVariable<int> (
handle: "nextHammerExp",
displayName: "Next Hammer Level Exp",
tracker: () => snapshot == null ? 0 : snapshot.nextHammer.Item1
),
ExportVariable<string> (
handle: "nextHammerName",
displayName: "Next Hammer Level Name (Short)",
tracker: () => snapshot == null ? "" : snapshot.nextHammer.Item2
),
ExportVariable<string> (
handle: "nextHammerNameLong",
displayName: "Next Hammer Level Name (Long)",
tracker: () => snapshot == null ? "" : snapshot.nextHammer.Item3
),
ExportVariable<int> (
handle: "ribbonXp",
displayName: "Ribbon Exp",
tracker: () => snapshot == null ? 0 : snapshot.ribbonXp
),
ExportVariable<int> (
handle: "nextRibbonExp",
displayName: "Next Ribbon Level Exp",
tracker: () => snapshot == null ? 0 : snapshot.nextRibbon.Item1
),
ExportVariable<string> (
handle: "nextRibbonName",
displayName: "Next Ribbon Level Name (Short)",
tracker: () => snapshot == null ? "" : snapshot.nextRibbon.Item2
),
ExportVariable<string> (
handle: "nextRibbonNameLong",
displayName: "Next Ribbon Level Name (Long)",
tracker: () => snapshot == null ? "" : snapshot.nextRibbon.Item3
),
ExportVariable<int> (
handle: "carrotXp",
displayName: "Carrot Exp",
tracker: () => snapshot == null ? 0 : snapshot.carrotXp
),
ExportVariable<int> (
handle: "nextCarrotExp",
displayName: "Next Carrot Level Exp",
tracker: () => snapshot == null ? 0 : snapshot.nextCarrot.Item1
),
ExportVariable<string> (
handle: "nextCarrotName",
displayName: "Next Carrot Level Name (Short)",
tracker: () => snapshot == null ? "" : snapshot.nextCarrot.Item2
),
ExportVariable<string> (
handle: "nextCarrotNameLong",
displayName: "Next Carrot Level Name (Long)",
tracker: () => snapshot == null ? "" : snapshot.nextCarrot.Item3
),
ExportVariable<float> (
handle: "amulet",
displayName: "Amulet",
tracker: () => snapshot == null ? 0 : snapshot.amulet
),
ExportVariable<int> (
handle: "boost",
displayName: "Boost",
tracker: () => snapshot == null ? 0 : snapshot.boost
),
ExportVariable<float> (
handle: "mana",
displayName: "MP",
tracker: () => snapshot == null ? 0 : snapshot.mana
),
ExportVariable<int> (
handle: "stamina",
displayName: "SP",
tracker: () => snapshot == null ? 0 : snapshot.stamina
),
ExportVariable<float> (
handle: "x",
displayName: "x",
tracker: () => snapshot == null ? 0 : snapshot.px
),
ExportVariable<float> (
handle: "y",
displayName: "y",
tracker: () => snapshot == null ? 0 : snapshot.py
),
ExportVariable<int> (
handle: "playtime",
displayName: "Playtime",
tracker: () => snapshot == null ? 0 : snapshot.playtime
),
ExportVariable<int> (
handle: "blackness",
displayName: "Blackness",
tracker: () => snapshot == null ? 0 : snapshot.blackness
),
});
}
private ExportableVariable<T> ExportVariable<T>(string handle, string displayName, Func<T> tracker)
{
return new ExportableVariable<T>(handle, displayName, tracker);
}
}
}

@ -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<VariableExportSetting> _variableExportSettings;
private List<ExportableVariable> _variables;
private Dictionary<string, object> variableValues;
private ItemCollection variableListBoxItems;
private ItemCollection variableExportListBoxItems;
public VariableExportContext()
{
_variableExportSettings = DefaultVariableExportSettings();
_variables = new List<ExportableVariable>();
variableValues = new Dictionary<string, object>();
}
private List<VariableExportSetting> DefaultVariableExportSettings()
{
return new List<VariableExportSetting>
{
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<ExportableVariable> Variables
{
get { return _variables; }
private set
{
_variables = value;
variableListBoxItems.Refresh();
}
}
public List<VariableExportSetting> 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));
}
}
}

@ -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<T> : ExportableVariable
{
private readonly Func<T> tracker;
public ExportableVariable(string handle, string displayName, Func<T> 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<string, object> variableValues)
{
try
{
return _outputFormat.Inject(variableValues);
}
catch (FormatException e)
{
return e.Message;
}
}
internal void UpdateText(Dictionary<string, object> 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));
}
}
}

@ -0,0 +1,78 @@
<UserControl x:Class="rabi_splitter_WPF.VariableExportTab"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:rabi_splitter_WPF"
mc:Ignorable="d"
d:DesignHeight="500" d:DesignWidth="780">
<UserControl.Resources>
<local:InvertableBooleanToVisibilityConverter x:Key="InvertableBooleanToVisibilityConverter"/>
</UserControl.Resources>
<DockPanel Height="500" Width="780">
<DockPanel DockPanel.Dock="Left" Width="260">
<Button Content="Add Text File Export" Width="160" Height="30" Margin="5,5,5,5" DockPanel.Dock="Top" Click="AddButton_Click"/>
<TextBlock Margin="5,5,0,0" DockPanel.Dock="Top" Text="Variable Handle List"/>
<ListBox Name="VariableListBox" ItemsSource="{Binding Variables}" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="2,2,2,2">
<TextBlock FontFamily="Consolas" Margin="2,0,0,0" Text="{Binding Path=Handle}"/>
<Grid Margin="15,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="Description:"/>
<TextBlock Grid.Column="0" Grid.Row="1" Text="Value:"/>
<TextBlock Margin="2,0,0,0" Grid.Column="1" Grid.Row="0" Text="{Binding Path=DisplayName}" TextWrapping="Wrap"/>
<TextBlock Margin="2,0,0,0" Grid.Column="1" Grid.Row="1" Text="{Binding Path=Value}" TextWrapping="Wrap"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
<ListBox Name="VariableExportListBox" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Width="520" ItemsSource="{Binding VariableExportSettings}" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Name="MainPanel" Height="80" DockPanel.Dock="Top" Margin="5,0,5,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<DockPanel Grid.Column="0" Margin="0,5,0,5" >
<TextBlock DockPanel.Dock="Top" HorizontalAlignment="Left" Text="Format"/>
<TextBox Height="50" DockPanel.Dock="Top" Text="{Binding Path=OutputFormat, Mode=TwoWay}" AcceptsReturn="True"/>
</DockPanel>
<DockPanel Grid.Column="1" Margin="0,5,0,5" >
<TextBlock DockPanel.Dock="Top" HorizontalAlignment="Left" Text="Preview"/>
<TextBlock Height="50" DockPanel.Dock="Top" Text="{Binding Path=FormatPreview}"/>
</DockPanel>
<DockPanel Grid.Column="2" Margin="2,0,2,0">
<ToggleButton Margin="3,5,3,5" Content="Export" DockPanel.Dock="Bottom" IsChecked="{Binding Path=IsExporting, Mode=TwoWay}"/>
<TextBox DockPanel.Dock="Bottom" Text="{Binding Path=OutputFileName, Mode=TwoWay}"/>
<TextBlock DockPanel.Dock="Left" Text="Output File" VerticalAlignment="Bottom" Margin="0,0,0,2"/>
<Button Content="X" Width="20" Height="20" HorizontalAlignment="Right" Click="CloseButton_Click"/>
</DockPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</UserControl>

@ -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
{
/// <summary>
/// Interaction logic for VariableExportTab.xaml
/// </summary>
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);
}
}
}

@ -38,6 +38,8 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
@ -64,6 +66,13 @@
<Compile Include="RabiRibiDisplay.cs" />
<Compile Include="RabiRibiState.cs" />
<Compile Include="StaticData.cs" />
<Compile Include="StringInjectExtension.cs" />
<Compile Include="VariableExportConfig.cs" />
<Compile Include="VariableExportContext.cs" />
<Compile Include="VariableExportSetting.cs" />
<Compile Include="VariableExportTab.xaml.cs">
<DependentUpon>VariableExportTab.xaml</DependentUpon>
</Compile>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@ -77,6 +86,10 @@
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Page Include="VariableExportTab.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Compile Include="MemoryHelper.cs" />

Loading…
Cancel
Save