This commit is contained in:
sup39 2021-09-25 02:48:44 +08:00
parent cd6a6d14f7
commit 54f9fe590a
20 changed files with 1921 additions and 0 deletions

22
LICENSE Normal file
View file

@ -0,0 +1,22 @@
Copyright (c) 2021 sup39[サポミク]
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

21
README.md Normal file
View file

@ -0,0 +1,21 @@
# supAutoSplit
An auto splitter for LiveSplit using OpenCV and template matching.
## Installation
### Step 1. Install supAutoSplit
Download `LiveSplit.supAutoSplit.dll` from the [release page](https://github.com/sup39/supAutoSplit/releases), and put it into `(LiveSplit)\Components`
### Step 2. Install [opencvsharp](https://github.com/shimat/opencvsharp)
Download `OpenCvSharp-*.zip` (e.g. `OpenCvSharp-4.5.3-20210821.zip`) from the [release page](https://github.com/shimat/opencvsharp/releases), extract and copy:
- `(Zip)\ManagedLib\net461\OpenCvSharp.dll` to `(LiveSplit)\Components\`
- `(Zip)\NativeLib\win` (whole directory) to `(LiveSplit)\Components\`, and rename the directory to `dll`
### After the installation
There should be the following files in the `(LiveSplit)\Components` directory:
- `LiveSplit.supAutoSplit.dll`
- `OpenCvSharp.dll`
- `dll\x86\OpenCvSharpExtern.dll`
- `dll\x64\OpenCvSharpExtern.dll`
## Usage
- Right click on LiveSplit, and select `Edit Layout...`
- In Layout Editor, click the (+) button, in `Control` click `supAutoSplit`
- After adding `supAutoSplit` to the list, click the `Layout Settings` button, navigate to the `supAutoSplit` tab, add template and set the parameters

25
supAutoSplit.sln Normal file
View file

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.1622
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "supAutoSplit", "supAutoSplit\supAutoSplit.csproj", "{C1B6A79E-088A-411C-B546-4D72447B4DAA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C1B6A79E-088A-411C-B546-4D72447B4DAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C1B6A79E-088A-411C-B546-4D72447B4DAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C1B6A79E-088A-411C-B546-4D72447B4DAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C1B6A79E-088A-411C-B546-4D72447B4DAA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7FDD6D01-2B0C-45F7-A245-9FEE2E65724F}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,39 @@
using LiveSplit.UI.Components;
using LiveSplit.SupAutoSplit;
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: ComponentFactory(typeof(Factory))]
// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。
// アセンブリに関連付けられている情報を変更するには、
// これらの属性値を変更してください。
[assembly: AssemblyTitle("supAutoSplit")]
[assembly: AssemblyDescription("An auto splitter for LiveSplit using OpenCV and template matching")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("supAutoSplit")]
[assembly: AssemblyCopyright("Copyright © 2021 sup39[サポミク]")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// ComVisible を false に設定すると、このアセンブリ内の型は COM コンポーネントから
// 参照できなくなります。COM からこのアセンブリ内の型にアクセスする必要がある場合は、
// その型の ComVisible 属性を true に設定してください。
[assembly: ComVisible(false)]
// このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります
[assembly: Guid("c1b6a79e-088a-411c-b546-4d72447b4daa")]
// アセンブリのバージョン情報は次の 4 つの値で構成されています:
//
// メジャー バージョン
// マイナー バージョン
// ビルド番号
// Revision
//
// すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます
// 以下のように '*' を使用します:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.1.0.0")]
// [assembly: AssemblyFileVersion("0.1.0.0")]

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is automatically generated by Visual Studio .Net. It is
used to store generic object data source configuration information.
Renaming the file extension or editing the content of this file may
cause the file to be unrecognizable by the program.
-->
<GenericObjectDataSource DisplayName="TemplateSettings" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
<TypeInfo>LiveSplit.SupAutoSplit.UI.TemplateSettings, Livesplit.SupAutoSplit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
</GenericObjectDataSource>

View file

@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// このコードはツールによって生成されました。
// ランタイム バージョン:4.0.30319.42000
//
// このファイルへの変更は、以下の状況下で不正な動作の原因になったり、
// コードが再生成されるときに損失したりします。
// </auto-generated>
//------------------------------------------------------------------------------
namespace LiveSplit.Properties {
using System;
/// <summary>
/// ローカライズされた文字列などを検索するための、厳密に型指定されたリソース クラスです。
/// </summary>
// このクラスは StronglyTypedResourceBuilder クラスが ResGen
// または Visual Studio のようなツールを使用して自動生成されました。
// メンバーを追加または削除するには、.ResX ファイルを編集して、/str オプションと共に
// ResGen を実行し直すか、または VS プロジェクトをビルドし直します。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// このクラスで使用されているキャッシュされた ResourceManager インスタンスを返します。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LiveSplit.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// すべてについて、現在のスレッドの CurrentUICulture プロパティをオーバーライドします
/// 現在のスレッドの CurrentUICulture プロパティをオーバーライドします。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using System.Xml;
using LiveSplit.Model;
using LiveSplit.UI;
using LiveSplit.UI.Components;
using OpenCvSharp;
namespace LiveSplit.SupAutoSplit {
public sealed class Component : LogicComponent {
public override string ComponentName => "supAutoSplit";
private UI.Settings Settings { get; set; }
private LiveSplitState State { get; set; }
private TimerModel Model { get; }
public Component(LiveSplitState state) {
Settings = new UI.Settings();
State = state;
Model = new TimerModel {
CurrentState = state
};
ContextMenuControls = new Dictionary<string, Action> {
{ "Start supAutoSplit", Start }
};
}
private Thread captureThread;
private List<MatchHandler> handlersAll;
private List<MatchHandler> handlers;
private volatile bool handlerReady = false;
private void UpdateHandlers(object sender, EventArgs e) {
handlers = handlersAll.Where(h => h.Reset(State)).ToList();
handlerReady = true;
}
private void UpdateHandlers_Reset(object sender, TimerPhase e) => UpdateHandlers(null, null);
private void Start() {
ContextMenuControls.Clear();
ContextMenuControls.Add("Reload supAutoSplit", Reload);
ContextMenuControls.Add("Stop supAutoSplit", Stop);
Reload();
State.OnStart += UpdateHandlers;
State.OnSplit += UpdateHandlers;
State.OnSkipSplit += UpdateHandlers;
State.OnUndoSplit += UpdateHandlers;
State.OnPause += UpdateHandlers;
State.OnResume += UpdateHandlers;
State.OnReset += UpdateHandlers_Reset;
captureThread = new Thread(() => {
using (var capture = new VideoCapture(Settings.CaptureDevice))
using (var window = new Window(Settings.WindowName))
using (Mat frame = new Mat()) {
try {
// Stopwatch sw = new Stopwatch();
// Stopwatch sw1 = new Stopwatch();
// Stopwatch sw2 = new Stopwatch();
// sw.Start();
while (capture.Read(frame)) {
// sw1.Restart();
if (handlerReady) {
foreach (var handler in handlers) {
if (handler.Match(frame)) {
handlerReady = false;
handler.Action(Model);
break;
}
}
}
// sw1.Stop();
// sw2.Restart();
window.ShowImage(frame);
// sw2.Stop();
// sw.Stop();
// Debug.WriteLine($"{sw1.ElapsedMilliseconds:#0} ({sw1.ElapsedTicks}) {sw2.ElapsedMilliseconds:#0} ({sw2.ElapsedTicks}) {sw.ElapsedMilliseconds:#0}");
Cv2.WaitKey(1);
// sw.Restart();
}
} catch (ThreadInterruptedException) {}
}
});
captureThread.Start();
}
private void Reload() {
handlersAll = Settings.TemplateSettings.Select(o => new MatchHandler(o)).ToList();
UpdateHandlers(null, null);
}
private void Stop() {
captureThread?.Abort();
captureThread = null;
State.OnStart -= UpdateHandlers;
State.OnSplit -= UpdateHandlers;
State.OnSkipSplit -= UpdateHandlers;
State.OnUndoSplit -= UpdateHandlers;
State.OnPause -= UpdateHandlers;
State.OnResume -= UpdateHandlers;
State.OnReset -= UpdateHandlers_Reset;
ContextMenuControls.Clear();
ContextMenuControls.Add("Start supAutoSplit", Start);
}
public override void Dispose() {
captureThread?.Abort();
}
public override XmlNode GetSettings(XmlDocument document) => Settings.GetSettings(document);
public override Control GetSettingsControl(LayoutMode mode) => Settings;
public int GetSettingsHashCode() => Settings.GetSettingsHashCode();
public override void SetSettings(XmlNode settings) => Settings.SetSettings(settings);
public override void Update(IInvalidator invalidator, LiveSplitState state, float width, float height, LayoutMode mode) {}
}
}
namespace LiveSplit.SupAutoSplit {
class MatchHandler {
private readonly Predicate<LiveSplitState> fEnabled;
private readonly Rect imageRange;
private readonly Func<Mat, double> fSim;
private readonly Predicate<double> fMatch;
private readonly Predicate<double> fMatchNeg;
public Action<TimerModel> Action { get; }
private bool ready = false;
public MatchHandler(UI.TemplateSettings settings) {
fEnabled = settings.EnableIf;
using (var timg = Cv2.ImRead(settings.ImagePath, ImreadModes.Unchanged)) {
imageRange = new Rect(settings.ImageOffset, timg.Size());
fSim = settings.MatchMethodFactory(timg);
}
var threshold = settings.MatchThreshold;
var thresholdNeg = settings.MatchThresholdNeg;
if (thresholdNeg <= 0) thresholdNeg = threshold;
if (settings.IsActionOnPosedge) {
fMatch = sim => sim <= threshold;
fMatchNeg = sim => sim >= thresholdNeg;
} else {
fMatch = sim => sim >= threshold;
fMatchNeg = sim => sim <= thresholdNeg;
}
Action = settings.MatchAction;
}
public bool Reset(LiveSplitState state) {
ready = false;
return fEnabled(state);
}
public bool Match(Mat frame) {
// Stopwatch sw = new Stopwatch();
var fimg = frame[imageRange];
var sim = fSim(fimg);
// sw.Stop();
// Debug.WriteLine($"#sim {sim} | {sw.ElapsedMilliseconds} ({sw.ElapsedTicks})");
if (ready) {
return fMatch(sim);
} else if (fMatchNeg(sim)) {
ready = true;
}
return false;
}
}
}

View file

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LiveSplit.Model;
using LiveSplit.UI.Components;
namespace LiveSplit.SupAutoSplit {
public class Factory : IComponentFactory {
public string ComponentName => "supAutoSplit";
public string Description => "An auto splitter using template matching";
public ComponentCategory Category => ComponentCategory.Control;
public string UpdateName => ComponentName;
public string XMLURL => "https://301.sup39.ml/LiveSplit/supAutoSplit.xml"; // TODO
public string UpdateURL => "https://301.sup39.ml/LiveSplit/supAutoSplit/update"; // TODO
public Version Version => Version.Parse("0.1.0");
public IComponent Create(LiveSplitState state) => new Component(state);
}
}

View file

@ -0,0 +1,124 @@
using LiveSplit.Model;
using OpenCvSharp;
using SupExtension;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace LiveSplit.SupAutoSplit {
using MatchAction = Action<TimerModel>;
using EnableIfFactory = Func<string, Predicate<LiveSplitState>>;
using MatchMethodFactory = Func<Mat, Func<Mat, double>>;
class MatchTemplate {
static public readonly ListItem<EnableIfFactory>[] EnableIfFactories = {
("Index", "Split Index in range", arg => {
var testers = arg.Split(',').Select(s => {
string[] ns = arg.Split(':');
if (ns.Length == 0 || ns.Length > 3) return null;
int start = 0, end = int.MaxValue, step = 1;
if (ns[0] != "" && !int.TryParse(ns[0], out start)) return null;
if (ns.Length == 1) return start < 0 ?
(Func<int, int, bool>)((idx, len) => idx == len + start) :
((idx, len) => idx==start);
if (ns[1] != "" && !int.TryParse(ns[1], out end)) return null;
if (ns.Length == 3 && ns[2] != "" && (!int.TryParse(ns[2], out step) || step==0)) return null;
return (idx, len) => {
int start1 = start<0 ? start+len : start;
int end1 = end<0 ? end+len : end;
return start1 <= idx && idx < end1 && (idx-start1)%step == 0;
};
}).Where(s => s!=null).ToList();
return state => testers.Any(f => f(state.CurrentSplitIndex, state.Run.Count));
}),
("Name", "Split Name matches", arg => {
try {
var re = new Regex(arg);
return state => state.CurrentSplit == null ? false : re.IsMatch(state.CurrentSplit.Name);
} catch {
// not regex
return state => state.CurrentSplit == null ? false : state.CurrentSplit.Name == arg;
}
}),
};
static public readonly string[] EnableIfItems = EnableIfFactories.Select(e => e.text).ToArray();
static public readonly ListItem<MatchAction>[] MatchActions = {
("Start", "Start", model => model.Start()),
("Split", "Split", model => model.Split()),
("Skip", "Skip Split", model => model.SkipSplit()),
("Undo", "Undo Split", model => model.UndoSplit()),
("Reset", "Reset", model => model.Reset()),
("Pause", "Pause", model => model.Pause()),
};
static public readonly string[] MatchActionItems = MatchActions.Select(e => e.text).ToArray();
static public readonly ListItem<bool>[] ActionTimings = {
("Posedge", "Posedge: First Match after unmatch", true),
("Negedge", "Negedge: First Unmatch after match", false),
};
static public readonly string[] ActionTimingItems = ActionTimings.Select(e => e.text).ToArray();
static public readonly ListItem<MatchMethodFactory>[] MatchMethodFactories = {
("COSINE", "1 - Cosine Similarity", timg => {
Mat[] bchs = timg.Split();
Mat mask = bchs[3];
Array.Resize(ref bchs, 3);
Mat[] bimg = bchs.Select(m => (Mat)m.BitwiseAnd(mask)).ToArray();
var b1 = Math.Sqrt(bimg.Select(m => m.Norm(NormTypes.L2SQR, mask)).Sum());
return frame => {
Mat[] fimg = frame.Split();
var f1 = Math.Sqrt(fimg.Select(m => m.Norm(NormTypes.L2SQR, mask)).Sum());
var fb = fimg.Zip(bimg, (mf, mb) => mf.Dot(mb)).Sum();
fimg.ForEach(m => m.Release());
return 1-fb/f1/b1;
};
}),
("SQDIFF_NORM", "Normalized Squared Difference", timg => {
Mat[] bimg = timg.Split();
Mat mask = bimg[3];
Array.Resize(ref bimg, 3);
var b1 = Math.Sqrt(bimg.Select(m => m.Norm(NormTypes.L2SQR, mask)).Sum());
return frame => {
Mat[] fimg = frame.Split();
var f1 = Math.Sqrt(fimg.Select(m => m.Norm(NormTypes.L2SQR, mask)).Sum());
var d2 = fimg.Zip(bimg, (mf, mb) => Cv2.Norm(mf, mb, NormTypes.L2SQR, mask)).Sum();
fimg.ForEach(m => m.Release());
return d2/f1/b1;
};
}),
("V128_BINARY", "V128 Binary Classification: MAX(R,G,B)<128 or not", timg => {
Mat bimg;
{
Mat[] chs = timg.Split();
if (chs.Length == 3) {
using (Mat v = RGB2V(chs))
bimg = v.LessThan(128);
} else {
bimg = chs[0].LessThan(128);
}
chs.ForEach(m => m.Release());
}
return frame => {
Mat[] chs = frame.Split();
using (Mat v = RGB2V(chs)) {
chs.ForEach(ch => ch.Release());
using (Mat fimg = v.LessThan(128))
return Cv2.Mean(fimg.NotEquals(bimg)).ToDouble()/255.0;
}
};
}),
};
static public readonly string[] MatchMethodItems = MatchMethodFactories.Select(e => e.text).ToArray();
static public Mat RGB2V(Mat[] chs) {
Mat m = new Mat();
Cv2.Max(chs[0], chs[1], m);
Cv2.Max(m, chs[2], m);
return m;
}
}
}

View file

@ -0,0 +1,145 @@
namespace LiveSplit.SupAutoSplit.UI {
partial class Settings {
/// <summary>
/// 必要なデザイナー変数です。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 使用中のリソースをすべてクリーンアップします。
/// </summary>
/// <param name="disposing">マネージド リソースを破棄する場合は true を指定し、その他の場合は false を指定します。</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region
/// <summary>
/// デザイナー サポートに必要なメソッドです。このメソッドの内容を
/// コード エディターで変更しないでください。
/// </summary>
private void InitializeComponent() {
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.label3 = new System.Windows.Forms.Label();
this.label1 = new System.Windows.Forms.Label();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.tlpTemplates = new System.Windows.Forms.TableLayoutPanel();
this.btnAddTemplate = new System.Windows.Forms.Button();
this.tableLayoutPanel1.SuspendLayout();
this.groupBox1.SuspendLayout();
this.tlpTemplates.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.AutoSize = true;
this.tableLayoutPanel1.ColumnCount = 2;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.label3, 1, 0);
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.groupBox1, 0, 1);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 3);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 2;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(450, 79);
this.tableLayoutPanel1.TabIndex = 0;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(54, 3);
this.label3.Margin = new System.Windows.Forms.Padding(3);
this.label3.Name = "label3";
this.label3.Padding = new System.Windows.Forms.Padding(3);
this.label3.Size = new System.Drawing.Size(65, 18);
this.label3.TabIndex = 2;
this.label3.Text = "SMS Any%";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(3, 3);
this.label1.Margin = new System.Windows.Forms.Padding(3);
this.label1.Name = "label1";
this.label1.Padding = new System.Windows.Forms.Padding(3);
this.label1.Size = new System.Drawing.Size(45, 18);
this.label1.TabIndex = 0;
this.label1.Text = "Profile:";
//
// groupBox1
//
this.groupBox1.AutoSize = true;
this.tableLayoutPanel1.SetColumnSpan(this.groupBox1, 2);
this.groupBox1.Controls.Add(this.tlpTemplates);
this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.groupBox1.Location = new System.Drawing.Point(3, 27);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(444, 49);
this.groupBox1.TabIndex = 3;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Templates";
//
// tlpTemplates
//
this.tlpTemplates.AutoSize = true;
this.tlpTemplates.ColumnCount = 1;
this.tlpTemplates.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tlpTemplates.Controls.Add(this.btnAddTemplate, 0, 0);
this.tlpTemplates.Dock = System.Windows.Forms.DockStyle.Fill;
this.tlpTemplates.Location = new System.Drawing.Point(3, 18);
this.tlpTemplates.Name = "tlpTemplates";
this.tlpTemplates.RowCount = 1;
this.tlpTemplates.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tlpTemplates.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tlpTemplates.Size = new System.Drawing.Size(438, 28);
this.tlpTemplates.TabIndex = 0;
//
// btnAddTemplate
//
this.btnAddTemplate.AutoSize = true;
this.btnAddTemplate.Location = new System.Drawing.Point(3, 3);
this.btnAddTemplate.Name = "btnAddTemplate";
this.btnAddTemplate.Size = new System.Drawing.Size(81, 22);
this.btnAddTemplate.TabIndex = 0;
this.btnAddTemplate.Text = "Add Template";
this.btnAddTemplate.UseVisualStyleBackColor = true;
this.btnAddTemplate.Click += new System.EventHandler(this.BtnAddTemplate_Click);
//
// Settings
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true;
this.Controls.Add(this.tableLayoutPanel1);
this.Name = "Settings";
this.Padding = new System.Windows.Forms.Padding(3);
this.Size = new System.Drawing.Size(456, 85);
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.tlpTemplates.ResumeLayout(false);
this.tlpTemplates.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.TableLayoutPanel tlpTemplates;
private System.Windows.Forms.Button btnAddTemplate;
}
}

View file

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using LiveSplit.UI.Components;
using LiveSplit.UI;
using SupExtension;
namespace LiveSplit.SupAutoSplit.UI {
public partial class Settings : UserControl {
public int CaptureDevice = 0; // TODO
public string WindowName = "supAutoSplit"; // TODO
public List<TemplateSettings> TemplateSettings { get; set; } = new List<TemplateSettings>();
public Settings() {
InitializeComponent();
}
public XmlNode GetSettings(XmlDocument document) {
var parent = document.CreateElement("Settings");
CreateSettingsNode(document, parent);
return parent;
}
public int GetSettingsHashCode() {
return CreateSettingsNode(null, null);
}
public int CreateSettingsNode(XmlDocument document, XmlElement parent) {
var hashCode =
SettingsHelper.CreateSetting(document, parent, "Version", "1.0.0");
// profile TODO
var profileRoot = document?.CreateElement("Profile", parent);
SettingsHelper.CreateSetting(document, profileRoot, "Name", "SMS Any%");
// templates
var templatesRoot = document?.CreateElement("Templates", profileRoot);
foreach (var ts in TemplateSettings) {
XmlElement templateParent = document?.CreateElement("Template", templatesRoot);
hashCode ^= ts.CreateSettingsNode(document, templateParent);
}
// return
return hashCode;
}
public void SetSettings(XmlNode settings) {
if (settings == null) return;
// profile
var profileRoot = settings["Profile"];
if (profileRoot == null) return;
// template
var templatesRoot = profileRoot["Templates"];
if (templatesRoot == null) return;
// reset RowStyles
var rs = tlpTemplates.RowStyles[0];
tlpTemplates.RowStyles.Clear();
tlpTemplates.RowStyles.Add(rs);
// remove UI
foreach (var ts in TemplateSettings) {
tlpTemplates.Controls.Remove(ts);
}
tlpTemplates.RowCount = 1;
// reinit TemplateSettings[]
TemplateSettings.Clear();
foreach (var templateParent in templatesRoot.ChildNodes) {
AddTemplateSettings((XmlElement)templateParent);
}
}
private void BtnAddTemplate_Click(object sender, EventArgs e) {
AddTemplateSettings(null);
}
private void AddTemplateSettings(XmlElement settings) {
var irow = tlpTemplates.RowCount;
tlpTemplates.RowCount = irow + 1;
tlpTemplates.RowStyles.Add(new RowStyle(SizeType.AutoSize));
var v = new TemplateSettings(settings, o => {
tlpTemplates.Controls.Remove(o);
tlpTemplates.RowCount = irow;
tlpTemplates.RowStyles.RemoveAt(irow);
TemplateSettings.Remove(o);
});
// tlpTemplates.RowStyles.Add(new RowStyle(SizeType.Absolute, v.Height));
// v.Width = tlpTemplates.Width;
tlpTemplates.Controls.Add(v, 0, irow);
TemplateSettings.Add(v);
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,439 @@
namespace LiveSplit.SupAutoSplit.UI {
partial class TemplateSettings {
/// <summary>
/// 必要なデザイナー変数です。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 使用中のリソースをすべてクリーンアップします。
/// </summary>
/// <param name="disposing">マネージド リソースを破棄する場合は true を指定し、その他の場合は false を指定します。</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region
/// <summary>
/// デザイナー サポートに必要なメソッドです。このメソッドの内容を
/// コード エディターで変更しないでください。
/// </summary>
private void InitializeComponent() {
this.ofdTemplate = new System.Windows.Forms.OpenFileDialog();
this.gpbRoot = new System.Windows.Forms.GroupBox();
this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel();
this.ttbThresholdNeg = new System.Windows.Forms.TextBox();
this.cbbEnableIf = new System.Windows.Forms.ComboBox();
this.cbbActionTiming = new System.Windows.Forms.ComboBox();
this.label1 = new System.Windows.Forms.Label();
this.ttbThreshold = new System.Windows.Forms.TextBox();
this.label10 = new System.Windows.Forms.Label();
this.label7 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.cbbAction = new System.Windows.Forms.ComboBox();
this.ttbName = new System.Windows.Forms.TextBox();
this.label9 = new System.Windows.Forms.Label();
this.cbbMethod = new System.Windows.Forms.ComboBox();
this.flowLayoutPanel3 = new System.Windows.Forms.FlowLayoutPanel();
this.label12 = new System.Windows.Forms.Label();
this.ttbOffsetX = new System.Windows.Forms.TextBox();
this.label11 = new System.Windows.Forms.Label();
this.ttbOffsetY = new System.Windows.Forms.TextBox();
this.label13 = new System.Windows.Forms.Label();
this.btnImage = new System.Windows.Forms.Button();
this.ttbEnableIfArg = new System.Windows.Forms.TextBox();
this.btnRemove = new System.Windows.Forms.Button();
this.label3 = new System.Windows.Forms.Label();
this.label6 = new System.Windows.Forms.Label();
this.gpbRoot.SuspendLayout();
this.tableLayoutPanel3.SuspendLayout();
this.flowLayoutPanel3.SuspendLayout();
this.SuspendLayout();
//
// ofdTemplate
//
this.ofdTemplate.Filter = "Image Files|*.bmp;*.pbm;*.pgm;*.ppm;*.sr;*.ras;*.jpeg;*.jpg;*.jpe;*.jp2;*.tiff;*." +
"tif;*.png";
//
// gpbRoot
//
this.gpbRoot.AutoSize = true;
this.gpbRoot.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.gpbRoot.Controls.Add(this.tableLayoutPanel3);
this.gpbRoot.DataBindings.Add(new System.Windows.Forms.Binding("Text", this, "TemplateTitle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.gpbRoot.Dock = System.Windows.Forms.DockStyle.Fill;
this.gpbRoot.Location = new System.Drawing.Point(0, 0);
this.gpbRoot.Name = "gpbRoot";
this.gpbRoot.Size = new System.Drawing.Size(434, 314);
this.gpbRoot.TabIndex = 16;
this.gpbRoot.TabStop = false;
this.gpbRoot.Text = "Template: ";
//
// tableLayoutPanel3
//
this.tableLayoutPanel3.ColumnCount = 3;
this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 126F));
this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel3.Controls.Add(this.ttbThresholdNeg, 1, 8);
this.tableLayoutPanel3.Controls.Add(this.cbbEnableIf, 1, 1);
this.tableLayoutPanel3.Controls.Add(this.cbbActionTiming, 1, 3);
this.tableLayoutPanel3.Controls.Add(this.label1, 0, 2);
this.tableLayoutPanel3.Controls.Add(this.ttbThreshold, 1, 7);
this.tableLayoutPanel3.Controls.Add(this.label10, 0, 7);
this.tableLayoutPanel3.Controls.Add(this.label7, 0, 0);
this.tableLayoutPanel3.Controls.Add(this.label5, 0, 5);
this.tableLayoutPanel3.Controls.Add(this.label4, 0, 4);
this.tableLayoutPanel3.Controls.Add(this.label2, 0, 1);
this.tableLayoutPanel3.Controls.Add(this.cbbAction, 1, 2);
this.tableLayoutPanel3.Controls.Add(this.ttbName, 1, 0);
this.tableLayoutPanel3.Controls.Add(this.label9, 0, 6);
this.tableLayoutPanel3.Controls.Add(this.cbbMethod, 1, 6);
this.tableLayoutPanel3.Controls.Add(this.flowLayoutPanel3, 1, 5);
this.tableLayoutPanel3.Controls.Add(this.btnImage, 1, 4);
this.tableLayoutPanel3.Controls.Add(this.ttbEnableIfArg, 2, 1);
this.tableLayoutPanel3.Controls.Add(this.btnRemove, 2, 9);
this.tableLayoutPanel3.Controls.Add(this.label3, 0, 3);
this.tableLayoutPanel3.Controls.Add(this.label6, 0, 8);
this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel3.Location = new System.Drawing.Point(3, 18);
this.tableLayoutPanel3.Name = "tableLayoutPanel3";
this.tableLayoutPanel3.RowCount = 10;
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.Size = new System.Drawing.Size(428, 293);
this.tableLayoutPanel3.TabIndex = 2;
//
// ttbThresholdNeg
//
this.tableLayoutPanel3.SetColumnSpan(this.ttbThresholdNeg, 2);
this.ttbThresholdNeg.DataBindings.Add(new System.Windows.Forms.Binding("Text", this, "MatchThresholdNegStr", true));
this.ttbThresholdNeg.Dock = System.Windows.Forms.DockStyle.Fill;
this.ttbThresholdNeg.Location = new System.Drawing.Point(97, 239);
this.ttbThresholdNeg.Name = "ttbThresholdNeg";
this.ttbThresholdNeg.Size = new System.Drawing.Size(328, 22);
this.ttbThresholdNeg.TabIndex = 28;
//
// cbbEnableIf
//
this.cbbEnableIf.Dock = System.Windows.Forms.DockStyle.Fill;
this.cbbEnableIf.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cbbEnableIf.FormattingEnabled = true;
this.cbbEnableIf.Location = new System.Drawing.Point(97, 31);
this.cbbEnableIf.Name = "cbbEnableIf";
this.cbbEnableIf.Size = new System.Drawing.Size(120, 20);
this.cbbEnableIf.TabIndex = 26;
//
// cbbActionTiming
//
this.tableLayoutPanel3.SetColumnSpan(this.cbbActionTiming, 2);
this.cbbActionTiming.Dock = System.Windows.Forms.DockStyle.Fill;
this.cbbActionTiming.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cbbActionTiming.FormattingEnabled = true;
this.cbbActionTiming.Location = new System.Drawing.Point(97, 85);
this.cbbActionTiming.Name = "cbbActionTiming";
this.cbbActionTiming.Size = new System.Drawing.Size(328, 20);
this.cbbActionTiming.TabIndex = 25;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(3, 59);
this.label1.Margin = new System.Windows.Forms.Padding(3);
this.label1.Name = "label1";
this.label1.Padding = new System.Windows.Forms.Padding(3);
this.label1.Size = new System.Drawing.Size(45, 18);
this.label1.TabIndex = 22;
this.label1.Text = "Action:";
//
// ttbThreshold
//
this.tableLayoutPanel3.SetColumnSpan(this.ttbThreshold, 2);
this.ttbThreshold.DataBindings.Add(new System.Windows.Forms.Binding("Text", this, "MatchThresholdStr", true));
this.ttbThreshold.Dock = System.Windows.Forms.DockStyle.Fill;
this.ttbThreshold.Location = new System.Drawing.Point(97, 211);
this.ttbThreshold.Name = "ttbThreshold";
this.ttbThreshold.Size = new System.Drawing.Size(328, 22);
this.ttbThreshold.TabIndex = 8;
//
// label10
//
this.label10.AutoSize = true;
this.label10.Location = new System.Drawing.Point(3, 211);
this.label10.Margin = new System.Windows.Forms.Padding(3);
this.label10.Name = "label10";
this.label10.Padding = new System.Windows.Forms.Padding(3);
this.label10.Size = new System.Drawing.Size(87, 18);
this.label10.TabIndex = 17;
this.label10.Text = "Max Difference:";
//
// label7
//
this.label7.AutoSize = true;
this.label7.Location = new System.Drawing.Point(3, 3);
this.label7.Margin = new System.Windows.Forms.Padding(3);
this.label7.Name = "label7";
this.label7.Padding = new System.Windows.Forms.Padding(3);
this.label7.Size = new System.Drawing.Size(41, 18);
this.label7.TabIndex = 14;
this.label7.Text = "Name:";
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(3, 157);
this.label5.Margin = new System.Windows.Forms.Padding(3);
this.label5.Name = "label5";
this.label5.Padding = new System.Windows.Forms.Padding(3);
this.label5.Size = new System.Drawing.Size(88, 18);
this.label5.TabIndex = 6;
this.label5.Text = "Template Offset:";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(3, 111);
this.label4.Margin = new System.Windows.Forms.Padding(3);
this.label4.Name = "label4";
this.label4.Padding = new System.Windows.Forms.Padding(3);
this.label4.Size = new System.Drawing.Size(57, 18);
this.label4.TabIndex = 5;
this.label4.Text = "Template:";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(3, 31);
this.label2.Margin = new System.Windows.Forms.Padding(3);
this.label2.Name = "label2";
this.label2.Padding = new System.Windows.Forms.Padding(3);
this.label2.Size = new System.Drawing.Size(57, 18);
this.label2.TabIndex = 4;
this.label2.Text = "Enable If:";
//
// cbbAction
//
this.tableLayoutPanel3.SetColumnSpan(this.cbbAction, 2);
this.cbbAction.Dock = System.Windows.Forms.DockStyle.Fill;
this.cbbAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cbbAction.FormattingEnabled = true;
this.cbbAction.Location = new System.Drawing.Point(97, 59);
this.cbbAction.Name = "cbbAction";
this.cbbAction.Size = new System.Drawing.Size(328, 20);
this.cbbAction.TabIndex = 3;
//
// ttbName
//
this.tableLayoutPanel3.SetColumnSpan(this.ttbName, 2);
this.ttbName.DataBindings.Add(new System.Windows.Forms.Binding("Text", this, "TemplateName", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
this.ttbName.Dock = System.Windows.Forms.DockStyle.Fill;
this.ttbName.Location = new System.Drawing.Point(97, 3);
this.ttbName.Name = "ttbName";
this.ttbName.Size = new System.Drawing.Size(328, 22);
this.ttbName.TabIndex = 1;
//
// label9
//
this.label9.AutoSize = true;
this.label9.Location = new System.Drawing.Point(3, 185);
this.label9.Margin = new System.Windows.Forms.Padding(3);
this.label9.Name = "label9";
this.label9.Padding = new System.Windows.Forms.Padding(3);
this.label9.Size = new System.Drawing.Size(82, 18);
this.label9.TabIndex = 16;
this.label9.Text = "Match Method:";
//
// cbbMethod
//
this.tableLayoutPanel3.SetColumnSpan(this.cbbMethod, 2);
this.cbbMethod.Dock = System.Windows.Forms.DockStyle.Fill;
this.cbbMethod.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cbbMethod.FormattingEnabled = true;
this.cbbMethod.Location = new System.Drawing.Point(97, 185);
this.cbbMethod.Name = "cbbMethod";
this.cbbMethod.Size = new System.Drawing.Size(328, 20);
this.cbbMethod.TabIndex = 7;
//
// flowLayoutPanel3
//
this.flowLayoutPanel3.AutoSize = true;
this.tableLayoutPanel3.SetColumnSpan(this.flowLayoutPanel3, 2);
this.flowLayoutPanel3.Controls.Add(this.label12);
this.flowLayoutPanel3.Controls.Add(this.ttbOffsetX);
this.flowLayoutPanel3.Controls.Add(this.label11);
this.flowLayoutPanel3.Controls.Add(this.ttbOffsetY);
this.flowLayoutPanel3.Controls.Add(this.label13);
this.flowLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel3.Location = new System.Drawing.Point(94, 154);
this.flowLayoutPanel3.Margin = new System.Windows.Forms.Padding(0);
this.flowLayoutPanel3.Name = "flowLayoutPanel3";
this.flowLayoutPanel3.Size = new System.Drawing.Size(334, 28);
this.flowLayoutPanel3.TabIndex = 18;
//
// label12
//
this.label12.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.label12.AutoSize = true;
this.label12.Location = new System.Drawing.Point(3, 8);
this.label12.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3);
this.label12.Name = "label12";
this.label12.Size = new System.Drawing.Size(9, 12);
this.label12.TabIndex = 19;
this.label12.Text = "(";
//
// ttbOffsetX
//
this.ttbOffsetX.DataBindings.Add(new System.Windows.Forms.Binding("Text", this, "ImageOffsetXStr", true));
this.ttbOffsetX.Location = new System.Drawing.Point(15, 3);
this.ttbOffsetX.Name = "ttbOffsetX";
this.ttbOffsetX.Size = new System.Drawing.Size(48, 22);
this.ttbOffsetX.TabIndex = 5;
//
// label11
//
this.label11.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.label11.AutoSize = true;
this.label11.Location = new System.Drawing.Point(66, 13);
this.label11.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
this.label11.Name = "label11";
this.label11.Size = new System.Drawing.Size(11, 12);
this.label11.TabIndex = 17;
this.label11.Text = ", ";
//
// ttbOffsetY
//
this.ttbOffsetY.DataBindings.Add(new System.Windows.Forms.Binding("Text", this, "ImageOffsetYStr", true));
this.ttbOffsetY.Location = new System.Drawing.Point(80, 3);
this.ttbOffsetY.Name = "ttbOffsetY";
this.ttbOffsetY.Size = new System.Drawing.Size(48, 22);
this.ttbOffsetY.TabIndex = 6;
//
// label13
//
this.label13.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.label13.AutoSize = true;
this.label13.Location = new System.Drawing.Point(131, 8);
this.label13.Margin = new System.Windows.Forms.Padding(0, 3, 3, 3);
this.label13.Name = "label13";
this.label13.Size = new System.Drawing.Size(9, 12);
this.label13.TabIndex = 20;
this.label13.Text = ")";
//
// btnImage
//
this.btnImage.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.tableLayoutPanel3.SetColumnSpan(this.btnImage, 2);
this.btnImage.Location = new System.Drawing.Point(97, 111);
this.btnImage.Name = "btnImage";
this.btnImage.Size = new System.Drawing.Size(40, 40);
this.btnImage.TabIndex = 4;
this.btnImage.UseVisualStyleBackColor = true;
this.btnImage.Click += new System.EventHandler(this.BtnTemplate_Click);
//
// ttbEnableIfArg
//
this.ttbEnableIfArg.DataBindings.Add(new System.Windows.Forms.Binding("Text", this, "EnableIfArg", true));
this.ttbEnableIfArg.Dock = System.Windows.Forms.DockStyle.Fill;
this.ttbEnableIfArg.Location = new System.Drawing.Point(223, 31);
this.ttbEnableIfArg.Name = "ttbEnableIfArg";
this.ttbEnableIfArg.Size = new System.Drawing.Size(202, 22);
this.ttbEnableIfArg.TabIndex = 2;
//
// btnRemove
//
this.btnRemove.AutoSize = true;
this.btnRemove.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.btnRemove.Dock = System.Windows.Forms.DockStyle.Right;
this.btnRemove.Location = new System.Drawing.Point(325, 267);
this.btnRemove.Name = "btnRemove";
this.btnRemove.Size = new System.Drawing.Size(100, 23);
this.btnRemove.TabIndex = 23;
this.btnRemove.TabStop = false;
this.btnRemove.Text = "Remove Template";
this.btnRemove.UseVisualStyleBackColor = true;
this.btnRemove.Click += new System.EventHandler(this.BtnRemove_Click);
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(3, 85);
this.label3.Margin = new System.Windows.Forms.Padding(3);
this.label3.Name = "label3";
this.label3.Padding = new System.Windows.Forms.Padding(3);
this.label3.Size = new System.Drawing.Size(82, 18);
this.label3.TabIndex = 24;
this.label3.Text = "Action Timing:";
//
// label6
//
this.label6.AutoSize = true;
this.label6.Location = new System.Drawing.Point(3, 239);
this.label6.Margin = new System.Windows.Forms.Padding(3);
this.label6.Name = "label6";
this.label6.Padding = new System.Windows.Forms.Padding(3);
this.label6.Size = new System.Drawing.Size(88, 18);
this.label6.TabIndex = 27;
this.label6.Text = "Ready Min Diff:";
//
// TemplateSettings
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.gpbRoot);
this.Name = "TemplateSettings";
this.Size = new System.Drawing.Size(434, 314);
this.gpbRoot.ResumeLayout(false);
this.tableLayoutPanel3.ResumeLayout(false);
this.tableLayoutPanel3.PerformLayout();
this.flowLayoutPanel3.ResumeLayout(false);
this.flowLayoutPanel3.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.OpenFileDialog ofdTemplate;
private System.Windows.Forms.GroupBox gpbRoot;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3;
private System.Windows.Forms.TextBox ttbEnableIfArg;
private System.Windows.Forms.TextBox ttbThreshold;
private System.Windows.Forms.Label label10;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox ttbName;
private System.Windows.Forms.Label label9;
private System.Windows.Forms.ComboBox cbbMethod;
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel3;
private System.Windows.Forms.Label label12;
private System.Windows.Forms.TextBox ttbOffsetX;
private System.Windows.Forms.Label label11;
private System.Windows.Forms.TextBox ttbOffsetY;
private System.Windows.Forms.Label label13;
private System.Windows.Forms.Button btnImage;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.ComboBox cbbAction;
private System.Windows.Forms.Button btnRemove;
private System.Windows.Forms.ComboBox cbbActionTiming;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.ComboBox cbbEnableIf;
private System.Windows.Forms.TextBox ttbThresholdNeg;
private System.Windows.Forms.Label label6;
}
}

View file

@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using LiveSplit.UI.Components;
using SupExtension;
using LiveSplit.Model;
using LiveSplit.UI;
using System.Xml;
using OpenCvSharp;
namespace LiveSplit.SupAutoSplit.UI {
using MatchAction = Action<TimerModel>;
using EnableIfFactory = Func<string, Predicate<LiveSplitState>>;
using MatchMethodFactory = Func<Mat, Func<Mat, double>>;
public partial class TemplateSettings : UserControl {
internal Action<TemplateSettings> RemoveHandler;
#region Parameters
public string TemplateName { get; set; }
public string TemplateTitle => $"Template: {TemplateName}";
public int EnableIfISel { get; set; } = 0;
public string EnableIfArg { get; set; }
private ListItem<EnableIfFactory> EnableIfSel => MatchTemplate.EnableIfFactories[EnableIfISel];
private bool IsActionStart => matchActionISel == 0; // start
public Predicate<LiveSplitState> EnableIf => IsActionStart ? (state => state.CurrentSplitIndex == -1): EnableIfSel.value(EnableIfArg);
private int matchActionISel;
public int MatchActionISel {
get => matchActionISel;
set {
matchActionISel = value;
// Start => Disable Arg
cbbEnableIf.Enabled = ttbEnableIfArg.Enabled = !IsActionStart;
}
}
private ListItem<MatchAction> MatchActionSel => MatchTemplate.MatchActions[MatchActionISel];
public MatchAction MatchAction => MatchActionSel.value;
public int ActionTimingISel { get; set; } = 1;
private ListItem<bool> ActionTimingSel => MatchTemplate.ActionTimings[ActionTimingISel];
public bool IsActionOnPosedge => ActionTimingSel.value;
private string _imagePath;
public string ImagePath {
get => _imagePath;
set {
try {
if (value != "") {
var img = Image.FromFile(value);
var h0 = btnImage.Height;
var h = Math.Max(11, Math.Min(66, img.Height));
btnImage.Height = h;
btnImage.Width = h * img.Width / img.Height;
btnImage.BackgroundImage = img;
// adjust this.Height
Height += h - h0;
}
_imagePath = value;
} catch {
_imagePath = "";
}
}
}
private int ImageOffsetX;
private int ImageOffsetY;
public string ImageOffsetXStr {
get => ImageOffsetX.ToString();
set => int.TryParse(value, out ImageOffsetX);
}
public string ImageOffsetYStr {
get => ImageOffsetY.ToString();
set => int.TryParse(value, out ImageOffsetY);
}
public OpenCvSharp.Point ImageOffset => new OpenCvSharp.Point(ImageOffsetX, ImageOffsetY);
public int MatchMethodISel { get; set; }
private ListItem<MatchMethodFactory> MatchMethodSel => MatchTemplate.MatchMethodFactories[MatchMethodISel];
public MatchMethodFactory MatchMethodFactory => MatchMethodSel.value;
private double matchThreshold;
public double MatchThreshold => matchThreshold;
public string MatchThresholdStr {
get => MatchThreshold.ToString();
set => double.TryParse(value, out matchThreshold);
}
private double matchThresholdNeg;
public double MatchThresholdNeg => matchThresholdNeg;
public string MatchThresholdNegStr {
get => MatchThresholdNeg.ToString();
set => double.TryParse(value, out matchThresholdNeg);
}
#endregion
public TemplateSettings(XmlElement settings, Action<TemplateSettings> removeHandler) {
RemoveHandler = removeHandler;
InitializeComponent();
SetSettings(settings);
cbbEnableIf.DataSource = MatchTemplate.EnableIfItems;
cbbEnableIf.DataBindings.Add("SelectedIndex", this, "EnableIfISel", false, DataSourceUpdateMode.OnPropertyChanged);
cbbAction.DataSource = MatchTemplate.MatchActionItems;
cbbAction.DataBindings.Add("SelectedIndex", this, "MatchActionISel", false, DataSourceUpdateMode.OnPropertyChanged);
cbbActionTiming.DataSource = MatchTemplate.ActionTimingItems;
cbbActionTiming.DataBindings.Add("SelectedIndex", this, "ActionTimingISel", false, DataSourceUpdateMode.OnPropertyChanged);
cbbMethod.DataSource = MatchTemplate.MatchMethodItems;
cbbMethod.DataBindings.Add("SelectedIndex", this, "MatchMethodISel", false, DataSourceUpdateMode.OnPropertyChanged);
}
private void BtnTemplate_Click(object sender, EventArgs e) {
if (ofdTemplate.ShowDialog() == DialogResult.OK)
ImagePath = ofdTemplate.FileName;
}
private void BtnRemove_Click(object sender, EventArgs e) {
RemoveHandler(this);
}
internal int CreateSettingsNode(XmlDocument document, XmlElement parent) {
var hashCode =
SettingsHelper.CreateSetting(document, parent, "Name", TemplateName) ^
SettingsHelper.CreateSetting(document, parent, "EnableIf", EnableIfSel.key) ^
SettingsHelper.CreateSetting(document, parent, "EnableIfArg", EnableIfArg) ^
SettingsHelper.CreateSetting(document, parent, "Action", MatchActionSel.key) ^
SettingsHelper.CreateSetting(document, parent, "ActionOn", ActionTimingSel.key) ^
SettingsHelper.CreateSetting(document, parent, "Image", ImagePath) ^
SettingsHelper.CreateSetting(document, parent, "OffsetX", ImageOffsetX) ^
SettingsHelper.CreateSetting(document, parent, "OffsetY", ImageOffsetY) ^
SettingsHelper.CreateSetting(document, parent, "Method", MatchMethodSel.key) ^
SettingsHelper.CreateSetting(document, parent, "Threshold", MatchThreshold);
SettingsHelper.CreateSetting(document, parent, "ThresholdNeg", MatchThresholdNeg);
return hashCode;
}
private void SetSettings(XmlElement settings) {
TemplateName = SettingsHelper.ParseString(settings?["Name"], "");
EnableIfISel = SupSettingsHelper.ParseListISel(settings?["EnableIf"], MatchTemplate.EnableIfFactories, 0);
EnableIfArg = SettingsHelper.ParseString(settings?["EnableIfArg"], "");
MatchActionISel = SupSettingsHelper.ParseListISel(settings?["Action"], MatchTemplate.MatchActions, 1);
ActionTimingISel = SupSettingsHelper.ParseListISel(settings?["ActionOn"], MatchTemplate.ActionTimings, 0);
ImagePath = SettingsHelper.ParseString(settings?["Image"], "");
ImageOffsetX = SettingsHelper.ParseInt(settings?["OffsetX"], 0);
ImageOffsetY = SettingsHelper.ParseInt(settings?["OffsetY"], 0);
MatchMethodISel = SupSettingsHelper.ParseListISel(settings?["Method"], MatchTemplate.MatchMethodFactories, 0);
matchThreshold = SettingsHelper.ParseDouble(settings?["Threshold"], 0);
matchThresholdNeg = SettingsHelper.ParseDouble(settings?["ThresholdNeg"], 0);
}
}
}
namespace SupExtension {
public struct ListItem<V> {
public string key;
public string text;
public V value;
public static implicit operator ListItem<V>((string, V) arg) =>
new ListItem<V>() { key = arg.Item1, text = arg.Item1, value = arg.Item2 };
public static implicit operator ListItem<V>((string, string, V) arg) =>
new ListItem<V>() { key = arg.Item1, text = arg.Item2, value = arg.Item3 };
}
public static class SupArray {
public static int FindIndex<T>(this T[] self, Predicate<T> match) =>
Array.FindIndex(self, match);
}
static class SupSettingsHelper {
internal static int ParseListISel<T>(XmlElement element, ListItem<T>[] items, int defaultValue=-1) {
var key = SettingsHelper.ParseString(element);
if (key == null) return defaultValue;
var index = Array.FindIndex(items, o => o.key == key);
return index == -1 ? defaultValue : index;
}
}
}

View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="ofdTemplate.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View file

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace SupExtension {
public static class SupEnum {
public static E[] Enumerate<E>() => (E[])Enum.GetValues(typeof(E));
public static IEnumerable<string> EnumerateDescription<E>() =>
Enumerate<E>().Select(e => e.ToDescription());
public static string[] DescriptionList<E>() =>
Enumerate<E>().Select(e => e.ToDescription()).ToArray();
public static string ToDescription<E>(this E val) {
DescriptionAttribute[] attributes = (DescriptionAttribute[])val
.GetType()
.GetField(val.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : string.Empty;
}
}
public static class SupEnumerable {
public static void ForEach<T>(this IEnumerable<T> itr, Action<T> action) {
foreach (T item in itr) {
action(item);
}
}
}
public static class SupXml {
public static XmlElement CreateElement(this XmlDocument document, string name, XmlElement parent) {
var elm = document.CreateElement(name);
parent?.AppendChild(elm);
return elm;
}
}
public static class SupParse {
public static int? Int(string s) => int.TryParse(s, out var result) ? (int?)result : null;
}
}

11
supAutoSplit/app.config Normal file
View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="OpenCvSharp4" version="4.5.3.20210817" targetFramework="net461" />
<package id="OpenCvSharp4.runtime.win" version="4.5.3.20210817" targetFramework="net461" />
<package id="OpenCvSharp4.Windows" version="4.5.3.20210817" targetFramework="net461" />
<package id="OpenCvSharp4.WpfExtensions" version="4.5.3.20210817" targetFramework="net461" />
<package id="System.Buffers" version="4.5.1" targetFramework="net461" />
<package id="System.Drawing.Common" version="5.0.2" targetFramework="net461" />
<package id="System.Memory" version="4.5.4" targetFramework="net461" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net461" />
<package id="System.Runtime.CompilerServices.Unsafe" version="5.0.0" targetFramework="net461" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net461" />
</packages>

View file

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\OpenCvSharp4.runtime.win.4.5.3.20210817\build\net\OpenCvSharp4.runtime.win.props" Condition="Exists('..\packages\OpenCvSharp4.runtime.win.4.5.3.20210817\build\net\OpenCvSharp4.runtime.win.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C1B6A79E-088A-411C-B546-4D72447B4DAA}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>LiveSplit</RootNamespace>
<AssemblyName>Livesplit.supAutoSplit</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="LiveSplit.Core">
<HintPath>..\..\..\..\..\..\Software\LiveSplit\LiveSplit.Core.dll</HintPath>
</Reference>
<Reference Include="OpenCvSharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6adad1e807fea099, processorArchitecture=MSIL">
<HintPath>..\packages\OpenCvSharp4.4.5.3.20210817\lib\net461\OpenCvSharp.dll</HintPath>
</Reference>
<Reference Include="OpenCvSharp.Extensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6adad1e807fea099, processorArchitecture=MSIL">
<HintPath>..\packages\OpenCvSharp4.4.5.3.20210817\lib\net461\OpenCvSharp.Extensions.dll</HintPath>
</Reference>
<Reference Include="OpenCvSharp.WpfExtensions, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\OpenCvSharp4.WpfExtensions.4.5.3.20210817\lib\net461\OpenCvSharp.WpfExtensions.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Drawing.Common, Version=4.0.0.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Drawing.Common.5.0.2\lib\net461\System.Drawing.Common.dll</HintPath>
</Reference>
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="UpdateManager">
<HintPath>..\..\..\..\..\..\Software\LiveSplit\UpdateManager.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="SupAutoSplit\MatchTemplate.cs" />
<Compile Include="SupExtension.cs" />
<Compile Include="SupAutoSplit\Component.cs" />
<Compile Include="SupAutoSplit\Factory.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SupAutoSplit\UI\Settings.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="SupAutoSplit\UI\Settings.Designer.cs">
<DependentUpon>Settings.cs</DependentUpon>
</Compile>
<Compile Include="SupAutoSplit\UI\TemplateSettings.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="SupAutoSplit\UI\TemplateSettings.Designer.cs">
<DependentUpon>TemplateSettings.cs</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
<None Include="Properties\DataSources\LiveSplit.SupAutoSplit.UI.TemplateSettings.datasource" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="SupAutoSplit\UI\Settings.resx">
<DependentUpon>Settings.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="SupAutoSplit\UI\TemplateSettings.resx">
<DependentUpon>TemplateSettings.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>copy $(TargetDir)$(TargetFileName) C:\Software\LiveSplit\Components\</PostBuildEvent>
</PropertyGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>このプロジェクトは、このコンピューター上にない NuGet パッケージを参照しています。それらのパッケージをダウンロードするには、[NuGet パッケージの復元] を使用します。詳細については、http://go.microsoft.com/fwlink/?LinkID=322105 を参照してください。見つからないファイルは {0} です。</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\OpenCvSharp4.runtime.win.4.5.3.20210817\build\net\OpenCvSharp4.runtime.win.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\OpenCvSharp4.runtime.win.4.5.3.20210817\build\net\OpenCvSharp4.runtime.win.props'))" />
</Target>
</Project>