init test

This commit is contained in:
DismissedLight
2025-01-21 17:31:09 +08:00
parent 7f7d28825e
commit d4aae4508a
35 changed files with 806 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
<Project Sdk="MSTest.Sdk/3.6.4">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UseVSTest>true</UseVSTest>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BetterGenshinImpact.CombatScript\BetterGenshinImpact.CombatScript.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,45 @@
namespace BetterGenshinImpact.CombatScript.Test;
[TestClass]
public sealed class ScriptEmitTest
{
[TestMethod]
public void TestEmit()
{
ScriptUnit scriptUnit = new(
[
new CommentSymbol("测试注释"),
new LineBreakTriviaSymbol(),
new AvatarInstructionListSymbol(new("钟离"), [new SpaceTriviaSymbol()], new(
[
new WalkSymbol(WalkDirection.Backward, [new DoubleSymbol(0.1)], [new CommaTriviaSymbol()]),
new SkillSymbol(true, [new HoldSymbol()], [new CommaTriviaSymbol()]),
new WaitSymbol([new DoubleSymbol(0.3)], [new CommaTriviaSymbol()]),
new WalkSymbol(WalkDirection.Forward, [new DoubleSymbol(0.1)], []),
])),
new LineBreakTriviaSymbol(),
new AvatarInstructionListSymbol(new("芙宁娜"), [new SpaceTriviaSymbol()], new(
[
new SkillSymbol(true, [new CommaTriviaSymbol()]),
new BurstSymbol(true, [])
])),
new LineBreakTriviaSymbol(),
new AvatarInstructionListSymbol(new("行秋"), [new SpaceTriviaSymbol()], new(
[
new SkillSymbol(true, [new CommaTriviaSymbol()]),
new BurstSymbol(true, [new CommaTriviaSymbol()]),
new SkillSymbol(true, []),
])),
]);
Console.WriteLine(scriptUnit.Emit(new DefaultSymbolEmitter()));
}
[TestMethod]
public void Test()
{
ReadOnlySpan<char> raw = "ABCDEF;GHIJKL\r\nMNOPQR\nSTUVWX\rYZ\r\n";
SymbolParser parser = new();
ScriptUnit scriptUnit = parser.Parse(raw);
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Immutable;
namespace BetterGenshinImpact.CombatScript;
public class AttackSymbol : InstructionSymbol, IInstructionSymbolHasDuration
{
public AttackSymbol(ImmutableArray<IParameterSymbol> parameterList, ImmutableArray<TriviaSymbol> trivia)
: base("attack", parameterList, trivia)
{
InstructionThrowHelper.ThrowIfParameterListIsDefault(parameterList);
InstructionThrowHelper.ThrowIfParameterListCountNotCorrect(parameterList, [0, 1]);
if (parameterList.Length is 1)
{
InstructionThrowHelper.ThrowIfParameterAtIndexIsNot(parameterList, 0, out DoubleSymbol doubleSymbol);
HasDuration = true;
Duration = TimeSpan.FromSeconds(doubleSymbol.Value);
}
}
public AttackSymbol(ImmutableArray<TriviaSymbol> trivia)
: base("attack", trivia)
{
}
public bool HasDuration { get; }
public TimeSpan Duration { get; }
}

View File

@@ -0,0 +1,24 @@
using System.Collections.Immutable;
namespace BetterGenshinImpact.CombatScript;
public sealed class AvatarInstructionListSymbol : BaseSymbol
{
public AvatarInstructionListSymbol(AvatarSymbol avatar, ImmutableArray<TriviaSymbol> triviaList, InstructionListSymbol instructionList)
{
Avatar = avatar;
TriviaList = triviaList;
InstructionList = instructionList;
}
public AvatarSymbol Avatar { get; }
public ImmutableArray<TriviaSymbol> TriviaList { get; }
public InstructionListSymbol InstructionList { get; }
public override void Emit(ISymbolEmitter emitter)
{
emitter.Append(Avatar).Append(TriviaList).Append(InstructionList);
}
}

View File

@@ -0,0 +1,16 @@
namespace BetterGenshinImpact.CombatScript;
public sealed class AvatarSymbol : BaseSymbol
{
public AvatarSymbol(string name)
{
Name = name;
}
public string Name { get; }
public override void Emit(ISymbolEmitter emitter)
{
emitter.Append(Name);
}
}

View File

@@ -0,0 +1,6 @@
namespace BetterGenshinImpact.CombatScript;
public abstract class BaseSymbol : ISymbol
{
public abstract void Emit(ISymbolEmitter emitter);
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,25 @@
using System.Collections.Immutable;
namespace BetterGenshinImpact.CombatScript;
public class BurstSymbol : InstructionSymbol, IInstructionSymbolHasAlias
{
public BurstSymbol(bool isAlias, ImmutableArray<IParameterSymbol> parameterList, ImmutableArray<TriviaSymbol> trivia)
: base("burst", parameterList, trivia)
{
InstructionThrowHelper.ThrowIfParameterListIsDefault(parameterList);
InstructionThrowHelper.ThrowIfParameterListCountNotCorrect(parameterList, [0]);
IsAlias = isAlias;
}
public BurstSymbol(bool isAlias, ImmutableArray<TriviaSymbol> trivia)
: base("burst", trivia)
{
IsAlias = isAlias;
}
public string AliasName { get; } = "q";
public bool IsAlias { get; }
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Immutable;
namespace BetterGenshinImpact.CombatScript;
public class ChargeSymbol : InstructionSymbol, IInstructionSymbolHasDuration
{
public ChargeSymbol(ImmutableArray<IParameterSymbol> parameterList, ImmutableArray<TriviaSymbol> trivia)
: base("charge", parameterList, trivia)
{
InstructionThrowHelper.ThrowIfParameterListIsDefault(parameterList);
InstructionThrowHelper.ThrowIfParameterListCountNotCorrect(parameterList, [0, 1]);
if (parameterList.Length is 1)
{
InstructionThrowHelper.ThrowIfParameterAtIndexIsNot(parameterList, 0, out DoubleSymbol doubleSymbol);
HasDuration = true;
Duration = TimeSpan.FromSeconds(doubleSymbol.Value);
}
}
public ChargeSymbol(ImmutableArray<TriviaSymbol> trivia)
: base("charge", trivia)
{
}
public bool HasDuration { get; }
public TimeSpan Duration { get; }
}

View File

@@ -0,0 +1,17 @@
namespace BetterGenshinImpact.CombatScript;
public sealed class CommaTriviaSymbol : TriviaSymbol
{
public override void Emit(ISymbolEmitter emitter)
{
emitter.Append(',');
}
}
public sealed class SemicolonTriviaSymbol : TriviaSymbol
{
public override void Emit(ISymbolEmitter emitter)
{
emitter.Append(';');
}
}

View File

@@ -0,0 +1,16 @@
namespace BetterGenshinImpact.CombatScript;
public sealed class CommentSymbol : TriviaSymbol
{
public CommentSymbol(string comment)
{
Comment = comment;
}
public string Comment { get; }
public override void Emit(ISymbolEmitter emitter)
{
emitter.Append("//").Append(Comment);
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Immutable;
namespace BetterGenshinImpact.CombatScript;
public class DashSymbol : InstructionSymbol, IInstructionSymbolHasDuration
{
public DashSymbol(ImmutableArray<IParameterSymbol> parameterList, ImmutableArray<TriviaSymbol> trivia)
: base("dash", parameterList, trivia)
{
InstructionThrowHelper.ThrowIfParameterListIsDefault(parameterList);
InstructionThrowHelper.ThrowIfParameterListCountNotCorrect(parameterList, [0, 1]);
if (parameterList.Length is 1)
{
InstructionThrowHelper.ThrowIfParameterAtIndexIsNot(parameterList, 0, out DoubleSymbol doubleSymbol);
HasDuration = true;
Duration = TimeSpan.FromSeconds(doubleSymbol.Value);
}
}
public DashSymbol(ImmutableArray<TriviaSymbol> trivia)
: base("dash", trivia)
{
}
public bool HasDuration { get; }
public TimeSpan Duration { get; }
}

View File

@@ -0,0 +1,31 @@
using System.Text;
namespace BetterGenshinImpact.CombatScript;
public sealed class DefaultSymbolEmitter : ISymbolEmitter
{
private readonly StringBuilder builder = new();
public string Emit()
{
return builder.ToString();
}
public ISymbolEmitter Append(char value)
{
builder.Append(value);
return this;
}
public ISymbolEmitter Append(double value)
{
builder.Append(value);
return this;
}
public ISymbolEmitter Append(string value)
{
builder.Append(value);
return this;
}
}

View File

@@ -0,0 +1,16 @@
namespace BetterGenshinImpact.CombatScript;
public sealed class DoubleSymbol : BaseSymbol, IParameterSymbol
{
public DoubleSymbol(double value)
{
Value = value;
}
public double Value { get; }
public override void Emit(ISymbolEmitter emitter)
{
emitter.Append(Value);
}
}

View File

@@ -0,0 +1,9 @@
namespace BetterGenshinImpact.CombatScript;
public sealed class HoldSymbol : BaseSymbol, IParameterSymbol
{
public override void Emit(ISymbolEmitter emitter)
{
emitter.Append("hold");
}
}

View File

@@ -0,0 +1,8 @@
namespace BetterGenshinImpact.CombatScript;
public interface IInstructionSymbolHasAlias
{
public string AliasName { get; }
public bool IsAlias { get; }
}

View File

@@ -0,0 +1,10 @@
using System;
namespace BetterGenshinImpact.CombatScript;
public interface IInstructionSymbolHasDuration
{
public bool HasDuration { get; }
public TimeSpan Duration { get; }
}

View File

@@ -0,0 +1,5 @@
namespace BetterGenshinImpact.CombatScript;
public interface IParameterSymbol : ISymbol
{
}

View File

@@ -0,0 +1,6 @@
namespace BetterGenshinImpact.CombatScript;
public interface ISymbol
{
void Emit(ISymbolEmitter emitter);
}

View File

@@ -0,0 +1,12 @@
namespace BetterGenshinImpact.CombatScript;
public interface ISymbolEmitter
{
string Emit();
ISymbolEmitter Append(char value);
ISymbolEmitter Append(double value);
ISymbolEmitter Append(string value);
}

View File

@@ -0,0 +1,18 @@
using System.Collections.Immutable;
namespace BetterGenshinImpact.CombatScript;
public sealed class InstructionListSymbol : BaseSymbol
{
public InstructionListSymbol(ImmutableArray<InstructionSymbol> instructions)
{
Instructions = instructions;
}
public ImmutableArray<InstructionSymbol> Instructions { get; }
public override void Emit(ISymbolEmitter emitter)
{
emitter.Append(Instructions);
}
}

View File

@@ -0,0 +1,50 @@
using System.Collections.Immutable;
namespace BetterGenshinImpact.CombatScript;
public abstract class InstructionSymbol : BaseSymbol
{
protected InstructionSymbol(string name, ImmutableArray<TriviaSymbol> trivia)
{
Name = name;
HasParameterList = false;
TriviaList = trivia;
}
protected InstructionSymbol(string name, ImmutableArray<IParameterSymbol> parameterList, ImmutableArray<TriviaSymbol> trivia)
{
Name = name;
HasParameterList = true;
ParameterList = parameterList;
TriviaList = trivia;
}
public string Name { get; }
public bool HasParameterList { get; }
public ImmutableArray<IParameterSymbol> ParameterList { get; }
public ImmutableArray<TriviaSymbol> TriviaList { get; }
public override void Emit(ISymbolEmitter emitter)
{
if (this is IInstructionSymbolHasAlias {IsAlias: true } hasAlias)
{
emitter.Append(hasAlias.AliasName);
}
else
{
emitter.Append(Name);
}
if (HasParameterList)
{
emitter.Append('(');
emitter.Append(ParameterList);
emitter.Append(')');
}
emitter.Append(TriviaList);
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Collections.Frozen;
using System.Collections.Immutable;
namespace BetterGenshinImpact.CombatScript;
public static class InstructionThrowHelper
{
public static void ThrowIfParameterListIsDefault(ImmutableArray<IParameterSymbol> parameterList)
{
if (parameterList.IsDefault)
{
throw new ArgumentException($"Parameter list can not be default.");
}
}
public static void ThrowIfParameterListCountNotCorrect(ImmutableArray<IParameterSymbol> parameterList, FrozenSet<int> allowedCounts)
{
if (!allowedCounts.Contains(parameterList.Length))
{
throw new ArgumentException($"Instruction parameter count not correct.");
}
}
public static void ThrowIfParameterAtIndexIsNot<T>(ImmutableArray<IParameterSymbol> parameterList, int index, out T symbol)
where T : IParameterSymbol
{
if (parameterList.Length <= index || parameterList[index] is not T result)
{
throw new ArgumentException($"Instruction's parameter at index {index} must be a {typeof(T).Name}.");
}
symbol = result;
}
}

View File

@@ -0,0 +1,25 @@
using System.Collections.Immutable;
namespace BetterGenshinImpact.CombatScript;
public class JumpSymbol : InstructionSymbol, IInstructionSymbolHasAlias
{
public JumpSymbol(bool isAlias, ImmutableArray<IParameterSymbol> parameterList, ImmutableArray<TriviaSymbol> trivia)
: base("jump", parameterList, trivia)
{
InstructionThrowHelper.ThrowIfParameterListIsDefault(parameterList);
InstructionThrowHelper.ThrowIfParameterListCountNotCorrect(parameterList, [0]);
IsAlias = isAlias;
}
public JumpSymbol(bool isAlias, ImmutableArray<TriviaSymbol> trivia)
: base("jump", trivia)
{
IsAlias = isAlias;
}
public string AliasName { get; } = "j";
public bool IsAlias { get; }
}

View File

@@ -0,0 +1,28 @@
using System;
namespace BetterGenshinImpact.CombatScript;
public class LineBreakTriviaSymbol : TriviaSymbol
{
private readonly string lineBreak;
public LineBreakTriviaSymbol()
{
lineBreak = Environment.NewLine;
}
public LineBreakTriviaSymbol(string lineBreak)
{
if (lineBreak is not ("\r\n" or "\n" or "\r" or ";"))
{
throw new ArgumentException("Invalid line break.");
}
this.lineBreak = lineBreak;
}
public override void Emit(ISymbolEmitter emitter)
{
emitter.Append(lineBreak);
}
}

View File

@@ -0,0 +1,23 @@
using System.Collections.Immutable;
namespace BetterGenshinImpact.CombatScript;
public sealed class ScriptUnit
{
public ScriptUnit(ImmutableArray<ISymbol> symbols)
{
Symbols = symbols;
}
public ImmutableArray<ISymbol> Symbols { get; }
public string Emit(ISymbolEmitter emitter)
{
foreach (ref readonly ISymbol symbol in Symbols.AsSpan())
{
symbol.Emit(emitter);
}
return emitter.Emit();
}
}

View File

@@ -0,0 +1,30 @@
using System.Collections.Immutable;
namespace BetterGenshinImpact.CombatScript;
public class SkillSymbol : InstructionSymbol, IInstructionSymbolHasAlias
{
public SkillSymbol(bool isAlias, ImmutableArray<IParameterSymbol> parameterList, ImmutableArray<TriviaSymbol> trivia)
: base("skill", parameterList, trivia)
{
InstructionThrowHelper.ThrowIfParameterListIsDefault(parameterList);
InstructionThrowHelper.ThrowIfParameterListCountNotCorrect(parameterList, [0, 1]);
if (parameterList.Length is 1)
{
InstructionThrowHelper.ThrowIfParameterAtIndexIsNot(parameterList, 0, out HoldSymbol _);
}
IsAlias = isAlias;
}
public SkillSymbol(bool isAlias, ImmutableArray<TriviaSymbol> trivia)
: base("skill", trivia)
{
IsAlias = isAlias;
}
public string AliasName { get; } = "e";
public bool IsAlias { get; }
}

View File

@@ -0,0 +1,9 @@
namespace BetterGenshinImpact.CombatScript;
public class SpaceTriviaSymbol : TriviaSymbol
{
public override void Emit(ISymbolEmitter emitter)
{
emitter.Append(' ');
}
}

View File

@@ -0,0 +1,23 @@
using System.Collections.Immutable;
namespace BetterGenshinImpact.CombatScript;
public static class SymbolEmitterExtensions
{
public static ISymbolEmitter Append(this ISymbolEmitter emitter, ISymbol symbol)
{
symbol.Emit(emitter);
return emitter;
}
public static ISymbolEmitter Append<TSymbol>(this ISymbolEmitter emitter, ImmutableArray<TSymbol> symbolList)
where TSymbol : ISymbol
{
foreach(ref readonly TSymbol symbol in symbolList.AsSpan())
{
symbol.Emit(emitter);
}
return emitter;
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
namespace BetterGenshinImpact.CombatScript;
public sealed class SymbolParser
{
public ScriptUnit Parse(ReadOnlySpan<char> raw)
{
ImmutableArray<ISymbol>.Builder symbols = ImmutableArray.CreateBuilder<ISymbol>();
ParseLines(raw, symbols);
return new(symbols.ToImmutable());
}
private void ParseLines(ReadOnlySpan<char> raw, ImmutableArray<ISymbol>.Builder symbols)
{
bool skipNextRange = false;
ref readonly char end = ref raw[^1];
foreach(Range range in raw.SplitAny(['\r', '\n', ';']))
{
if (skipNextRange)
{
skipNextRange = false;
continue;
}
int offset = range.End.GetOffset(raw.Length);
if (offset >= raw.Length)
{
break;
}
ref readonly char peek = ref raw[offset];
LineBreakTriviaSymbol lineBreakTrivia;
if (peek is '\r')
{
if (Unsafe.IsAddressLessThan(in peek, in end) && Unsafe.Add(ref Unsafe.AsRef(in peek), 1) is '\n')
{
// It's a CRLF
lineBreakTrivia = new("\r\n");
skipNextRange = true;
}
else
{
// It's a CR
lineBreakTrivia = new("\r");
}
}
else if (peek is '\n')
{
// It's a LF
lineBreakTrivia = new("\n");
}
else if (peek is ';')
{
lineBreakTrivia = new(";");
}
else
{
throw new InvalidOperationException($"Failed to parse line break trivia at {range}.");
}
ReadOnlySpan<char> currentSpan = raw[range];
symbols.Add(lineBreakTrivia);
}
}
}

View File

@@ -0,0 +1,5 @@
namespace BetterGenshinImpact.CombatScript;
public abstract class TriviaSymbol : BaseSymbol
{
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Immutable;
namespace BetterGenshinImpact.CombatScript;
public class WaitSymbol : InstructionSymbol, IInstructionSymbolHasDuration
{
public WaitSymbol(ImmutableArray<IParameterSymbol> parameterList, ImmutableArray<TriviaSymbol> trivia)
: base("wait", parameterList, trivia)
{
InstructionThrowHelper.ThrowIfParameterListIsDefault(parameterList);
InstructionThrowHelper.ThrowIfParameterListCountNotCorrect(parameterList, [1]);
InstructionThrowHelper.ThrowIfParameterAtIndexIsNot(parameterList, 0, out DoubleSymbol doubleSymbol);
HasDuration = true;
Duration = TimeSpan.FromSeconds(doubleSymbol.Value);
}
public bool HasDuration { get; }
public TimeSpan Duration { get; }
}

View File

@@ -0,0 +1,9 @@
namespace BetterGenshinImpact.CombatScript;
public enum WalkDirection
{
Forward,
Backward,
Left,
Right
}

View File

@@ -0,0 +1,70 @@
using System;
using System.Collections.Immutable;
namespace BetterGenshinImpact.CombatScript;
public class WalkSymbol : InstructionSymbol, IInstructionSymbolHasAlias, IInstructionSymbolHasDuration, IParameterSymbol
{
public WalkSymbol(WalkDirection direction, ImmutableArray<IParameterSymbol> parameterList, ImmutableArray<TriviaSymbol> trivia)
: base("walk", parameterList, trivia)
{
InstructionThrowHelper.ThrowIfParameterListIsDefault(parameterList);
InstructionThrowHelper.ThrowIfParameterListCountNotCorrect(parameterList, [1]);
InstructionThrowHelper.ThrowIfParameterAtIndexIsNot(parameterList, 0, out DoubleSymbol doubleSymbol);
HasDuration = true;
Duration = TimeSpan.FromSeconds(doubleSymbol.Value);
Direction = direction;
IsAlias = true;
}
// Used for parameter
public WalkSymbol(WalkDirection direction, ImmutableArray<TriviaSymbol> trivia)
: base("walk", trivia)
{
Direction = direction;
IsAlias = true;
}
public WalkSymbol(ImmutableArray<IParameterSymbol> parameterList, ImmutableArray<TriviaSymbol> trivia)
: base("walk", parameterList, trivia)
{
InstructionThrowHelper.ThrowIfParameterListIsDefault(parameterList);
InstructionThrowHelper.ThrowIfParameterListCountNotCorrect(parameterList, [2]);
InstructionThrowHelper.ThrowIfParameterAtIndexIsNot(parameterList, 0, out WalkSymbol directionAlias);
InstructionThrowHelper.ThrowIfParameterAtIndexIsNot(parameterList, 1, out DoubleSymbol doubleSymbol);
Direction = directionAlias.Direction;
HasDuration = true;
Duration = TimeSpan.FromSeconds(doubleSymbol.Value);
IsAlias = false;
}
public string AliasName
{
get
{
return Direction switch
{
WalkDirection.Forward => "w",
WalkDirection.Backward => "s",
WalkDirection.Left => "a",
WalkDirection.Right => "d",
_ => throw new ArgumentOutOfRangeException(),
};
}
}
public bool IsAlias { get; }
public WalkDirection Direction { get; }
public bool HasDuration { get; }
public TimeSpan Duration { get; }
}

View File

@@ -19,6 +19,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterGenshinImpact.Test",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fischless.WindowsInput", "Fischless.WindowsInput\Fischless.WindowsInput.csproj", "{9D00BC7A-9280-4AC9-8951-4502EDB71B76}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BetterGenshinImpact.CombatScript", "BetterGenshinImpact.CombatScript\BetterGenshinImpact.CombatScript.csproj", "{57C278F9-F62A-480A-B929-B0E5D8174194}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BetterGenshinImpact.CombatScript.Test", "BetterGenshinImpact.CombatScript.Test\BetterGenshinImpact.CombatScript.Test.csproj", "{09427DA8-665E-441A-A576-C11FBC6F9E5C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -83,6 +87,22 @@ Global
{9D00BC7A-9280-4AC9-8951-4502EDB71B76}.Release|Any CPU.Build.0 = Release|x64
{9D00BC7A-9280-4AC9-8951-4502EDB71B76}.Release|x64.ActiveCfg = Release|x64
{9D00BC7A-9280-4AC9-8951-4502EDB71B76}.Release|x64.Build.0 = Release|x64
{57C278F9-F62A-480A-B929-B0E5D8174194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{57C278F9-F62A-480A-B929-B0E5D8174194}.Debug|Any CPU.Build.0 = Debug|Any CPU
{57C278F9-F62A-480A-B929-B0E5D8174194}.Debug|x64.ActiveCfg = Debug|Any CPU
{57C278F9-F62A-480A-B929-B0E5D8174194}.Debug|x64.Build.0 = Debug|Any CPU
{57C278F9-F62A-480A-B929-B0E5D8174194}.Release|Any CPU.ActiveCfg = Release|Any CPU
{57C278F9-F62A-480A-B929-B0E5D8174194}.Release|Any CPU.Build.0 = Release|Any CPU
{57C278F9-F62A-480A-B929-B0E5D8174194}.Release|x64.ActiveCfg = Release|Any CPU
{57C278F9-F62A-480A-B929-B0E5D8174194}.Release|x64.Build.0 = Release|Any CPU
{09427DA8-665E-441A-A576-C11FBC6F9E5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{09427DA8-665E-441A-A576-C11FBC6F9E5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09427DA8-665E-441A-A576-C11FBC6F9E5C}.Debug|x64.ActiveCfg = Debug|Any CPU
{09427DA8-665E-441A-A576-C11FBC6F9E5C}.Debug|x64.Build.0 = Debug|Any CPU
{09427DA8-665E-441A-A576-C11FBC6F9E5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09427DA8-665E-441A-A576-C11FBC6F9E5C}.Release|Any CPU.Build.0 = Release|Any CPU
{09427DA8-665E-441A-A576-C11FBC6F9E5C}.Release|x64.ActiveCfg = Release|Any CPU
{09427DA8-665E-441A-A576-C11FBC6F9E5C}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE