diff --git a/.gitignore b/.gitignore index 2f667b3d..d5e6857a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ /.vs /JoinFS/Installer/bin /JoinFS/Installer/obj +/JoinFS.Tests/bin +/JoinFS.Tests/obj diff --git a/JoinFS.Tests/JoinFS.Tests.csproj b/JoinFS.Tests/JoinFS.Tests.csproj new file mode 100644 index 00000000..972fd3c4 --- /dev/null +++ b/JoinFS.Tests/JoinFS.Tests.csproj @@ -0,0 +1,21 @@ + + + + net8.0 + enable + enable + false + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JoinFS.Tests/ProgramCode.cs b/JoinFS.Tests/ProgramCode.cs new file mode 100644 index 00000000..f09fdeb9 --- /dev/null +++ b/JoinFS.Tests/ProgramCode.cs @@ -0,0 +1,126 @@ +using System; +using System.Text; + +namespace JoinFS.Tests +{ + /// + /// Copy of the Program.Code method from JoinFS for testing purposes. + /// This allows us to test the encoding/decoding logic independently. + /// + public static class ProgramCode + { + public static string? Code(string? s, bool bToCode, int nKey) + { + if (s == null) + return null; + + const char cLow = '!'; + const char cHigh = '~'; + + const int nRange = (int)cHigh - cLow + 1; + const string csCaesar = @"THEFIVBOXNGWZARDSJUMPQCKLY" + // Low to High, rearranged (will be nRange in length) + @"mywaftvexdzoquipsblchngjrk" + + @"1407329685" + + @"!""#$%&'()*+,-./:;<=>?@" + + @"[\]^_`{|}~"; + + if (s.Length == 0) + return s; + + StringBuilder sb = new StringBuilder(s); + + // find last valid char & trim ignored ones + int nLast = sb.Length - 1; + int nEnd = nLast; + while (nEnd >= 0 && (sb[nEnd] < cLow || sb[nEnd] > cHigh)) + --nEnd; + if (nEnd != nLast) + sb.Remove(nEnd + 1, sb.Length - nEnd - 1); + + // find first valid char & trim ignored ones + nLast = sb.Length - 1; + int nStart = 0; + while (nStart <= nLast && (sb[nStart] < cLow || sb[nStart] > cHigh)) + ++nStart; + if (nStart != 0) + sb.Remove(0, nStart); + + if (sb.Length == 0) + return sb.ToString(); + + nLast = sb.Length - 1; + + // if decoding, reverse-substitute + int nChar; + if (!bToCode) + { + for (int i = 0; i <= nLast; ++i) + { + if (sb[i] < cLow || sb[i] > cHigh) + continue; + + nChar = csCaesar.IndexOf(sb[i]); + + if (nChar >= 0) + sb[i] = (char)((int)cLow + nChar); + } + } + + // mangle + int nInc = 0; + int k; + const int nPasses = 11; + Random rnd = new Random(nKey); + for (int j = 0; j < nPasses; ++j) + { + k = (bToCode) ? j : nPasses - j - 1; + if ((k & 1) == 0) + { nStart = nLast; nEnd = -1; nInc = -1; } // from last downto first + else + { nStart = 0; nEnd = nLast + 1; nInc = 1; } // from first upto last + + for (int i = nStart; i != nEnd; i += nInc) + { + if (sb[i] < cLow || sb[i] > cHigh) + continue; + + nChar = sb[i] - cLow; + + if (bToCode) + nChar = nRange - 1 - nChar; + + if (bToCode) + nChar += rnd.Next(nRange - 1); + else + nChar -= rnd.Next(nRange - 1); + + if (nChar >= nRange) + nChar -= nRange; + else if (nChar < 0) + nChar += nRange; + + if (!bToCode) + nChar = nRange - 1 - nChar; + + sb[i] = (char)(nChar + cLow); + } + } + + // if encoding, substitute + if (bToCode) + { + for (int i = 0; i <= nLast; ++i) + { + if (sb[i] < cLow || sb[i] > cHigh) + continue; + + nChar = sb[i] - cLow; + + sb[i] = csCaesar[nChar]; + } + } + + return sb.ToString(); + } + } +} diff --git a/JoinFS.Tests/ProgramCodeTests.cs b/JoinFS.Tests/ProgramCodeTests.cs new file mode 100644 index 00000000..7e177a80 --- /dev/null +++ b/JoinFS.Tests/ProgramCodeTests.cs @@ -0,0 +1,345 @@ +namespace JoinFS.Tests; + +/// +/// Tests for the Program.Code(...) method which provides encoding/decoding functionality. +/// The Code method is a bidirectional cipher that uses Caesar substitution and random number generation. +/// +public class ProgramCodeTests +{ + // Standard test key for consistency + private const int TestKey = 1234; + + #region Null and Empty String Tests + + [Fact] + public void Code_WithNullString_ReturnsNull() + { + // Arrange + string? input = null; + + // Act + string? encodedResult = ProgramCode.Code(input, true, TestKey); + string? decodedResult = ProgramCode.Code(input, false, TestKey); + + // Assert + Assert.Null(encodedResult); + Assert.Null(decodedResult); + } + + [Fact] + public void Code_WithEmptyString_ReturnsEmptyString() + { + // Arrange + string input = string.Empty; + + // Act + string encodedResult = ProgramCode.Code(input, true, TestKey); + string decodedResult = ProgramCode.Code(input, false, TestKey); + + // Assert + Assert.Equal(string.Empty, encodedResult); + Assert.Equal(string.Empty, decodedResult); + } + + #endregion + + #region Round-Trip Tests + + [Theory] + [InlineData("Hello World")] + [InlineData("Test123")] + [InlineData("https://example.com")] + [InlineData("user@email.com")] + [InlineData("Path/To/File.txt")] + [InlineData("!@#$%^&*()")] + [InlineData("ABC")] + [InlineData("a")] + public void Code_RoundTrip_RestoresOriginalString(string original) + { + // Act + string encoded = ProgramCode.Code(original, true, TestKey); + string decoded = ProgramCode.Code(encoded, false, TestKey); + + // Assert + Assert.NotEqual(original, encoded); // Encoded should be different + Assert.Equal(original, decoded); // Decoded should match original + } + + [Fact] + public void Code_RoundTrip_WithAllPrintableAsciiCharacters() + { + // Arrange - Build a string with all printable ASCII characters (! to ~) + var sb = new System.Text.StringBuilder(); + for (char c = '!'; c <= '~'; c++) + { + sb.Append(c); + } + string original = sb.ToString(); + + // Act + string encoded = ProgramCode.Code(original, true, TestKey); + string decoded = ProgramCode.Code(encoded, false, TestKey); + + // Assert + Assert.NotEqual(original, encoded); + Assert.Equal(original, decoded); + } + + #endregion + + #region Key Sensitivity Tests + + [Fact] + public void Code_DifferentKeys_ProduceDifferentEncodings() + { + // Arrange + string input = "Test String"; + int key1 = 1234; + int key2 = 5678; + + // Act + string encoded1 = ProgramCode.Code(input, true, key1); + string encoded2 = ProgramCode.Code(input, true, key2); + + // Assert + Assert.NotEqual(encoded1, encoded2); + } + + [Fact] + public void Code_WrongKey_DoesNotRestoreOriginal() + { + // Arrange + string input = "Secret Message"; + int encodeKey = 1234; + int wrongKey = 5678; + + // Act + string encoded = ProgramCode.Code(input, true, encodeKey); + string decodedWithWrongKey = ProgramCode.Code(encoded, false, wrongKey); + + // Assert + Assert.NotEqual(input, decodedWithWrongKey); + } + + [Fact] + public void Code_SameKeyUsedTwice_RestoresOriginal() + { + // Arrange + string input = "Consistent Key Test"; + int key = 9999; + + // Act + string encoded = ProgramCode.Code(input, true, key); + string decoded = ProgramCode.Code(encoded, false, key); + + // Assert + Assert.Equal(input, decoded); + } + + #endregion + + #region Special Character Tests + + [Theory] + [InlineData("!")] + [InlineData("~")] + [InlineData("!~")] + [InlineData("!!!")] + [InlineData("~~~")] + public void Code_WithBoundaryCharacters_RoundTripsCorrectly(string input) + { + // Act + string encoded = ProgramCode.Code(input, true, TestKey); + string decoded = ProgramCode.Code(encoded, false, TestKey); + + // Assert + Assert.Equal(input, decoded); + } + + [Fact] + public void Code_WithMixedSpecialCharacters_RoundTripsCorrectly() + { + // Arrange + string input = "Hello!@#$%^&*()_+-=[]{}|;':\",./<>?`~World"; + + // Act + string encoded = ProgramCode.Code(input, true, TestKey); + string decoded = ProgramCode.Code(encoded, false, TestKey); + + // Assert + Assert.Equal(input, decoded); + } + + #endregion + + #region Edge Cases with Invalid Characters + + [Fact] + public void Code_WithOnlyInvalidCharacters_ReturnsEmptyString() + { + // Arrange - Characters outside the valid range (! to ~) + string input = "\t\n\r"; + + // Act + string encoded = ProgramCode.Code(input, true, TestKey); + + // Assert + Assert.Equal(string.Empty, encoded); + } + + [Fact] + public void Code_WithInvalidCharactersAtBoundaries_TrimsCorrectly() + { + // Arrange - Invalid chars at start and end + string input = "\t\nHello World\r\n"; + string expectedTrimmed = "Hello World"; + + // Act + string encoded = ProgramCode.Code(input, true, TestKey); + string decoded = ProgramCode.Code(encoded, false, TestKey); + + // Assert + Assert.Equal(expectedTrimmed, decoded); + } + + #endregion + + #region Encoding/Decoding Properties + + [Fact] + public void Code_Encoding_ProducesDifferentOutput() + { + // Arrange + string input = "This should be different"; + + // Act + string encoded = ProgramCode.Code(input, true, TestKey); + + // Assert + Assert.NotEqual(input, encoded); + Assert.NotEmpty(encoded); + } + + [Fact] + public void Code_EncodingSameStringTwice_ProducesSameResult() + { + // Arrange + string input = "Deterministic Test"; + + // Act + string encoded1 = ProgramCode.Code(input, true, TestKey); + string encoded2 = ProgramCode.Code(input, true, TestKey); + + // Assert + Assert.Equal(encoded1, encoded2); + } + + [Fact] + public void Code_EncodedLength_MatchesValidCharacterCount() + { + // Arrange + string input = "Test"; + + // Act + string encoded = ProgramCode.Code(input, true, TestKey); + + // Assert + // The encoded string should have the same length as the original + // (assuming all characters are valid) + Assert.Equal(input.Length, encoded.Length); + } + + #endregion + + #region Multiple Round-Trips + + [Fact] + public void Code_MultipleRoundTrips_MaintainsIntegrity() + { + // Arrange + string original = "Multiple Round Trip Test"; + + // Act & Assert - Perform 5 round trips + string current = original; + for (int i = 0; i < 5; i++) + { + string encoded = ProgramCode.Code(current, true, TestKey); + string decoded = ProgramCode.Code(encoded, false, TestKey); + Assert.Equal(current, decoded); + current = decoded; + } + + Assert.Equal(original, current); + } + + #endregion + + #region Real-World Usage Scenarios + + [Fact] + public void Code_WithUrl_RoundTripsCorrectly() + { + // Arrange + string url = "https://joinfs.net/download/file.zip"; + + // Act + string encoded = ProgramCode.Code(url, true, TestKey); + string decoded = ProgramCode.Code(encoded, false, TestKey); + + // Assert + Assert.Equal(url, decoded); + } + + [Fact] + public void Code_WithFilePath_RoundTripsCorrectly() + { + // Arrange + string path = "C:/Program Files/JoinFS/config.xml"; + + // Act + string encoded = ProgramCode.Code(path, true, TestKey); + string decoded = ProgramCode.Code(encoded, false, TestKey); + + // Assert + Assert.Equal(path, decoded); + } + + [Fact] + public void Code_WithLongString_RoundTripsCorrectly() + { + // Arrange + string longString = new string('A', 1000) + " test " + new string('Z', 1000); + + // Act + string encoded = ProgramCode.Code(longString, true, TestKey); + string decoded = ProgramCode.Code(encoded, false, TestKey); + + // Assert + Assert.Equal(longString, decoded); + } + + #endregion + + #region Key Value Tests + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + [InlineData(int.MaxValue)] + [InlineData(int.MinValue)] + public void Code_WithVariousKeys_RoundTripsCorrectly(int key) + { + // Arrange + string input = "Key variation test"; + + // Act + string encoded = ProgramCode.Code(input, true, key); + string decoded = ProgramCode.Code(encoded, false, key); + + // Assert + Assert.Equal(input, decoded); + } + + #endregion +} diff --git a/JoinFS.Tests/README.md b/JoinFS.Tests/README.md new file mode 100644 index 00000000..a1db1a83 --- /dev/null +++ b/JoinFS.Tests/README.md @@ -0,0 +1,82 @@ +# JoinFS Tests + +This test project contains comprehensive unit tests for the JoinFS application. + +## Test Coverage + +### Program.Code(...) Method + +The `Program.Code(...)` method is a bidirectional cipher that implements encoding and decoding functionality using: +- Caesar substitution cipher +- Pseudo-random number generation with seeded keys +- Character range validation (printable ASCII characters from '!' to '~') + +#### Test Categories + +1. **Null and Empty String Tests** + - Validates handling of null inputs + - Validates handling of empty strings + +2. **Round-Trip Tests** + - Ensures encoding followed by decoding restores the original string + - Tests various common input patterns (URLs, file paths, email addresses, etc.) + - Tests all printable ASCII characters + +3. **Key Sensitivity Tests** + - Verifies different keys produce different encodings + - Confirms wrong keys cannot decode messages + - Validates key consistency + +4. **Special Character Tests** + - Tests boundary characters ('!' and '~') + - Tests mixed special character strings + +5. **Edge Cases with Invalid Characters** + - Handles characters outside the valid range + - Properly trims invalid characters at boundaries + +6. **Encoding/Decoding Properties** + - Verifies encoding produces different output + - Confirms deterministic behavior (same input + key = same output) + - Validates encoded string length + +7. **Multiple Round-Trips** + - Ensures data integrity over multiple encode/decode cycles + +8. **Real-World Usage Scenarios** + - Tests with URLs, file paths, and long strings + +9. **Key Value Tests** + - Tests with various key values including edge cases (0, negative, max/min int) + +## Running Tests + +To run all tests: +```bash +cd JoinFS.Tests +dotnet test +``` + +To run tests with verbose output: +```bash +dotnet test --verbosity normal +``` + +To run tests with coverage: +```bash +dotnet test --collect:"XPlat Code Coverage" +``` + +## Test Results + +All 34 tests pass successfully, providing comprehensive coverage of the `Program.Code(...)` method's functionality including: +- Normal operation +- Edge cases +- Error conditions +- Round-trip encoding/decoding +- Key sensitivity +- Character range handling + +## Implementation Note + +The test project includes a copy of the `Program.Code` method in `ProgramCode.cs` to avoid circular dependencies with the main Windows Forms application. This ensures the tests can run independently on any platform. diff --git a/JoinFS.sln b/JoinFS.sln index c9763b68..eddd7cc1 100644 --- a/JoinFS.sln +++ b/JoinFS.sln @@ -23,70 +23,210 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utils", "Utils", "{E6679DD8 util\version.txt = util\version.txt EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JoinFS.Tests", "JoinFS.Tests\JoinFS.Tests.csproj", "{6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CONSOLE|x64 = CONSOLE|x64 CONSOLE|x86 = CONSOLE|x86 + CONSOLE|Any CPU = CONSOLE|Any CPU CONSOLE-Debug|x64 = CONSOLE-Debug|x64 CONSOLE-Debug|x86 = CONSOLE-Debug|x86 + CONSOLE-Debug|Any CPU = CONSOLE-Debug|Any CPU FS2020|x64 = FS2020|x64 FS2020|x86 = FS2020|x86 + FS2020|Any CPU = FS2020|Any CPU FS2020-Debug|x64 = FS2020-Debug|x64 FS2020-Debug|x86 = FS2020-Debug|x86 + FS2020-Debug|Any CPU = FS2020-Debug|Any CPU FS2024|x64 = FS2024|x64 FS2024|x86 = FS2024|x86 + FS2024|Any CPU = FS2024|Any CPU FS2024-Debug|x64 = FS2024-Debug|x64 FS2024-Debug|x86 = FS2024-Debug|x86 + FS2024-Debug|Any CPU = FS2024-Debug|Any CPU FSX|x64 = FSX|x64 FSX|x86 = FSX|x86 + FSX|Any CPU = FSX|Any CPU FSX-Debug|x64 = FSX-Debug|x64 FSX-Debug|x86 = FSX-Debug|x86 + FSX-Debug|Any CPU = FSX-Debug|Any CPU P3D|x64 = P3D|x64 P3D|x86 = P3D|x86 + P3D|Any CPU = P3D|Any CPU P3D-Debug|x64 = P3D-Debug|x64 P3D-Debug|x86 = P3D-Debug|x86 + P3D-Debug|Any CPU = P3D-Debug|Any CPU XPLANE|x64 = XPLANE|x64 XPLANE|x86 = XPLANE|x86 + XPLANE|Any CPU = XPLANE|Any CPU XPLANE-Debug|x64 = XPLANE-Debug|x64 XPLANE-Debug|x86 = XPLANE-Debug|x86 + XPLANE-Debug|Any CPU = XPLANE-Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Debug|Any CPU = Debug|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {91153E12-DEC1-1343-F4BF-108E45E61261}.CONSOLE|x64.ActiveCfg = CONSOLE|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.CONSOLE|x64.Build.0 = CONSOLE|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.CONSOLE|x86.ActiveCfg = CONSOLE|x86 + {91153E12-DEC1-1343-F4BF-108E45E61261}.CONSOLE|Any CPU.ActiveCfg = CONSOLE|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.CONSOLE|Any CPU.Build.0 = CONSOLE|Any CPU {91153E12-DEC1-1343-F4BF-108E45E61261}.CONSOLE-Debug|x64.ActiveCfg = CONSOLE-Debug|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.CONSOLE-Debug|x64.Build.0 = CONSOLE-Debug|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.CONSOLE-Debug|x86.ActiveCfg = CONSOLE-Debug|x86 + {91153E12-DEC1-1343-F4BF-108E45E61261}.CONSOLE-Debug|Any CPU.ActiveCfg = CONSOLE-Debug|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.CONSOLE-Debug|Any CPU.Build.0 = CONSOLE-Debug|Any CPU {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2020|x64.ActiveCfg = FS2020|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2020|x64.Build.0 = FS2020|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2020|x86.ActiveCfg = FS2020|x86 + {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2020|Any CPU.ActiveCfg = FS2020|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2020|Any CPU.Build.0 = FS2020|Any CPU {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2020-Debug|x64.ActiveCfg = FS2020-Debug|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2020-Debug|x64.Build.0 = FS2020-Debug|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2020-Debug|x86.ActiveCfg = FS2020-Debug|x86 + {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2020-Debug|Any CPU.ActiveCfg = FS2020-Debug|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2020-Debug|Any CPU.Build.0 = FS2020-Debug|Any CPU {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2024|x64.ActiveCfg = FS2024|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2024|x64.Build.0 = FS2024|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2024|x86.ActiveCfg = FS2024|x86 + {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2024|Any CPU.ActiveCfg = FS2024|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2024|Any CPU.Build.0 = FS2024|Any CPU {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2024-Debug|x64.ActiveCfg = FS2024-Debug|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2024-Debug|x64.Build.0 = FS2024-Debug|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2024-Debug|x86.ActiveCfg = FS2024-Debug|x86 + {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2024-Debug|Any CPU.ActiveCfg = FS2024-Debug|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.FS2024-Debug|Any CPU.Build.0 = FS2024-Debug|Any CPU {91153E12-DEC1-1343-F4BF-108E45E61261}.FSX|x64.ActiveCfg = FSX|x86 {91153E12-DEC1-1343-F4BF-108E45E61261}.FSX|x86.ActiveCfg = FSX|x86 {91153E12-DEC1-1343-F4BF-108E45E61261}.FSX|x86.Build.0 = FSX|x86 + {91153E12-DEC1-1343-F4BF-108E45E61261}.FSX|Any CPU.ActiveCfg = FSX|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.FSX|Any CPU.Build.0 = FSX|Any CPU {91153E12-DEC1-1343-F4BF-108E45E61261}.FSX-Debug|x64.ActiveCfg = FSX-Debug|x86 {91153E12-DEC1-1343-F4BF-108E45E61261}.FSX-Debug|x86.ActiveCfg = FSX-Debug|x86 {91153E12-DEC1-1343-F4BF-108E45E61261}.FSX-Debug|x86.Build.0 = FSX-Debug|x86 + {91153E12-DEC1-1343-F4BF-108E45E61261}.FSX-Debug|Any CPU.ActiveCfg = FSX-Debug|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.FSX-Debug|Any CPU.Build.0 = FSX-Debug|Any CPU {91153E12-DEC1-1343-F4BF-108E45E61261}.P3D|x64.ActiveCfg = FSX|x86 {91153E12-DEC1-1343-F4BF-108E45E61261}.P3D|x86.ActiveCfg = P3D|x86 {91153E12-DEC1-1343-F4BF-108E45E61261}.P3D|x86.Build.0 = P3D|x86 + {91153E12-DEC1-1343-F4BF-108E45E61261}.P3D|Any CPU.ActiveCfg = P3D|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.P3D|Any CPU.Build.0 = P3D|Any CPU {91153E12-DEC1-1343-F4BF-108E45E61261}.P3D-Debug|x64.ActiveCfg = FSX-Debug|x86 {91153E12-DEC1-1343-F4BF-108E45E61261}.P3D-Debug|x86.ActiveCfg = FSX-Debug|x86 {91153E12-DEC1-1343-F4BF-108E45E61261}.P3D-Debug|x86.Build.0 = FSX-Debug|x86 + {91153E12-DEC1-1343-F4BF-108E45E61261}.P3D-Debug|Any CPU.ActiveCfg = P3D-Debug|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.P3D-Debug|Any CPU.Build.0 = P3D-Debug|Any CPU {91153E12-DEC1-1343-F4BF-108E45E61261}.XPLANE|x64.ActiveCfg = XPLANE|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.XPLANE|x64.Build.0 = XPLANE|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.XPLANE|x86.ActiveCfg = XPLANE|x86 + {91153E12-DEC1-1343-F4BF-108E45E61261}.XPLANE|Any CPU.ActiveCfg = XPLANE|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.XPLANE|Any CPU.Build.0 = XPLANE|Any CPU {91153E12-DEC1-1343-F4BF-108E45E61261}.XPLANE-Debug|x64.ActiveCfg = XPLANE-Debug|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.XPLANE-Debug|x64.Build.0 = XPLANE-Debug|x64 {91153E12-DEC1-1343-F4BF-108E45E61261}.XPLANE-Debug|x86.ActiveCfg = XPLANE-Debug|x86 + {91153E12-DEC1-1343-F4BF-108E45E61261}.XPLANE-Debug|Any CPU.ActiveCfg = XPLANE-Debug|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.XPLANE-Debug|Any CPU.Build.0 = XPLANE-Debug|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.Debug|x64.ActiveCfg = Debug|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.Debug|x64.Build.0 = Debug|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.Debug|x86.ActiveCfg = Debug|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.Debug|x86.Build.0 = Debug|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.Release|x64.ActiveCfg = Release|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.Release|x64.Build.0 = Release|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.Release|x86.ActiveCfg = Release|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.Release|x86.Build.0 = Release|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91153E12-DEC1-1343-F4BF-108E45E61261}.Release|Any CPU.Build.0 = Release|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.CONSOLE|x64.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.CONSOLE|x64.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.CONSOLE|x86.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.CONSOLE|x86.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.CONSOLE|Any CPU.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.CONSOLE|Any CPU.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.CONSOLE-Debug|x64.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.CONSOLE-Debug|x64.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.CONSOLE-Debug|x86.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.CONSOLE-Debug|x86.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.CONSOLE-Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.CONSOLE-Debug|Any CPU.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2020|x64.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2020|x64.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2020|x86.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2020|x86.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2020|Any CPU.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2020|Any CPU.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2020-Debug|x64.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2020-Debug|x64.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2020-Debug|x86.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2020-Debug|x86.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2020-Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2020-Debug|Any CPU.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2024|x64.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2024|x64.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2024|x86.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2024|x86.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2024|Any CPU.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2024|Any CPU.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2024-Debug|x64.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2024-Debug|x64.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2024-Debug|x86.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2024-Debug|x86.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2024-Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FS2024-Debug|Any CPU.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FSX|x64.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FSX|x64.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FSX|x86.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FSX|x86.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FSX|Any CPU.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FSX|Any CPU.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FSX-Debug|x64.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FSX-Debug|x64.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FSX-Debug|x86.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FSX-Debug|x86.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FSX-Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.FSX-Debug|Any CPU.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.P3D|x64.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.P3D|x64.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.P3D|x86.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.P3D|x86.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.P3D|Any CPU.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.P3D|Any CPU.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.P3D-Debug|x64.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.P3D-Debug|x64.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.P3D-Debug|x86.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.P3D-Debug|x86.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.P3D-Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.P3D-Debug|Any CPU.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.XPLANE|x64.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.XPLANE|x64.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.XPLANE|x86.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.XPLANE|x86.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.XPLANE|Any CPU.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.XPLANE|Any CPU.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.XPLANE-Debug|x64.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.XPLANE-Debug|x64.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.XPLANE-Debug|x86.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.XPLANE-Debug|x86.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.XPLANE-Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.XPLANE-Debug|Any CPU.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.Debug|x64.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.Debug|x64.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.Debug|x86.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.Debug|x86.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.Release|x64.ActiveCfg = Release|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.Release|x64.Build.0 = Release|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.Release|x86.ActiveCfg = Release|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.Release|x86.Build.0 = Release|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BFEC8A5-2FA0-4E77-AC0F-7B57D91D1882}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE