~ruther/NosSmooth

b1ca4f80bce17293b0e78f73cb01a9e058079544 — Rutherther 2 years ago 7793e41
feat(crypto): add cryptography server, client, world, login...
A Core/NosSmooth.Cryptography/ClientLoginCryptography.cs => Core/NosSmooth.Cryptography/ClientLoginCryptography.cs +41 -0
@@ 0,0 1,41 @@
//
//  ClientLoginCryptography.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Text;

namespace NosSmooth.Cryptography;

/// <summary>
/// A cryptography used for logging to NosTale server from the client.
/// </summary>
public class ClientLoginCryptography : ICryptography
{
    private static readonly Random Random = new Random(DateTime.Now.Millisecond);

    /// <inheritdoc />
    public string Decrypt(in ReadOnlySpan<byte> bytes, Encoding encoding)
    {
        var output = new StringBuilder();
        foreach (var c in bytes)
        {
            output.Append(Convert.ToChar(c - 0xF));
        }

        return output.ToString();
    }

    /// <inheritdoc />
    public byte[] Encrypt(string value, Encoding encoding)
    {
        var output = new byte[value.Length + 1];
        for (int i = 0; i < value.Length; i++)
        {
            output[i] = (byte)((value[i] ^ 0xC3) + 0xF);
        }
        output[output.Length - 1] = 0xD8;
        return output;
    }
}
\ No newline at end of file

A Core/NosSmooth.Cryptography/ClientWorldCryptography.cs => Core/NosSmooth.Cryptography/ClientWorldCryptography.cs +311 -0
@@ 0,0 1,311 @@
//
//  ClientWorldCryptography.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Text;

namespace NosSmooth.Cryptography;

/// <summary>
/// A cryptography used on world server, has to have a session id (encryption key) set from the client.
/// </summary>
public class ClientWorldCryptography : ICryptography
{
    private static readonly char[] Keys = { ' ', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'n' };

    /// <summary>
    /// Gets or sets the encryption key.
    /// </summary>
    public int EncryptionKey { get; set; }

    /// <summary>
    /// Initializes a new instance of the <see cref="ClientWorldCryptography"/> class.
    /// </summary>
    /// <param name="encryptionKey">Encryption key received by LoginServer.</param>
    public ClientWorldCryptography(int encryptionKey = 0)
    {
        EncryptionKey = encryptionKey;
    }

    /// <inheritdoc />
    public string Decrypt(in ReadOnlySpan<byte> bytes, Encoding encoding)
    {
        int index = 0;
        var currentPacket = new StringBuilder();

        while (index < bytes.Length)
        {
            byte currentByte = bytes[index++];

            if (currentByte == 0xFF)
            {
                currentPacket.Append('\n');
                continue;
            }

            int length = currentByte & 0x7F;

            if ((currentByte & 0x80) != 0)
            {
                while (length != 0)
                {
                    if (index < bytes.Length)
                    {
                        currentByte = bytes[index++];
                        int firstIndex = (currentByte & 0xF0) >> 4;
                        char first = '?';
                        if (firstIndex != 0)
                        {
                            firstIndex--;
                            first = firstIndex != 14 ? Keys[firstIndex] : '\u0000';
                        }

                        if (first != 0x6E)
                        {
                            currentPacket.Append(first);
                        }

                        if (length <= 1)
                        {
                            break;
                        }

                        int secondIndex = currentByte & 0xF;
                        char second = '?';
                        if (secondIndex != 0)
                        {
                            secondIndex--;
                            second = secondIndex != 14 ? Keys[secondIndex] : '\u0000';
                        }

                        if (second != 0x6E)
                        {
                            currentPacket.Append(second);
                        }

                        length -= 2;
                    }
                    else
                    {
                        length--;
                    }
                }
            }
            else
            {
                while (length != 0)
                {
                    if (index < bytes.Length)
                    {
                        currentPacket.Append((char)(bytes[index] ^ 0xFF));
                        index++;
                    }
                    else if (index == bytes.Length)
                    {
                        currentPacket.Append((char)0xFF);
                        index++;
                    }

                    length--;
                }
            }
        }

        return currentPacket.ToString();
    }

    /// <inheritdoc />
    public byte[] Encrypt(string value, Encoding encoding)
    {
        var output = new List<byte>();

        string mask = new string
        (
            value.Select
            (
                c =>
                {
                    sbyte b = (sbyte)c;
                    if (c == '#' || c == '/' || c == '%')
                    {
                        return '0';
                    }

                    if ((b -= 0x20) == 0 || (b += unchecked((sbyte)0xF1)) < 0 || (b -= 0xB) < 0 ||
                        b - unchecked((sbyte)0xC5) == 0)
                    {
                        return '1';
                    }

                    return '0';
                }
            ).ToArray()
        );

        int packetLength = value.Length;

        int sequenceCounter = 0;
        int currentPosition = 0;

        while (currentPosition <= packetLength)
        {
            int lastPosition = currentPosition;
            while (currentPosition < packetLength && mask[currentPosition] == '0')
            {
                currentPosition++;
            }

            int sequences;
            int length;

            if (currentPosition != 0)
            {
                length = currentPosition - lastPosition;
                sequences = length / 0x7E;
                for (int i = 0; i < length; i++, lastPosition++)
                {
                    if (i == sequenceCounter * 0x7E)
                    {
                        if (sequences == 0)
                        {
                            output.Add((byte)(length - i));
                        }
                        else
                        {
                            output.Add(0x7E);
                            sequences--;
                            sequenceCounter++;
                        }
                    }

                    output.Add((byte)((byte)value[lastPosition] ^ 0xFF));
                }
            }

            if (currentPosition >= packetLength)
            {
                break;
            }

            lastPosition = currentPosition;
            while (currentPosition < packetLength && mask[currentPosition] == '1')
            {
                currentPosition++;
            }

            if (currentPosition == 0)
            {
                continue;
            }

            length = currentPosition - lastPosition;
            sequences = length / 0x7E;
            for (int i = 0; i < length; i++, lastPosition++)
            {
                if (i == sequenceCounter * 0x7E)
                {
                    if (sequences == 0)
                    {
                        output.Add((byte)((length - i) | 0x80));
                    }
                    else
                    {
                        output.Add(0x7E | 0x80);
                        sequences--;
                        sequenceCounter++;
                    }
                }

                byte currentByte = (byte)value[lastPosition];
                switch (currentByte)
                {
                    case 0x20:
                        currentByte = 1;
                        break;
                    case 0x2D:
                        currentByte = 2;
                        break;
                    case 0xFF:
                        currentByte = 0xE;
                        break;
                    default:
                        currentByte -= 0x2C;
                        break;
                }

                if (currentByte == 0x00)
                {
                    continue;
                }

                if (i % 2 == 0)
                {
                    output.Add((byte)(currentByte << 4));
                }
                else
                {
                    output[output.Count - 1] = (byte)(output.Last() | currentByte);
                }
            }
        }

        output.Add(0xFF);

        sbyte sessionNumber = (sbyte)((EncryptionKey >> 6) & 0xFF & 0x80000003);

        if (sessionNumber < 0)
        {
            sessionNumber = (sbyte)(((sessionNumber - 1) | 0xFFFFFFFC) + 1);
        }

        byte sessionKey = (byte)(EncryptionKey & 0xFF);

        if (EncryptionKey != 0)
        {
            sessionNumber = -1;
        }

        switch (sessionNumber)
        {
            case 0:
                for (int i = 0; i < output.Count; i++)
                {
                    output[i] = (byte)(output[i] + sessionKey + 0x40);
                }

                break;
            case 1:
                for (int i = 0; i < output.Count; i++)
                {
                    output[i] = (byte)(output[i] - (sessionKey + 0x40));
                }

                break;
            case 2:
                for (int i = 0; i < output.Count; i++)
                {
                    output[i] = (byte)((output[i] ^ 0xC3) + sessionKey + 0x40);
                }

                break;
            case 3:
                for (int i = 0; i < output.Count; i++)
                {
                    output[i] = (byte)((output[i] ^ 0xC3) - (sessionKey + 0x40));
                }

                break;
            default:
                for (int i = 0; i < output.Count; i++)
                {
                    output[i] = (byte)(output[i] + 0x0F);
                }

                break;
        }

        return output.ToArray();
    }
}
\ No newline at end of file

A Core/NosSmooth.Cryptography/CryptographyManager.cs => Core/NosSmooth.Cryptography/CryptographyManager.cs +57 -0
@@ 0,0 1,57 @@
//
//  CryptographyManager.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace NosSmooth.Cryptography;

/// <summary>
/// A storage of server and client cryptography.
/// </summary>
public class CryptographyManager
{
    /// <summary>
    /// Initializes a new instance of the <see cref="CryptographyManager"/> class.
    /// </summary>
    public CryptographyManager()
    {
        ServerWorld = new ServerWorldCryptography(0);
        ServerLogin = new ServerLoginCryptography();
        ClientLogin = new ClientLoginCryptography();
        ClientWorld = new ClientWorldCryptography(0);
    }

    /// <summary>
    /// Gets the cryptography for server world.
    /// </summary>
    public ICryptography ServerWorld { get; }

    /// <summary>
    /// Gets the cryptography for server login.
    /// </summary>
    public ICryptography ServerLogin { get; }

    /// <summary>
    /// Gets the cryptography for client world.
    /// </summary>
    public ICryptography ClientWorld { get; }

    /// <summary>
    /// Gets the cryptography for client login.
    /// </summary>
    public ICryptography ClientLogin { get; }

    /// <summary>
    /// Gets or sets the encryption key of the connection.
    /// </summary>
    public int EncryptionKey
    {
        get => ((ServerWorldCryptography)ServerWorld).EncryptionKey;
        set
        {
            ((ServerWorldCryptography)ServerWorld).EncryptionKey = value;
            ((ClientWorldCryptography)ClientWorld).EncryptionKey = value;
        }
    }
}
\ No newline at end of file

A Core/NosSmooth.Cryptography/ICryptography.cs => Core/NosSmooth.Cryptography/ICryptography.cs +31 -0
@@ 0,0 1,31 @@
//
//  ICryptography.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Text;

namespace NosSmooth.Cryptography;

/// <summary>
/// An intefrace for NosTale cryptography, encryption, decryption of packets.
/// </summary>
public interface ICryptography
{
    /// <summary>
    /// Decrypt the raw packet (byte array) to a readable list string.
    /// </summary>
    /// <param name="str">Bytes to decrypt.</param>
    /// <param name="encoding">The encoding.</param>
    /// <returns>Decrypted packet to string list.</returns>
    string Decrypt(in ReadOnlySpan<byte> str, Encoding encoding);

    /// <summary>
    /// Encrypt the string packet to byte array.
    /// </summary>
    /// <param name="packet">String to encrypt.</param>
    /// <param name="encoding">The encoding.</param>
    /// <returns>Encrypted packet as byte array.</returns>
    byte[] Encrypt(string packet, Encoding encoding);
}
\ No newline at end of file

A Core/NosSmooth.Cryptography/NosSmooth.Cryptography.csproj => Core/NosSmooth.Cryptography/NosSmooth.Cryptography.csproj +9 -0
@@ 0,0 1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <TargetFramework>net7.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
    </PropertyGroup>

</Project>

A Core/NosSmooth.Cryptography/ServerLoginCryptography.cs => Core/NosSmooth.Cryptography/ServerLoginCryptography.cs +62 -0
@@ 0,0 1,62 @@
//
//  ServerLoginCryptography.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Text;

namespace NosSmooth.Cryptography;

/// <summary>
/// A cryptography used for logging to NosTale server from the server.
/// </summary>
public class ServerLoginCryptography : ICryptography
{
    /// <inheritdoc />
    public string Decrypt(in ReadOnlySpan<byte> str, Encoding encoding)
    {
        try
        {
            string decryptedPacket = string.Empty;

            foreach (byte character in str)
            {
                if (character > 14)
                {
                    decryptedPacket += Convert.ToChar((character - 15) ^ 195);
                }
                else
                {
                    decryptedPacket += Convert.ToChar((256 - (15 - character)) ^ 195);
                }
            }

            return decryptedPacket;
        }
        catch
        {
            return string.Empty;
        }
    }

    /// <inheritdoc />
    public byte[] Encrypt(string packet, Encoding encoding)
    {
        try
        {
            packet += " ";
            byte[] tmp = Encoding.Default.GetBytes(packet);
            for (int i = 0; i < packet.Length; i++)
            {
                tmp[i] = Convert.ToByte(tmp[i] + 15);
            }
            tmp[tmp.Length - 1] = 25;
            return tmp;
        }
        catch
        {
            return Array.Empty<byte>();
        }
    }
}
\ No newline at end of file

A Core/NosSmooth.Cryptography/ServerWorldCryptography.cs => Core/NosSmooth.Cryptography/ServerWorldCryptography.cs +282 -0
@@ 0,0 1,282 @@
//
//  ServerWorldCryptography.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Text;

namespace NosSmooth.Cryptography;

/// <summary>
/// A cryptography used on world server, has to have a session id (encryption key) set from the world.
/// </summary>
public class ServerWorldCryptography : ICryptography
{
    /// <summary>
    /// Initializes a new instance of the <see cref="ServerWorldCryptography"/> class.
    /// </summary>
    /// <param name="encryptionKey">The encryption key.</param>
    public ServerWorldCryptography(int encryptionKey)
    {
        EncryptionKey = encryptionKey;
    }

    /// <summary>
    /// Gets or sets the encryption key.
    /// </summary>
    public int EncryptionKey { get; set; }

    /// <inheritdoc />
    public string Decrypt(in ReadOnlySpan<byte> str, Encoding encoding)
    {
        if (EncryptionKey == 0)
        {
            return DecryptUnauthed(str);
        }

        return DecryptAuthed(str, EncryptionKey, encoding);
    }

    /// <inheritdoc />
    public byte[] Encrypt(string packet, Encoding encoding)
    {
        byte[] strBytes = encoding.GetBytes(packet);
        int bytesLength = strBytes.Length;

        byte[] encryptedData = new byte[bytesLength + (int)Math.Ceiling((decimal)bytesLength / 0x7E) + 1];

        int ii = 0;
        for (int i = 0; i < bytesLength; i++)
        {
            if (i % 0x7E == 0)
            {
                encryptedData[i + ii] = (byte)(bytesLength - i > 0x7E ? 0x7E : bytesLength - i);
                ii++;
            }
            encryptedData[i + ii] = (byte)~strBytes[i];
        }
        encryptedData[encryptedData.Length - 1] = 0xFF;

        return encryptedData;
    }

    private static string DecryptAuthed(in ReadOnlySpan<byte> str, int encryptionKey, Encoding encoding)
    {
        var encryptedString = new StringBuilder();

        int sessionKey = encryptionKey & 0xFF;
        byte sessionNumber = unchecked((byte)(encryptionKey >> 6));
        sessionNumber &= 0xFF;
        sessionNumber &= 3;

        switch (sessionNumber)
        {
            case 0:
                foreach (byte character in str)
                {
                    byte firstbyte = unchecked((byte)(sessionKey + 0x40));
                    byte highbyte = unchecked((byte)(character - firstbyte));
                    encryptedString.Append((char)highbyte);
                }

                break;

            case 1:
                foreach (byte character in str)
                {
                    byte firstbyte = unchecked((byte)(sessionKey + 0x40));
                    byte highbyte = unchecked((byte)(character + firstbyte));
                    encryptedString.Append((char)highbyte);
                }

                break;

            case 2:
                foreach (byte character in str)
                {
                    byte firstbyte = unchecked((byte)(sessionKey + 0x40));
                    byte highbyte = unchecked((byte)(character - firstbyte ^ 0xC3));
                    encryptedString.Append((char)highbyte);
                }

                break;

            case 3:
                foreach (byte character in str)
                {
                    byte firstbyte = unchecked((byte)(sessionKey + 0x40));
                    byte highbyte = unchecked((byte)(character + firstbyte ^ 0xC3));
                    encryptedString.Append((char)highbyte);
                }

                break;

            default:
                encryptedString.Append((char)0xF);
                break;
        }

        string[] temp = encryptedString.ToString().Split((char)0xFF);

        var save = new StringBuilder();

        for (int i = 0; i < temp.Length; i++)
        {
            save.Append(DecryptPrivate(temp[i].AsSpan(), encoding));
            if (i < temp.Length - 2)
            {
                save.Append((char)'\n');
            }
        }

        return save.ToString();
    }

    private static string DecryptPrivate(in ReadOnlySpan<char> str, Encoding encoding)
    {
        using var receiveData = new MemoryStream();
        char[] table = { ' ', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '\n' };
        for (int count = 0; count < str.Length; count++)
        {
            if (str[count] <= 0x7A)
            {
                int len = str[count];

                for (int i = 0; i < len; i++)
                {
                    count++;

                    try
                    {
                        receiveData.WriteByte(unchecked((byte)(str[count] ^ 0xFF)));
                    }
                    catch
                    {
                        receiveData.WriteByte(255);
                    }
                }
            }
            else
            {
                int len = str[count];
                len &= 0x7F;

                for (int i = 0; i < len; i++)
                {
                    count++;
                    int highbyte;
                    try
                    {
                        highbyte = str[count];
                    }
                    catch
                    {
                        highbyte = 0;
                    }

                    highbyte &= 0xF0;
                    highbyte >>= 0x4;

                    int lowbyte;
                    try
                    {
                        lowbyte = str[count];
                    }
                    catch
                    {
                        lowbyte = 0;
                    }

                    lowbyte &= 0x0F;

                    if (highbyte != 0x0 && highbyte != 0xF)
                    {
                        receiveData.WriteByte(unchecked((byte)table[highbyte - 1]));
                        i++;
                    }

                    if (lowbyte != 0x0 && lowbyte != 0xF)
                    {
                        receiveData.WriteByte(unchecked((byte)table[lowbyte - 1]));
                    }
                }
            }
        }

        byte[] tmp = Encoding.Convert(encoding, Encoding.UTF8, receiveData.ToArray());
        return Encoding.UTF8.GetString(tmp);
    }

    private static string DecryptUnauthed(in ReadOnlySpan<byte> str)
    {
        try
        {
            var encryptedStringBuilder = new StringBuilder();
            for (int i = 1; i < str.Length; i++)
            {
                if (Convert.ToChar(str[i]) == 0xE)
                {
                    return encryptedStringBuilder.ToString();
                }

                int firstbyte = Convert.ToInt32(str[i] - 0xF);
                int secondbyte = firstbyte;
                secondbyte &= 240;
                firstbyte = Convert.ToInt32(firstbyte - secondbyte);
                secondbyte >>= 4;

                switch (secondbyte)
                {
                    case 0:
                    case 1:
                        encryptedStringBuilder.Append(' ');
                        break;

                    case 2:
                        encryptedStringBuilder.Append('-');
                        break;

                    case 3:
                        encryptedStringBuilder.Append('.');
                        break;

                    default:
                        secondbyte += 0x2C;
                        encryptedStringBuilder.Append(Convert.ToChar(secondbyte));
                        break;
                }

                switch (firstbyte)
                {
                    case 0:
                        encryptedStringBuilder.Append(' ');
                        break;

                    case 1:
                        encryptedStringBuilder.Append(' ');
                        break;

                    case 2:
                        encryptedStringBuilder.Append('-');
                        break;

                    case 3:
                        encryptedStringBuilder.Append('.');
                        break;

                    default:
                        firstbyte += 0x2C;
                        encryptedStringBuilder.Append(Convert.ToChar(firstbyte));
                        break;
                }
            }

            return encryptedStringBuilder.ToString();
        }
        catch (OverflowException)
        {
            return string.Empty;
        }
    }
}
\ No newline at end of file

M NosSmooth.sln => NosSmooth.sln +32 -0
@@ 54,6 54,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NosSmooth.Extensions.Pathfi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NosSmooth.Game.Tests", "Tests\NosSmooth.Game.Tests\NosSmooth.Game.Tests.csproj", "{21ECBA0F-38FA-45A5-8B42-9A76425204BC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NosSmooth.Cryptography", "Core\NosSmooth.Cryptography\NosSmooth.Cryptography.csproj", "{9035E5AD-8B5F-46CA-B8E1-5273722B392D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Pcap", "Pcap", "{B4224C41-FDB4-426A-83D8-C5AFB4F7D362}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NosSmooth.Pcap", "Pcap\NosSmooth.Pcap\NosSmooth.Pcap.csproj", "{FD185689-EE0F-4403-A1EB-5511D2292CF4}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU


@@ 268,6 274,30 @@ Global
		{21ECBA0F-38FA-45A5-8B42-9A76425204BC}.Release|x64.Build.0 = Release|Any CPU
		{21ECBA0F-38FA-45A5-8B42-9A76425204BC}.Release|x86.ActiveCfg = Release|Any CPU
		{21ECBA0F-38FA-45A5-8B42-9A76425204BC}.Release|x86.Build.0 = Release|Any CPU
		{9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Debug|x64.ActiveCfg = Debug|Any CPU
		{9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Debug|x64.Build.0 = Debug|Any CPU
		{9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Debug|x86.ActiveCfg = Debug|Any CPU
		{9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Debug|x86.Build.0 = Debug|Any CPU
		{9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Release|Any CPU.Build.0 = Release|Any CPU
		{9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Release|x64.ActiveCfg = Release|Any CPU
		{9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Release|x64.Build.0 = Release|Any CPU
		{9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Release|x86.ActiveCfg = Release|Any CPU
		{9035E5AD-8B5F-46CA-B8E1-5273722B392D}.Release|x86.Build.0 = Release|Any CPU
		{FD185689-EE0F-4403-A1EB-5511D2292CF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{FD185689-EE0F-4403-A1EB-5511D2292CF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{FD185689-EE0F-4403-A1EB-5511D2292CF4}.Debug|x64.ActiveCfg = Debug|Any CPU
		{FD185689-EE0F-4403-A1EB-5511D2292CF4}.Debug|x64.Build.0 = Debug|Any CPU
		{FD185689-EE0F-4403-A1EB-5511D2292CF4}.Debug|x86.ActiveCfg = Debug|Any CPU
		{FD185689-EE0F-4403-A1EB-5511D2292CF4}.Debug|x86.Build.0 = Debug|Any CPU
		{FD185689-EE0F-4403-A1EB-5511D2292CF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{FD185689-EE0F-4403-A1EB-5511D2292CF4}.Release|Any CPU.Build.0 = Release|Any CPU
		{FD185689-EE0F-4403-A1EB-5511D2292CF4}.Release|x64.ActiveCfg = Release|Any CPU
		{FD185689-EE0F-4403-A1EB-5511D2292CF4}.Release|x64.Build.0 = Release|Any CPU
		{FD185689-EE0F-4403-A1EB-5511D2292CF4}.Release|x86.ActiveCfg = Release|Any CPU
		{FD185689-EE0F-4403-A1EB-5511D2292CF4}.Release|x86.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE


@@ 290,6 320,8 @@ Global
		{21F7EA0B-5E3C-4016-8ADD-28AF37C00782} = {3A6D13E3-4BBA-4B3D-AE99-BAC8B375F7DF}
		{564CAD6F-09B1-450B-83ED-9BCDE106B646} = {3A6D13E3-4BBA-4B3D-AE99-BAC8B375F7DF}
		{21ECBA0F-38FA-45A5-8B42-9A76425204BC} = {C6A8760D-92CB-4307-88A7-36CCAEBA4AD1}
		{9035E5AD-8B5F-46CA-B8E1-5273722B392D} = {01B5E872-271F-4D30-A1AA-AD48D81840C5}
		{FD185689-EE0F-4403-A1EB-5511D2292CF4} = {B4224C41-FDB4-426A-83D8-C5AFB4F7D362}
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {C5F46653-4DEC-429B-8580-4ED18ED9B4CA}