diff --git a/MSBuild/Robust.Analyzers.targets b/MSBuild/Robust.Analyzers.targets
new file mode 100644
index 000000000..3304dc624
--- /dev/null
+++ b/MSBuild/Robust.Analyzers.targets
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/MSBuild/Robust.Engine.targets b/MSBuild/Robust.Engine.targets
index a64fc74c7..0c1f7a6e0 100644
--- a/MSBuild/Robust.Engine.targets
+++ b/MSBuild/Robust.Engine.targets
@@ -1,2 +1,3 @@
+
diff --git a/Robust.Analyzers/ExplicitInterfaceAnalyzer.cs b/Robust.Analyzers/ExplicitInterfaceAnalyzer.cs
new file mode 100644
index 000000000..d1f5923c9
--- /dev/null
+++ b/Robust.Analyzers/ExplicitInterfaceAnalyzer.cs
@@ -0,0 +1,92 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Document = Microsoft.CodeAnalysis.Document;
+
+namespace Robust.Analyzers
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class ExplicitInterfaceAnalyzer : DiagnosticAnalyzer
+ {
+ public readonly SyntaxKind[] ExcludedModifiers =
+ {
+ SyntaxKind.VirtualKeyword,
+ SyntaxKind.AbstractKeyword,
+ SyntaxKind.OverrideKeyword
+ };
+
+ public const string DiagnosticId = "RA0000";
+
+ private const string Title = "No explicit interface specified";
+ private const string MessageFormat = "No explicit interface specified";
+ private const string Description = "Make sure to specify the interface in your method-declaration.";
+ private const string Category = "Usage";
+
+ [SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
+
+ private const string RequiresExplicitImplementationAttributeMetadataName =
+ "Robust.Shared.Analyzers.RequiresExplicitImplementationAttribute";
+
+ public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+ context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.MethodDeclaration);
+ context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.PropertyDeclaration);
+ }
+
+ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
+ {
+ ISymbol symbol;
+ Location location;
+ switch (context.Node)
+ {
+ //we already have a explicit interface specified, no need to check further
+ case MethodDeclarationSyntax methodDecl when methodDecl.ExplicitInterfaceSpecifier != null || methodDecl.Modifiers.Any(m => ExcludedModifiers.Contains(m.Kind())):
+ return;
+ case PropertyDeclarationSyntax propertyDecl when propertyDecl.ExplicitInterfaceSpecifier != null || propertyDecl.Modifiers.Any(m => ExcludedModifiers.Contains(m.Kind())):
+ return;
+
+ case MethodDeclarationSyntax methodDecl:
+ symbol = context.SemanticModel.GetDeclaredSymbol(methodDecl);
+ location = methodDecl.Identifier.GetLocation();
+ break;
+ case PropertyDeclarationSyntax propertyDecl:
+ symbol = context.SemanticModel.GetDeclaredSymbol(propertyDecl);
+ location = propertyDecl.Identifier.GetLocation();
+ break;
+
+ default:
+ return;
+ }
+
+ var attrSymbol = context.Compilation.GetTypeByMetadataName(RequiresExplicitImplementationAttributeMetadataName);
+
+ var isInterfaceMember = symbol?.ContainingType.AllInterfaces.Any(
+ i =>
+ i.GetMembers().Any(m => SymbolEqualityComparer.Default.Equals(symbol, symbol.ContainingType.FindImplementationForInterfaceMember(m)))
+ && i.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol))
+ ) ?? false;
+
+ if (isInterfaceMember)
+ {
+ //we do not have an explicit interface specified. bad!
+ var diagnostic = Diagnostic.Create(
+ Rule,
+ location);
+ context.ReportDiagnostic(diagnostic);
+ }
+ }
+ }
+}
diff --git a/Robust.Analyzers/Robust.Analyzers.csproj b/Robust.Analyzers/Robust.Analyzers.csproj
new file mode 100644
index 000000000..f4946ce38
--- /dev/null
+++ b/Robust.Analyzers/Robust.Analyzers.csproj
@@ -0,0 +1,13 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
diff --git a/Robust.Analyzers/SerializableAnalyzer.cs b/Robust.Analyzers/SerializableAnalyzer.cs
new file mode 100644
index 000000000..1114e1990
--- /dev/null
+++ b/Robust.Analyzers/SerializableAnalyzer.cs
@@ -0,0 +1,150 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Robust.Analyzers
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class SerializableAnalyzer : DiagnosticAnalyzer
+ {
+ // Metadata of the analyzer
+ public const string DiagnosticId = "RA0001";
+
+ // You could use LocalizedString but it's a little more complicated for this sample
+ private const string Title = "Class not marked as (Net)Serializable";
+ private const string MessageFormat = "Class not marked as (Net)Serializable";
+ private const string Description = "The class should be marked as (Net)Serializable.";
+ private const string Category = "Usage";
+
+ private const string RequiresSerializableAttributeMetadataName = "Robust.Shared.Analyzers.RequiresSerializableAttribute";
+ private const string SerializableAttributeMetadataName = "System.SerializableAttribute";
+ private const string NetSerializableAttributeMetadataName = "Robust.Shared.Serialization.NetSerializableAttribute";
+
+ [SuppressMessage("ReSharper", "RS2008")] private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
+
+ public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+ context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration);
+ }
+
+ private bool Marked(INamedTypeSymbol namedTypeSymbol, INamedTypeSymbol attrSymbol)
+ {
+ if (namedTypeSymbol == null) return false;
+ if (namedTypeSymbol.GetAttributes()
+ .Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol))) return true;
+ return Marked(namedTypeSymbol.BaseType, attrSymbol);
+ }
+
+ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
+ {
+ var attrSymbol = context.Compilation.GetTypeByMetadataName(RequiresSerializableAttributeMetadataName);
+ var classDecl = (ClassDeclarationSyntax) context.Node;
+ var classSymbol = context.SemanticModel.GetDeclaredSymbol(classDecl);
+ if (classSymbol == null) return;
+
+ if (Marked(classSymbol, attrSymbol))
+ {
+ var attributes = classSymbol.GetAttributes();
+ var serAttr = context.Compilation.GetTypeByMetadataName(SerializableAttributeMetadataName);
+ var netSerAttr = context.Compilation.GetTypeByMetadataName(NetSerializableAttributeMetadataName);
+
+ var hasSerAttr = attributes.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, serAttr));
+ var hasNetSerAttr =
+ attributes.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, netSerAttr));
+
+ if (!hasSerAttr || !hasNetSerAttr)
+ {
+ var requiredAttributes = new List();
+ if(!hasSerAttr) requiredAttributes.Add(SerializableAttributeMetadataName);
+ if(!hasNetSerAttr) requiredAttributes.Add(NetSerializableAttributeMetadataName);
+
+ context.ReportDiagnostic(
+ Diagnostic.Create(
+ Rule,
+ classDecl.Identifier.GetLocation(),
+ ImmutableDictionary.CreateRange(new Dictionary()
+ {
+ {
+ "requiredAttributes", string.Join(",", requiredAttributes)
+ }
+ })));
+ }
+ }
+ }
+ }
+
+ [ExportCodeFixProvider(LanguageNames.CSharp)]
+ public class SerializableCodeFixProvider : CodeFixProvider
+ {
+ private const string Title = "Annotate class as (Net)Serializable.";
+
+ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
+ {
+ var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
+
+ foreach (var diagnostic in context.Diagnostics)
+ {
+ var span = diagnostic.Location.SourceSpan;
+ var classDecl = root.FindToken(span.Start).Parent.AncestorsAndSelf().OfType().First();
+
+ if(!diagnostic.Properties.TryGetValue("requiredAttributes", out var requiredAttributes)) return;
+
+ context.RegisterCodeFix(
+ CodeAction.Create(
+ Title,
+ c => FixAsync(context.Document, classDecl, requiredAttributes, c),
+ Title),
+ diagnostic);
+ }
+ }
+
+ private async Task FixAsync(Document document, ClassDeclarationSyntax classDecl,
+ string requiredAttributes, CancellationToken cancellationToken)
+ {
+ var attributes = new List();
+ var namespaces = new List();
+ foreach (var attribute in requiredAttributes.Split(','))
+ {
+ var tempSplit = attribute.Split('.');
+ namespaces.Add(string.Join(".",tempSplit.Take(tempSplit.Length-1)));
+ var @class = tempSplit.Last();
+ @class = @class.Substring(0, @class.Length - 9); //cut out "Attribute" at the end
+ attributes.Add(SyntaxFactory.Attribute(SyntaxFactory.ParseName(@class)));
+ }
+
+ var newClassDecl =
+ classDecl.AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(attributes)));
+
+ var root = (CompilationUnitSyntax) await document.GetSyntaxRootAsync(cancellationToken);
+ root = root.ReplaceNode(classDecl, newClassDecl);
+
+ foreach (var ns in namespaces)
+ {
+ if(root.Usings.Any(u => u.Name.ToString() == ns)) continue;
+ root = root.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(ns)));
+ }
+ return document.WithSyntaxRoot(root);
+ }
+
+ public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(SerializableAnalyzer.DiagnosticId);
+
+ public override FixAllProvider GetFixAllProvider()
+ {
+ return WellKnownFixAllProviders.BatchFixer;
+ }
+
+ }
+}
diff --git a/Robust.Client/Interfaces/Input/KeyBindingRegistration.cs b/Robust.Client/Interfaces/Input/KeyBindingRegistration.cs
index 700f3c093..8a6580485 100644
--- a/Robust.Client/Interfaces/Input/KeyBindingRegistration.cs
+++ b/Robust.Client/Interfaces/Input/KeyBindingRegistration.cs
@@ -18,7 +18,7 @@ namespace Robust.Client.Interfaces.Input
public bool CanRepeat;
public bool AllowSubCombs;
- public void ExposeData(ObjectSerializer serializer)
+ void IExposeData.ExposeData(ObjectSerializer serializer)
{
serializer.DataField(ref Function, "function", default);
serializer.DataField(ref Type, "type", KeyBindingType.State);
diff --git a/Robust.Server/GameObjects/Components/Container/ContainerManagerComponent.cs b/Robust.Server/GameObjects/Components/Container/ContainerManagerComponent.cs
index 0cc5bf4b7..4cde65bad 100644
--- a/Robust.Server/GameObjects/Components/Container/ContainerManagerComponent.cs
+++ b/Robust.Server/GameObjects/Components/Container/ContainerManagerComponent.cs
@@ -289,7 +289,7 @@ namespace Robust.Server.GameObjects.Components.Container
Type = type;
}
- public void ExposeData(ObjectSerializer serializer)
+ void IExposeData.ExposeData(ObjectSerializer serializer)
{
serializer.DataField(ref Entities, "entities", new List());
serializer.DataField(ref Type, "type", null);
diff --git a/Robust.Shared/Analyzers/RequiresExplicitImplementationAttribute.cs b/Robust.Shared/Analyzers/RequiresExplicitImplementationAttribute.cs
new file mode 100644
index 000000000..4db6c5045
--- /dev/null
+++ b/Robust.Shared/Analyzers/RequiresExplicitImplementationAttribute.cs
@@ -0,0 +1,7 @@
+using System;
+
+namespace Robust.Shared.Analyzers
+{
+ [AttributeUsage(AttributeTargets.Interface)]
+ public class RequiresExplicitImplementationAttribute : Attribute { }
+}
diff --git a/Robust.Shared/Analyzers/RequiresSerializableAttribute.cs b/Robust.Shared/Analyzers/RequiresSerializableAttribute.cs
new file mode 100644
index 000000000..b705dcb36
--- /dev/null
+++ b/Robust.Shared/Analyzers/RequiresSerializableAttribute.cs
@@ -0,0 +1,7 @@
+using System;
+
+namespace Robust.Shared.Analyzers
+{
+ [AttributeUsage(AttributeTargets.Class)]
+ public class RequiresSerializableAttribute : Attribute { }
+}
diff --git a/Robust.Shared/Audio/AudioParams.cs b/Robust.Shared/Audio/AudioParams.cs
index e126f3411..1ebcc8bcf 100644
--- a/Robust.Shared/Audio/AudioParams.cs
+++ b/Robust.Shared/Audio/AudioParams.cs
@@ -55,7 +55,7 @@ namespace Robust.Shared.Audio
///
public static readonly AudioParams Default = new(0, 1, "Master", 62.5f, 1, AudioMixTarget.Stereo, false, 0f);
- public void ExposeData(ObjectSerializer serializer)
+ void IExposeData.ExposeData(ObjectSerializer serializer)
{
Volume = serializer.ReadDataField("volume", 0f);
PitchScale = serializer.ReadDataField("pitchscale", 1f);
diff --git a/Robust.Shared/GameObjects/ComponentState.cs b/Robust.Shared/GameObjects/ComponentState.cs
index 0c285f5e9..6dacd8870 100644
--- a/Robust.Shared/GameObjects/ComponentState.cs
+++ b/Robust.Shared/GameObjects/ComponentState.cs
@@ -1,8 +1,10 @@
using Robust.Shared.Serialization;
using System;
+using Robust.Shared.Analyzers;
namespace Robust.Shared.GameObjects
{
+ [RequiresSerializable]
[Serializable, NetSerializable]
public class ComponentState
{
diff --git a/Robust.Shared/GameObjects/Components/Renderable/SharedSpriteComponent.cs b/Robust.Shared/GameObjects/Components/Renderable/SharedSpriteComponent.cs
index 3bd9072c7..a06b45d43 100644
--- a/Robust.Shared/GameObjects/Components/Renderable/SharedSpriteComponent.cs
+++ b/Robust.Shared/GameObjects/Components/Renderable/SharedSpriteComponent.cs
@@ -80,7 +80,7 @@ namespace Robust.Shared.GameObjects.Components.Renderable
};
}
- public void ExposeData(ObjectSerializer serializer)
+ void IExposeData.ExposeData(ObjectSerializer serializer)
{
serializer.DataField(ref Shader, "shader", null);
serializer.DataField(ref TexturePath, "texture", null);
diff --git a/Robust.Shared/GameObjects/Components/UserInterface/SharedUserInterfaceComponent.cs b/Robust.Shared/GameObjects/Components/UserInterface/SharedUserInterfaceComponent.cs
index 4ccd8d3c0..ba79de7ef 100644
--- a/Robust.Shared/GameObjects/Components/UserInterface/SharedUserInterfaceComponent.cs
+++ b/Robust.Shared/GameObjects/Components/UserInterface/SharedUserInterfaceComponent.cs
@@ -14,7 +14,7 @@ namespace Robust.Shared.GameObjects.Components.UserInterface
public object UiKey { get; private set; } = default!;
public string ClientType { get; private set; } = default!;
- public void ExposeData(ObjectSerializer serializer)
+ void IExposeData.ExposeData(ObjectSerializer serializer)
{
UiKey = serializer.ReadStringEnumKey("key");
ClientType = serializer.ReadDataField("type");
diff --git a/Robust.Shared/Interfaces/Serialization/IExposeData.cs b/Robust.Shared/Interfaces/Serialization/IExposeData.cs
index 516bd9fd9..8d14b0d36 100644
--- a/Robust.Shared/Interfaces/Serialization/IExposeData.cs
+++ b/Robust.Shared/Interfaces/Serialization/IExposeData.cs
@@ -1,3 +1,4 @@
+using Robust.Shared.Analyzers;
using Robust.Shared.Serialization;
namespace Robust.Shared.Interfaces.Serialization
@@ -5,6 +6,7 @@ namespace Robust.Shared.Interfaces.Serialization
///
/// Interface for the "expose data" system, which is basically our method of handling data serialization.
///
+ [RequiresExplicitImplementation]
public interface IExposeData
{
///
diff --git a/Robust.Shared/Map/PhysShapeGrid.cs b/Robust.Shared/Map/PhysShapeGrid.cs
index 8e5e49168..f7033cd09 100644
--- a/Robust.Shared/Map/PhysShapeGrid.cs
+++ b/Robust.Shared/Map/PhysShapeGrid.cs
@@ -1,5 +1,6 @@
using System;
using Robust.Shared.Interfaces.Map;
+using Robust.Shared.Interfaces.Serialization;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
@@ -83,7 +84,7 @@ namespace Robust.Shared.Map
}
///
- public void ExposeData(ObjectSerializer serializer)
+ void IExposeData.ExposeData(ObjectSerializer serializer)
{
serializer.DataField(ref _gridId, "grid", GridId.Invalid);
diff --git a/Robust.Shared/Physics/PhysShapeAabb.cs b/Robust.Shared/Physics/PhysShapeAabb.cs
index b5e7db959..80cb1a76e 100644
--- a/Robust.Shared/Physics/PhysShapeAabb.cs
+++ b/Robust.Shared/Physics/PhysShapeAabb.cs
@@ -1,4 +1,5 @@
using System;
+using Robust.Shared.Interfaces.Serialization;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
@@ -80,7 +81,7 @@ namespace Robust.Shared.Physics
}
///
- public void ExposeData(ObjectSerializer serializer)
+ void IExposeData.ExposeData(ObjectSerializer serializer)
{
serializer.DataField(ref _collisionLayer, "layer", 0, WithFormat.Flags());
serializer.DataField(ref _collisionMask, "mask", 0, WithFormat.Flags());
diff --git a/Robust.Shared/Physics/PhysShapeCircle.cs b/Robust.Shared/Physics/PhysShapeCircle.cs
index 014b7ffc2..0af57d3ac 100644
--- a/Robust.Shared/Physics/PhysShapeCircle.cs
+++ b/Robust.Shared/Physics/PhysShapeCircle.cs
@@ -1,4 +1,5 @@
using System;
+using Robust.Shared.Interfaces.Serialization;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
@@ -61,7 +62,7 @@ namespace Robust.Shared.Physics
}
///
- public void ExposeData(ObjectSerializer serializer)
+ void IExposeData.ExposeData(ObjectSerializer serializer)
{
serializer.DataField(ref _collisionLayer, "layer", 0, WithFormat.Flags());
serializer.DataField(ref _collisionMask, "mask", 0, WithFormat.Flags());
diff --git a/Robust.Shared/Physics/PhysShapeRect.cs b/Robust.Shared/Physics/PhysShapeRect.cs
index f7bdaea06..a90ec3ec2 100644
--- a/Robust.Shared/Physics/PhysShapeRect.cs
+++ b/Robust.Shared/Physics/PhysShapeRect.cs
@@ -1,4 +1,5 @@
using System;
+using Robust.Shared.Interfaces.Serialization;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
@@ -64,7 +65,7 @@ namespace Robust.Shared.Physics
handle.SetTransform(Matrix3.Identity);
}
- public void ExposeData(ObjectSerializer serializer)
+ void IExposeData.ExposeData(ObjectSerializer serializer)
{
serializer.DataField(ref _collisionLayer, "layer", 0, WithFormat.Flags());
serializer.DataField(ref _collisionMask, "mask", 0, WithFormat.Flags());
diff --git a/RobustToolbox.sln b/RobustToolbox.sln
index 701cbe2b7..3c91a0a42 100644
--- a/RobustToolbox.sln
+++ b/RobustToolbox.sln
@@ -32,6 +32,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ManagedHttpListener", "ManagedHttpListener", "{15D28C35-25F6-4EA8-8D53-29DA7C8A24A7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.HttpListener", "ManagedHttpListener\src\System.Net.HttpListener.csproj", "{C3EB43AF-31FD-48F5-A4FB-552D0F13948B}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Client.Injectors", "Robust.Client.Injectors\Robust.Client.Injectors.csproj", "{EEF2C805-5E03-41EA-A916-49C1DD15EF41}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Client.NameGenerator", "Robust.Client.NameGenerator\Robust.Client.NameGenerator.csproj", "{EFB7A05D-71D0-47D1-B7B4-35D4FF661F13}"
@@ -44,6 +45,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX.IL.Cecil", "XamlX\src
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX.Runtime", "XamlX\src\XamlX.Runtime\XamlX.Runtime.csproj", "{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Analyzers", "Robust.Analyzers\Robust.Analyzers.csproj", "{3173712A-9E75-4685-B657-9AF9B7D54EFB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -188,6 +191,14 @@ Global
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|Any CPU.Build.0 = Release|Any CPU
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|x64.ActiveCfg = Release|Any CPU
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1}.Release|x64.Build.0 = Release|Any CPU
+ {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Debug|x64.Build.0 = Debug|Any CPU
+ {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Release|x64.ActiveCfg = Release|Any CPU
+ {3173712A-9E75-4685-B657-9AF9B7D54EFB}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE