diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..9eae7c3
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,53 @@
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+ workflow_dispatch:
+
+env:
+ DOTNET_NOLOGO: true
+ DOTNET_CLI_TELEMETRY_OPTOUT: true
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
+
+jobs:
+ build-and-test:
+ name: Build & Test (.NET 8, Windows)
+ runs-on: windows-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup .NET 8
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Cache NuGet packages
+ uses: actions/cache@v4
+ with:
+ path: ~/.nuget/packages
+ key: ${{ runner.os }}-nuget-${{ hashFiles('src/**/*.csproj') }}
+ restore-keys: |
+ ${{ runner.os }}-nuget-
+
+ - name: Restore
+ run: dotnet restore src/SplitWireTurkey.sln
+
+ - name: Build (Release)
+ run: dotnet build src/SplitWireTurkey.sln --configuration Release --no-restore
+
+ - name: Test
+ run: dotnet test src/SplitWireTurkey.Tests/SplitWireTurkey.Tests.csproj --configuration Release --no-build --logger "trx;LogFileName=test-results.trx" --results-directory ./TestResults
+
+ - name: Upload test results
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: test-results
+ path: ./TestResults/**/*.trx
+ retention-days: 14
+ if-no-files-found: ignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ec215f4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,35 @@
+# Build outputs
+[Bb]in/
+[Oo]bj/
+[Pp]ublish/
+[Dd]ebug/
+[Rr]elease/
+*.user
+*.suo
+*.userprefs
+
+# Test results
+TestResults/
+coverage*.xml
+*.trx
+*.coverage
+*.coveragexml
+
+# NuGet
+.nuget/
+*.nupkg
+*.snupkg
+
+# Visual Studio / Rider
+.vs/
+.vscode/
+.idea/
+*.sln.docstates
+
+# OS
+Thumbs.db
+.DS_Store
+desktop.ini
+
+# Logs
+*.log
diff --git a/README.md b/README.md
index 7b78660..a583059 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,8 @@
[](https://github.com/cagritaskn/SplitWire-Turkey/blob/main/.github/README_RU.md)
[](https://github.com/cagritaskn/SplitWire-Turkey/blob/main/.github/README_ES.md)
+[](https://github.com/cagritaskn/SplitWire-Turkey/actions/workflows/ci.yml)
+
# SplitWire-Turkey
@@ -329,6 +331,18 @@ Gereksinimler:
..\build_simple.bat
```
+4. **Testleri Çalıştırın (opsiyonel)**
+ ```bash
+ # Çözüm dosyasından
+ dotnet test ..\SplitWireTurkey.sln -c Release
+
+ # Sadece test projesi
+ dotnet test ..\SplitWireTurkey.Tests\SplitWireTurkey.Tests.csproj
+ ```
+ `VersionHelper` ve `LanguageManager` davranışı için xUnit testleri
+ `src/SplitWireTurkey.Tests/` altındadır. Aynı paketler GitHub Actions'taki
+ CI workflow'u tarafından her PR'da otomatik olarak çalıştırılır.
+
### InnoSetup Kullanarak Kurulum Yürütülebilirini Tekrar Derleme
Gereksinimler:
- **InnoSetup 6**
diff --git a/src/SplitWireTurkey.Tests/LanguageManagerTests.cs b/src/SplitWireTurkey.Tests/LanguageManagerTests.cs
new file mode 100644
index 0000000..42b391d
--- /dev/null
+++ b/src/SplitWireTurkey.Tests/LanguageManagerTests.cs
@@ -0,0 +1,109 @@
+using SplitWireTurkey;
+using Xunit;
+
+namespace SplitWireTurkey.Tests
+{
+ ///
+ /// LanguageManager is a static class — all tests live in a single
+ /// non-parallel collection so the shared state is deterministic.
+ ///
+ [Collection("LanguageManager")]
+ public class LanguageManagerTests
+ {
+ [Fact]
+ public void LoadLanguage_WithSupportedCode_ReturnsTrue()
+ {
+ Assert.True(LanguageManager.LoadLanguage("TR"));
+ Assert.Equal("TR", LanguageManager.CurrentLanguage);
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ [InlineData(" ")]
+ public void LoadLanguage_WithNullOrBlank_ReturnsFalseAndDoesNotThrow(string code)
+ {
+ // Must never throw NullReferenceException / ArgumentNullException.
+ var result = LanguageManager.LoadLanguage(code);
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void LoadLanguage_WithUnknownCode_FallsBackAndStillExposesKeys()
+ {
+ // Unknown language file does not exist on disk, but the
+ // Turkish fallback dictionary should still resolve common keys.
+ var ok = LanguageManager.LoadLanguage("XX");
+ Assert.True(ok);
+
+ var translated = LanguageManager.GetText("buttons", "exit");
+ Assert.NotEqual("buttons.exit", translated);
+ Assert.False(string.IsNullOrWhiteSpace(translated));
+ }
+
+ [Fact]
+ public void GetText_TopLevelKey_ReturnsLocalizedString()
+ {
+ LanguageManager.LoadLanguage("EN");
+ // No top-level scalar keys exist by default, so this also
+ // verifies that an unknown flat key returns the key itself.
+ Assert.Equal("nonexistent_top_level_key",
+ LanguageManager.GetText("nonexistent_top_level_key"));
+ }
+
+ [Fact]
+ public void GetText_NestedKey_ReturnsLocalizedString_ForTurkish()
+ {
+ LanguageManager.LoadLanguage("TR");
+
+ Assert.Equal("WireSock", LanguageManager.GetText("tabs", "main"));
+ Assert.Equal("Çıkış", LanguageManager.GetText("buttons", "exit"));
+ }
+
+ [Fact]
+ public void GetText_NestedKey_ReturnsLocalizedString_ForEnglish()
+ {
+ LanguageManager.LoadLanguage("EN");
+
+ Assert.Equal("Exit", LanguageManager.GetText("buttons", "exit"));
+ Assert.Equal("Administrator Privileges Required",
+ LanguageManager.GetText("messages", "admin_required_title"));
+ }
+
+ [Fact]
+ public void GetText_UnknownNestedKey_ReturnsCategoryDotKey()
+ {
+ LanguageManager.LoadLanguage("TR");
+
+ Assert.Equal("buttons.does_not_exist",
+ LanguageManager.GetText("buttons", "does_not_exist"));
+ Assert.Equal("no_such_category.foo",
+ LanguageManager.GetText("no_such_category", "foo"));
+ }
+
+ [Fact]
+ public void GetText_NullKey_ReturnsNull()
+ {
+ LanguageManager.LoadLanguage("TR");
+
+ Assert.Null(LanguageManager.GetText(key: null));
+ Assert.Null(LanguageManager.GetText(category: null, key: "x"));
+ Assert.Null(LanguageManager.GetText(category: "x", key: null));
+ }
+
+ [Fact]
+ public void GetText_FormatsArguments()
+ {
+ // Use a key that exists in TR and contains a {0} placeholder.
+ // messages.unexpected_error_message contains "Beklenmeyen bir hata oluştu:\n{0}".
+ LanguageManager.LoadLanguage("TR");
+
+ var formatted = LanguageManager.GetText("messages", "unexpected_error_message", "boom");
+ Assert.Contains("boom", formatted);
+ Assert.DoesNotContain("{0}", formatted);
+ }
+ }
+
+ [CollectionDefinition("LanguageManager", DisableParallelization = true)]
+ public class LanguageManagerCollection { }
+}
diff --git a/src/SplitWireTurkey.Tests/SplitWireTurkey.Tests.csproj b/src/SplitWireTurkey.Tests/SplitWireTurkey.Tests.csproj
new file mode 100644
index 0000000..984cf37
--- /dev/null
+++ b/src/SplitWireTurkey.Tests/SplitWireTurkey.Tests.csproj
@@ -0,0 +1,39 @@
+
+
+
+ net8.0-windows
+ true
+ true
+ false
+ true
+ SplitWireTurkey.Tests
+ SplitWireTurkey.Tests
+ disable
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
+
+ res\Languages\%(Filename)%(Extension)
+ PreserveNewest
+
+
+
+
diff --git a/src/SplitWireTurkey.Tests/VersionHelperTests.cs b/src/SplitWireTurkey.Tests/VersionHelperTests.cs
new file mode 100644
index 0000000..1e99cc2
--- /dev/null
+++ b/src/SplitWireTurkey.Tests/VersionHelperTests.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Text.RegularExpressions;
+using SplitWireTurkey;
+using Xunit;
+
+namespace SplitWireTurkey.Tests
+{
+ public class VersionHelperTests
+ {
+ private static readonly Regex VersionRegex = new(@"^\d+\.\d+\.\d+(\.\d+)?", RegexOptions.Compiled);
+
+ [Fact]
+ public void GetAssemblyVersion_ReturnsParsableVersion()
+ {
+ var version = VersionHelper.GetAssemblyVersion();
+
+ Assert.False(string.IsNullOrWhiteSpace(version));
+ Assert.True(Version.TryParse(version, out _), $"Not a valid Version: {version}");
+ }
+
+ [Fact]
+ public void GetFileVersion_ReturnsParsableVersion()
+ {
+ var version = VersionHelper.GetFileVersion();
+
+ Assert.False(string.IsNullOrWhiteSpace(version));
+ Assert.True(Version.TryParse(version, out _), $"Not a valid Version: {version}");
+ }
+
+ [Fact]
+ public void GetProductVersion_StartsWithSemverLikePrefix()
+ {
+ var version = VersionHelper.GetProductVersion();
+
+ Assert.False(string.IsNullOrWhiteSpace(version));
+ Assert.Matches(VersionRegex, version);
+ }
+
+ [Fact]
+ public void AllAccessors_AreDeterministic()
+ {
+ Assert.Equal(VersionHelper.GetAssemblyVersion(), VersionHelper.GetAssemblyVersion());
+ Assert.Equal(VersionHelper.GetFileVersion(), VersionHelper.GetFileVersion());
+ Assert.Equal(VersionHelper.GetProductVersion(), VersionHelper.GetProductVersion());
+ }
+ }
+}
diff --git a/src/SplitWireTurkey.Tests/xunit.runner.json b/src/SplitWireTurkey.Tests/xunit.runner.json
new file mode 100644
index 0000000..4b7ee28
--- /dev/null
+++ b/src/SplitWireTurkey.Tests/xunit.runner.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
+ "parallelizeAssembly": false,
+ "parallelizeTestCollections": false,
+ "methodDisplay": "method"
+}
diff --git a/src/SplitWireTurkey.sln b/src/SplitWireTurkey.sln
new file mode 100644
index 0000000..06114aa
--- /dev/null
+++ b/src/SplitWireTurkey.sln
@@ -0,0 +1,48 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SplitWireTurkey", "SplitWireTurkey\SplitWireTurkey.csproj", "{CDE4D0B2-5B4F-4257-A177-D2C0BBD47E9C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SplitWireTurkey.Tests", "SplitWireTurkey.Tests\SplitWireTurkey.Tests.csproj", "{E46300A8-529E-4A1F-A244-D88C80B3531A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CDE4D0B2-5B4F-4257-A177-D2C0BBD47E9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CDE4D0B2-5B4F-4257-A177-D2C0BBD47E9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CDE4D0B2-5B4F-4257-A177-D2C0BBD47E9C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CDE4D0B2-5B4F-4257-A177-D2C0BBD47E9C}.Debug|x64.Build.0 = Debug|Any CPU
+ {CDE4D0B2-5B4F-4257-A177-D2C0BBD47E9C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CDE4D0B2-5B4F-4257-A177-D2C0BBD47E9C}.Debug|x86.Build.0 = Debug|Any CPU
+ {CDE4D0B2-5B4F-4257-A177-D2C0BBD47E9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CDE4D0B2-5B4F-4257-A177-D2C0BBD47E9C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CDE4D0B2-5B4F-4257-A177-D2C0BBD47E9C}.Release|x64.ActiveCfg = Release|Any CPU
+ {CDE4D0B2-5B4F-4257-A177-D2C0BBD47E9C}.Release|x64.Build.0 = Release|Any CPU
+ {CDE4D0B2-5B4F-4257-A177-D2C0BBD47E9C}.Release|x86.ActiveCfg = Release|Any CPU
+ {CDE4D0B2-5B4F-4257-A177-D2C0BBD47E9C}.Release|x86.Build.0 = Release|Any CPU
+ {E46300A8-529E-4A1F-A244-D88C80B3531A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E46300A8-529E-4A1F-A244-D88C80B3531A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E46300A8-529E-4A1F-A244-D88C80B3531A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E46300A8-529E-4A1F-A244-D88C80B3531A}.Debug|x64.Build.0 = Debug|Any CPU
+ {E46300A8-529E-4A1F-A244-D88C80B3531A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E46300A8-529E-4A1F-A244-D88C80B3531A}.Debug|x86.Build.0 = Debug|Any CPU
+ {E46300A8-529E-4A1F-A244-D88C80B3531A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E46300A8-529E-4A1F-A244-D88C80B3531A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E46300A8-529E-4A1F-A244-D88C80B3531A}.Release|x64.ActiveCfg = Release|Any CPU
+ {E46300A8-529E-4A1F-A244-D88C80B3531A}.Release|x64.Build.0 = Release|Any CPU
+ {E46300A8-529E-4A1F-A244-D88C80B3531A}.Release|x86.ActiveCfg = Release|Any CPU
+ {E46300A8-529E-4A1F-A244-D88C80B3531A}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/SplitWireTurkey/LanguageManager.cs b/src/SplitWireTurkey/LanguageManager.cs
index f582e45..15dacda 100644
--- a/src/SplitWireTurkey/LanguageManager.cs
+++ b/src/SplitWireTurkey/LanguageManager.cs
@@ -10,8 +10,11 @@ namespace SplitWireTurkey
///
public static class LanguageManager
{
+ private const string FallbackLanguage = "TR";
+
private static Dictionary _currentTranslations = new Dictionary();
- private static string _currentLanguage = "TR";
+ private static Dictionary _fallbackTranslations = new Dictionary();
+ private static string _currentLanguage = FallbackLanguage;
///
/// Mevcut dil
@@ -23,30 +26,32 @@ public static class LanguageManager
///
public static bool LoadLanguage(string languageCode)
{
+ if (string.IsNullOrWhiteSpace(languageCode))
+ {
+ return false;
+ }
+
try
{
_currentLanguage = languageCode;
-
- var languagePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "res", "Languages", $"{languageCode.ToLower()}.json");
-
- if (!File.Exists(languagePath))
+
+ // Always keep the Turkish dictionary in memory as a fallback for
+ // missing keys when a partial translation is loaded.
+ _fallbackTranslations = LoadDictionary(FallbackLanguage) ?? new Dictionary();
+
+ var loaded = LoadDictionary(languageCode);
+ if (loaded == null && !string.Equals(languageCode, FallbackLanguage, StringComparison.OrdinalIgnoreCase))
{
- // Fallback olarak TR dilini dene
- if (languageCode != "TR")
- {
- languagePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "res", "Languages", "tr.json");
- }
-
- if (!File.Exists(languagePath))
- {
- return false;
- }
+ loaded = _fallbackTranslations;
}
- var jsonContent = File.ReadAllText(languagePath);
- _currentTranslations = JsonSerializer.Deserialize>(jsonContent);
-
- return _currentTranslations != null;
+ if (loaded == null)
+ {
+ return false;
+ }
+
+ _currentTranslations = loaded;
+ return true;
}
catch (Exception ex)
{
@@ -55,41 +60,38 @@ public static bool LoadLanguage(string languageCode)
}
}
+ private static Dictionary LoadDictionary(string languageCode)
+ {
+ var path = Path.Combine(
+ AppDomain.CurrentDomain.BaseDirectory,
+ "res",
+ "Languages",
+ $"{languageCode.ToLower()}.json");
+
+ if (!File.Exists(path))
+ {
+ return null;
+ }
+
+ var json = File.ReadAllText(path);
+ return JsonSerializer.Deserialize>(json);
+ }
+
///
/// Çeviri metnini alır
///
public static string GetText(string key, params object[] args)
{
- try
+ if (key == null)
{
- if (_currentTranslations == null || !_currentTranslations.ContainsKey(key))
- {
- return key; // Anahtar bulunamazsa anahtarı döndür
- }
+ return null;
+ }
- var value = _currentTranslations[key];
-
- if (value is JsonElement element)
+ try
+ {
+ if (TryResolve(_currentTranslations, key, args, out var text)
+ || TryResolve(_fallbackTranslations, key, args, out text))
{
- var text = element.GetString();
- if (string.IsNullOrEmpty(text))
- {
- return key;
- }
-
- // String.Format benzeri işlem
- if (args != null && args.Length > 0)
- {
- try
- {
- return string.Format(text, args);
- }
- catch
- {
- return text;
- }
- }
-
return text;
}
@@ -102,49 +104,66 @@ public static string GetText(string key, params object[] args)
}
}
+ private static bool TryResolve(
+ Dictionary dictionary,
+ string key,
+ object[] args,
+ out string text)
+ {
+ text = null;
+ if (dictionary == null || !dictionary.TryGetValue(key, out var value))
+ {
+ return false;
+ }
+
+ if (value is JsonElement element && element.ValueKind == JsonValueKind.String)
+ {
+ var raw = element.GetString();
+ if (string.IsNullOrEmpty(raw))
+ {
+ return false;
+ }
+
+ text = FormatSafe(raw, args);
+ return true;
+ }
+
+ return false;
+ }
+
+ private static string FormatSafe(string text, object[] args)
+ {
+ if (args == null || args.Length == 0)
+ {
+ return text;
+ }
+
+ try
+ {
+ return string.Format(text, args);
+ }
+ catch
+ {
+ return text;
+ }
+ }
+
///
/// İç içe geçmiş çeviri anahtarından metin alır (örn: "tabs.main")
///
public static string GetText(string category, string key, params object[] args)
{
- try
+ if (category == null || key == null)
{
- if (_currentTranslations == null || !_currentTranslations.ContainsKey(category))
- {
- return $"{category}.{key}";
- }
+ return null;
+ }
- var categoryValue = _currentTranslations[category];
- if (categoryValue is JsonElement categoryElement)
+ try
+ {
+ if (TryResolveNested(_currentTranslations, category, key, args, out var text)
+ || TryResolveNested(_fallbackTranslations, category, key, args, out text))
{
- var categoryDict = JsonSerializer.Deserialize>(categoryElement.GetRawText());
- if (categoryDict != null && categoryDict.ContainsKey(key))
- {
- var value = categoryDict[key];
- if (value is JsonElement element)
- {
- var text = element.GetString();
- if (string.IsNullOrEmpty(text))
- {
- return $"{category}.{key}";
- }
-
- // String.Format benzeri işlem
- if (args != null && args.Length > 0)
- {
- try
- {
- return string.Format(text, args);
- }
- catch
- {
- return text;
- }
- }
-
- return text;
- }
- }
+ return text;
}
return $"{category}.{key}";
@@ -155,5 +174,35 @@ public static string GetText(string category, string key, params object[] args)
return $"{category}.{key}";
}
}
+
+ private static bool TryResolveNested(
+ Dictionary dictionary,
+ string category,
+ string key,
+ object[] args,
+ out string text)
+ {
+ text = null;
+ if (dictionary == null || !dictionary.TryGetValue(category, out var value))
+ {
+ return false;
+ }
+
+ if (value is JsonElement element && element.ValueKind == JsonValueKind.Object
+ && element.TryGetProperty(key, out var leaf)
+ && leaf.ValueKind == JsonValueKind.String)
+ {
+ var raw = leaf.GetString();
+ if (string.IsNullOrEmpty(raw))
+ {
+ return false;
+ }
+
+ text = FormatSafe(raw, args);
+ return true;
+ }
+
+ return false;
+ }
}
}
diff --git a/src/SplitWireTurkey/Resources/goodbyedpi/PLACHOLDER b/src/SplitWireTurkey/Resources/goodbyedpi/PLACEHOLDER
similarity index 100%
rename from src/SplitWireTurkey/Resources/goodbyedpi/PLACHOLDER
rename to src/SplitWireTurkey/Resources/goodbyedpi/PLACEHOLDER
diff --git a/src/SplitWireTurkey/query b/src/SplitWireTurkey/query
deleted file mode 100644
index d123bb2..0000000
--- a/src/SplitWireTurkey/query
+++ /dev/null
@@ -1 +0,0 @@
-WireSockRefresh
diff --git a/src/build_simple.bat b/src/build_simple.bat
index 38b99ef..fc38d20 100644
--- a/src/build_simple.bat
+++ b/src/build_simple.bat
@@ -4,7 +4,7 @@ echo Building SplitWire-Turkey C# WPF Application (Simple Build)...
REM Check if .NET SDK is installed
dotnet --version >nul 2>&1
if errorlevel 1 (
- echo Error: .NET SDK not found. Please install .NET 6.0 SDK or later.
+ echo Error: .NET SDK not found. Please install .NET 8.0 SDK or later.
pause
exit /b 1
)
@@ -22,7 +22,7 @@ if exist "splitwire-logo-128.png" copy "splitwire-logo-128.png" "SplitWireTurkey
if exist "splitwireturkeytext.png" copy "splitwireturkeytext.png" "SplitWireTurkey\Resources\"
if exist "loading.gif" copy "loading.gif" "SplitWireTurkey\Resources\"
if exist "wgcf.exe" copy "wgcf.exe" "SplitWireTurkey\Resources\"
-if exist "wiresock-vpn-client-x64-1.4.7.1.msi" copy "SplitWireTurkey\Resources\"
+if exist "wiresock-vpn-client-x64-1.4.7.1.msi" copy "wiresock-vpn-client-x64-1.4.7.1.msi" "SplitWireTurkey\Resources\"
REM Copy font files if they exist in the main directory
if exist "Poppins-Regular.ttf" copy "Poppins-Regular.ttf" "SplitWireTurkey\Resources\"