454 lines
16 KiB
C#
454 lines
16 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Windows.Forms;
|
|
using System.IO;
|
|
using System.Globalization;
|
|
using System.Data;
|
|
|
|
namespace Tweaky
|
|
{
|
|
public class TweakyCore
|
|
{
|
|
private Regex EntryPattern = new Regex(@"((?<type>ANY\(.*\))|(?<type>RANGE\(.*\))|(?<type>COMBO\(.*\)))\s*\=\s*(?<value>.*)\s*;");
|
|
private Regex AnyPattern = new Regex(@"ANY\((?<type>\w*),\s*(?<name>.*)\)");
|
|
private Regex ComboPattern = new Regex(@"COMBO\((?<type>\w*)\s*,\s*(?<options>.*),\s*(?<name>.*)\)");
|
|
private Regex RangePattern = new Regex(@"RANGE\((?<type>\w*)\s*,\s*(?<from>.*)\s*,\s*(?<to>.*),\s*(?<name>.*)\)");
|
|
|
|
private List<VarEntryBase> varEntries = new List<VarEntryBase>();
|
|
private Timer saveTimer;
|
|
|
|
public string FilePath {get; protected set; }
|
|
public bool HasWatchableVarEntries { get; protected set; }
|
|
|
|
public TweakyCore(string filePath)
|
|
{
|
|
this.FilePath = filePath;
|
|
this.ReadFileContent();
|
|
}
|
|
|
|
public void BuildUiElements(Control rootControl)
|
|
{
|
|
var table = new TableLayoutPanel();
|
|
table.AutoSize = true;
|
|
table.ColumnCount = 3;
|
|
table.RowCount = varEntries.Count;
|
|
|
|
foreach (RowStyle style in table.RowStyles)
|
|
style.SizeType = SizeType.AutoSize;
|
|
|
|
var currentRowIndex = 0;
|
|
foreach (var entry in varEntries)
|
|
{
|
|
var localEntry = entry;
|
|
|
|
Control editControl = entry.CreateUi();
|
|
if (editControl != null)
|
|
{
|
|
var nameLabel = new Label();
|
|
nameLabel.Text = entry.Name;
|
|
nameLabel.Dock = DockStyle.Fill;
|
|
nameLabel.AutoSize = true;
|
|
nameLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
|
|
|
var resetControl = new Button();
|
|
resetControl.Text = "Reset";
|
|
resetControl.Dock = DockStyle.Fill;
|
|
resetControl.AutoSize = true;
|
|
resetControl.Click += (s, e) => localEntry.Reset();
|
|
resetControl.Enabled = false;
|
|
|
|
table.Controls.Add(nameLabel, 0, currentRowIndex);
|
|
table.Controls.Add(editControl, 1, currentRowIndex);
|
|
table.Controls.Add(resetControl, 2, currentRowIndex);
|
|
|
|
localEntry.OnValueChanged += () =>
|
|
{
|
|
resetControl.Enabled = !localEntry.HasDefaultValue;
|
|
};
|
|
|
|
currentRowIndex++;
|
|
}
|
|
}
|
|
rootControl.Controls.Add(table);
|
|
}
|
|
|
|
protected void ReadFileContent()
|
|
{
|
|
using (var stringReader = new StreamReader(FilePath))
|
|
{
|
|
string line;
|
|
while ((line = stringReader.ReadLine()) != null)
|
|
{
|
|
var entryMatch = EntryPattern.Match(line.Trim());
|
|
if (!entryMatch.Success)
|
|
{
|
|
varEntries.Add(new VarEntryBase()
|
|
{
|
|
Name = line,
|
|
});
|
|
continue;
|
|
}
|
|
|
|
var maintype = entryMatch.Groups["type"].Value;
|
|
|
|
var anyMatch = AnyPattern.Match(maintype);
|
|
var rangeMatch = RangePattern.Match(maintype);
|
|
var comboMatch = ComboPattern.Match(maintype);
|
|
|
|
VarEntryBase newEntry = null;
|
|
if (anyMatch.Success)
|
|
{
|
|
var type = anyMatch.Groups["type"].Value;
|
|
newEntry = VarEntryUtility.CreateType(typeof(VarEntryT<>), type,
|
|
VarEntryUtility.ConvertToObject(type, entryMatch.Groups["value"].Value));
|
|
newEntry.Name = anyMatch.Groups["name"].Value;
|
|
this.HasWatchableVarEntries = true;
|
|
}
|
|
else if (rangeMatch.Success)
|
|
{
|
|
var type = rangeMatch.Groups["type"].Value;
|
|
newEntry = VarEntryUtility.CreateType(typeof(RangeEntryT<>), type,
|
|
VarEntryUtility.ConvertToObject(type, entryMatch.Groups["value"].Value),
|
|
VarEntryUtility.ConvertToObject(type, rangeMatch.Groups["from"].Value),
|
|
VarEntryUtility.ConvertToObject(type, rangeMatch.Groups["to"].Value));
|
|
newEntry.Name = rangeMatch.Groups["name"].Value;
|
|
this.HasWatchableVarEntries = true;
|
|
}
|
|
else if (comboMatch.Success)
|
|
{
|
|
var type = comboMatch.Groups["type"].Value;
|
|
var options = comboMatch.Groups["options"].Value
|
|
.Trim('\"').Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
|
.Select(o => VarEntryUtility.ConvertToObject(type, o.Trim()))
|
|
.ToList();
|
|
newEntry = VarEntryUtility.CreateType(typeof(ComboEntryT<>), type,
|
|
VarEntryUtility.ConvertToObject(type, entryMatch.Groups["value"].Value),
|
|
options);
|
|
newEntry.Name = comboMatch.Groups["name"].Value;
|
|
this.HasWatchableVarEntries = true;
|
|
}
|
|
|
|
|
|
newEntry.OnValueChanged += () => OnValueChanged();
|
|
|
|
varEntries.Add(newEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void OnValueChanged()
|
|
{
|
|
if (saveTimer != null)
|
|
return; // save is already pending
|
|
saveTimer = new Timer();
|
|
saveTimer.Interval = 1200; // > one second till the save call is progressed
|
|
saveTimer.Tick += (s, e) => WriteFileContent();
|
|
saveTimer.Start();
|
|
}
|
|
|
|
public void WriteFileContent()
|
|
{
|
|
if (saveTimer != null)
|
|
{
|
|
saveTimer.Stop();
|
|
saveTimer.Dispose();
|
|
saveTimer = null;
|
|
}
|
|
|
|
using (var streamWriter = new StreamWriter(FilePath, false))
|
|
{
|
|
foreach (var entry in varEntries)
|
|
streamWriter.Write(entry.ToString() + "\r\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
public static class VarEntryUtility
|
|
{
|
|
public static string GetTypeName<T>()
|
|
{
|
|
if (typeof(T) == typeof(bool))
|
|
return "bool";
|
|
else if (typeof(T) == typeof(int))
|
|
return "int";
|
|
else if (typeof(T) == typeof(float))
|
|
return "float";
|
|
return string.Empty;
|
|
}
|
|
|
|
private static Type GetType(Type baseType, string type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case "int":
|
|
return baseType.MakeGenericType(typeof(int));
|
|
case "float":
|
|
return baseType.MakeGenericType(typeof(float));
|
|
case "bool":
|
|
return baseType.MakeGenericType(typeof(bool));
|
|
default:
|
|
throw new ArgumentException(type);
|
|
}
|
|
}
|
|
|
|
public static VarEntryBase CreateType(Type baseType, string type, params object[] args)
|
|
{
|
|
return Activator.CreateInstance(GetType(baseType, type), args) as VarEntryBase;
|
|
}
|
|
|
|
public static object ConvertToObject(string type, string rawValue)
|
|
{
|
|
switch (type)
|
|
{
|
|
case "bool":
|
|
return bool.Parse(rawValue);
|
|
case "int":
|
|
return int.Parse(rawValue);
|
|
case "float":
|
|
return float.Parse(rawValue.TrimEnd('f'), CultureInfo.InvariantCulture);
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static string ConvertToString<T>(object rawValue)
|
|
{
|
|
if (typeof(T) == typeof(bool))
|
|
return (bool)rawValue == true ? "true" : "false";
|
|
if (typeof(T) == typeof(int))
|
|
return ((int)rawValue).ToString(CultureInfo.InvariantCulture);
|
|
if (typeof(T) == typeof(float))
|
|
return ((float)rawValue).ToString(CultureInfo.InvariantCulture);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public class VarEntryBase
|
|
{
|
|
public string Name;
|
|
|
|
public Action OnValueChanged;
|
|
|
|
public virtual Control CreateUi() { return null; }
|
|
|
|
public virtual void Reset() {}
|
|
|
|
public override string ToString()
|
|
{
|
|
return this.Name;
|
|
}
|
|
|
|
public virtual bool HasDefaultValue { get { return false; } }
|
|
}
|
|
|
|
public class VarEntryT<T> : VarEntryBase
|
|
{
|
|
public VarEntryT(T defaultValue)
|
|
{
|
|
this.CurrentValue = this.DefaultValue = defaultValue;
|
|
}
|
|
|
|
public T CurrentValue;
|
|
public T DefaultValue;
|
|
|
|
public override string ToString()
|
|
{
|
|
return string.Format("ANY({0}, {1}) = {2};",
|
|
VarEntryUtility.GetTypeName<T>(),
|
|
this.Name,
|
|
VarEntryUtility.ConvertToString<T>(this.CurrentValue));
|
|
}
|
|
|
|
protected float ToFloat(object value)
|
|
{
|
|
if (value.GetType() == typeof(int))
|
|
return (float)(int)value;
|
|
else
|
|
return (float)value;
|
|
}
|
|
|
|
protected T FromFloat(float value)
|
|
{
|
|
if (typeof(T) == typeof(int))
|
|
return (T)(object)(int)value;
|
|
else
|
|
return (T)(object)value; ;
|
|
}
|
|
|
|
|
|
private CheckBox checkBox;
|
|
private NumericUpDown numBox;
|
|
|
|
public override Control CreateUi()
|
|
{
|
|
if (typeof(T) == typeof(bool))
|
|
{
|
|
checkBox = new CheckBox();
|
|
checkBox.Dock = DockStyle.Left;
|
|
checkBox.Checked = (bool)(object)this.CurrentValue;
|
|
checkBox.CheckedChanged += (s, e) =>
|
|
{
|
|
this.CurrentValue = (T)(object)checkBox.Checked;
|
|
if (this.OnValueChanged != null)
|
|
this.OnValueChanged();
|
|
};
|
|
return checkBox;
|
|
}
|
|
else
|
|
{
|
|
numBox = new NumericUpDown();
|
|
numBox.DecimalPlaces = 6;
|
|
numBox.Dock = DockStyle.Fill;
|
|
numBox.Value = (decimal)ToFloat(this.CurrentValue);
|
|
numBox.ValueChanged += (s, e) =>
|
|
{
|
|
var oldValue = this.CurrentValue;
|
|
this.CurrentValue = FromFloat((float)numBox.Value);
|
|
if (this.OnValueChanged != null && !oldValue.Equals(this.CurrentValue))
|
|
this.OnValueChanged();
|
|
};
|
|
|
|
return numBox;
|
|
}
|
|
}
|
|
|
|
public override void Reset()
|
|
{
|
|
this.CurrentValue = this.DefaultValue;
|
|
|
|
if (typeof(T) == typeof(bool))
|
|
checkBox.Checked = (bool)(object)this.CurrentValue;
|
|
else
|
|
numBox.Value = (decimal)ToFloat(this.CurrentValue);
|
|
|
|
this.OnValueChanged();
|
|
}
|
|
|
|
public override bool HasDefaultValue { get { return this.CurrentValue.Equals(this.DefaultValue); } }
|
|
}
|
|
|
|
public class ComboEntryT<T> : VarEntryT<T>
|
|
{
|
|
public IEnumerable<T> Values;
|
|
|
|
public ComboEntryT(T defaultValue, IEnumerable<object> values)
|
|
: base(defaultValue)
|
|
{
|
|
this.Values = values.Select(o => (T)o);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
var options = string.Join(", ", this.Values
|
|
.Select(o => VarEntryUtility.ConvertToString<T>(o))
|
|
.ToArray());
|
|
return string.Format("COMBO({0}, \"{1}\", {2}) = {3};",
|
|
VarEntryUtility.GetTypeName<T>(),
|
|
options,
|
|
this.Name,
|
|
VarEntryUtility.ConvertToString<T>(this.CurrentValue));
|
|
}
|
|
|
|
private ComboBox comboBox;
|
|
public override Control CreateUi()
|
|
{
|
|
comboBox = new ComboBox();
|
|
comboBox.DropDownStyle = ComboBoxStyle.DropDownList;
|
|
foreach (var option in Values)
|
|
comboBox.Items.Add(option);
|
|
comboBox.SelectedItem = Values.FirstOrDefault(o => o.Equals(this.CurrentValue));
|
|
comboBox.Dock = DockStyle.Fill;
|
|
comboBox.SelectedValueChanged += (s, e) =>
|
|
{
|
|
var oldValue = this.CurrentValue;
|
|
this.CurrentValue = (T)comboBox.SelectedItem;
|
|
if (OnValueChanged != null && !oldValue.Equals(this.CurrentValue))
|
|
OnValueChanged();
|
|
};
|
|
return comboBox;
|
|
}
|
|
|
|
public override void Reset()
|
|
{
|
|
this.CurrentValue = this.DefaultValue;
|
|
|
|
comboBox.SelectedItem = Values.FirstOrDefault(o => o.Equals(this.CurrentValue));
|
|
|
|
this.OnValueChanged();
|
|
}
|
|
}
|
|
|
|
public class RangeEntryT<T> : VarEntryT<T>
|
|
{
|
|
public T From;
|
|
public T To;
|
|
|
|
public RangeEntryT(T defaultValue, T from, T to)
|
|
: base(defaultValue)
|
|
{
|
|
this.From = from;
|
|
this.To = to;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return string.Format("RANGE({0}, {1}, {2}, {3}) = {4};",
|
|
VarEntryUtility.GetTypeName<T>(),
|
|
VarEntryUtility.ConvertToString<T>(this.From),
|
|
VarEntryUtility.ConvertToString<T>(this.To),
|
|
this.Name,
|
|
VarEntryUtility.ConvertToString<T>(this.CurrentValue));
|
|
}
|
|
|
|
private TrackBar trackBar;
|
|
|
|
public override Control CreateUi()
|
|
{
|
|
var panel = new Panel();
|
|
panel.Dock = DockStyle.Fill;
|
|
panel.Height = 24;
|
|
|
|
var textBox = new TextBox();
|
|
textBox.MaximumSize = new System.Drawing.Size(50, 24);
|
|
textBox.Enabled = false;
|
|
textBox.Text = VarEntryUtility.ConvertToString<T>(this.CurrentValue);
|
|
textBox.Dock = DockStyle.Left;
|
|
|
|
trackBar = new TrackBar();
|
|
trackBar.TickStyle = TickStyle.None;
|
|
int Multiplicator = 100;
|
|
trackBar.Minimum = (int)(ToFloat(this.From) * Multiplicator);
|
|
trackBar.Maximum = (int)(ToFloat(this.To) * Multiplicator);
|
|
trackBar.Value = (int)(ToFloat(this.CurrentValue) * Multiplicator);
|
|
|
|
trackBar.Dock = DockStyle.Fill;
|
|
trackBar.ValueChanged += (s, e) =>
|
|
{
|
|
var oldValue = ToFloat(this.CurrentValue);
|
|
this.CurrentValue = FromFloat(trackBar.Value / (float)Multiplicator);
|
|
if (this.OnValueChanged != null && !oldValue.Equals(this.CurrentValue))
|
|
this.OnValueChanged();
|
|
|
|
textBox.Text = VarEntryUtility.ConvertToString<T>(this.CurrentValue);
|
|
};
|
|
|
|
panel.Controls.Add(trackBar);
|
|
panel.Controls.Add(textBox);
|
|
|
|
return panel;
|
|
}
|
|
|
|
public override void Reset()
|
|
{
|
|
this.CurrentValue = this.DefaultValue;
|
|
int Multiplicator = 100;
|
|
trackBar.Value = (int)(ToFloat(this.CurrentValue) * Multiplicator);
|
|
|
|
this.OnValueChanged();
|
|
}
|
|
}
|
|
}
|