diff --git a/ServerSelector/PathUtil.cs b/ServerSelector/PathUtil.cs new file mode 100644 index 0000000..f384fb5 --- /dev/null +++ b/ServerSelector/PathUtil.cs @@ -0,0 +1,99 @@ +using System; +using System.IO; + +namespace ServerSelector; + +public class PathUtil +{ + public bool LauncherExists { get; private set; } + public string? LauncherBasePath { get; set; } + public string GameBasePath { get; set; } + public string? SystemHostsFile + { + get + { + if (OperatingSystem.IsWindows()) + { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "Drivers\\etc\\hosts"); + } + else if (OperatingSystem.IsLinux()) + { + return "/etc/hosts"; + } + else throw new NotImplementedException("Unsupported operating system"); + } + } + + public string? WineHostsFile => GameBasePath + "../../wine_prefix/drive_c/windows/system32/drivers/etc/hosts"; + + public string? LauncherCertificatePath + { + get + { + if (LauncherBasePath == null) return null; + + string path = Path.Combine(LauncherBasePath, "intl_service/intl_cacert.pem"); + + if (!File.Exists(path)) + { + // Older game/SDK version + path = Path.Combine(LauncherBasePath, "intl_service/cacert.pem"); + } + return path; + } + } + + public string GamePluginsDirectory => Path.Combine(GameBasePath ?? throw new InvalidOperationException("Game path not assigned"), "nikke_Data/Plugins/x86_64/"); + public string? GameCertificatePath + { + get + { + string path = Path.Combine(GamePluginsDirectory, "intl_cacert.pem"); + + if (!File.Exists(path)) + { + // Older game/SDK version + path = Path.Combine(GamePluginsDirectory, "cacert.pem"); + } + + return path; + } + } + + public string? GameSodiumPath => Path.Combine(GamePluginsDirectory, "sodium.dll"); + public string? GameSodiumBackupPath => Path.Combine(GamePluginsDirectory, "sodium.dll.bak"); + + /// + /// Sets the directory where the (game name) and Launcher directories are located + /// + /// directory where the (game name) and Launcher directories are located + /// Return (bool, string) where if the operation is successful, true is returned. If it fails, the string contains more details. + public (bool, string?) SetBasePath(string basePath) + { + GameBasePath = Path.Combine(basePath, "NIKKE", "game"); + LauncherBasePath = Path.Combine(basePath, "Launcher"); + + // Various sanity checks + if (!Directory.Exists(GameBasePath)) + { + return (false, $"Directory \"{GameBasePath}\" does not exist"); + } + + LauncherExists = Directory.Exists(LauncherBasePath); + + if (LauncherExists) + { + if (!File.Exists(Path.Combine(LauncherBasePath, "nikke_launcher.exe"))) + { + return (false, "Game path is invalid. Make sure that nikke_launcher.exe exists in the /launcher folder"); + } + } + + if (!File.Exists(GameCertificatePath)) + { + return (false, $"Path is invalid. File \"{GameCertificatePath}\" does not exist."); + } + + return (true, null); + } +} diff --git a/ServerSelector/SearchReplace.cs b/ServerSelector/SearchReplace.cs deleted file mode 100644 index fdb6f96..0000000 --- a/ServerSelector/SearchReplace.cs +++ /dev/null @@ -1,164 +0,0 @@ -// From: https://github.com/Ninka-Rex/CSharp-Search-and-Replace/blob/main/SearchReplace.cs - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace ServerSelector; - -public static class PatchUtility -{ - public static bool CanFindOffset(string filePath, string[] searchPatterns) - { - // Check if the file exists - if (!File.Exists(filePath)) { Console.WriteLine("[ERROR] File not found: " + filePath); return false; } - - // Read the binary data from the file as an array of bytes - byte[] fileData = File.ReadAllBytes(filePath); - - for (int k = 0; k < searchPatterns.Length; k++) - { - // Convert the hexadecimal strings to byte arrays using a helper method - byte[] searchBytes = HexStringToBytes(searchPatterns[k]); - - // Find the index of the first occurrence of the search pattern in the file data using another helper method - List results = FindPatternIndex(fileData, searchBytes); - if (results.Count <= 1) return false; - - Console.WriteLine("offset: " + results[1].ToString("X")); - - // If the index is -1, it means the pattern was not found, so we return false and log an error message - if (results[1] == -1) - { - Console.WriteLine("[ERROR] Search pattern not found: " + searchPatterns[k]); - return false; - } - } - return true; - } - // This method searches and replaces binary patterns in a given file - // It takes three parameters: - // - filePath: the path of the file to be patched - // - searchPatterns: an array of hexadecimal strings representing the patterns to be searched for - // - replacePatterns: an array of hexadecimal strings representing the patterns to be replaced with - // It returns true if the patching was successful, or false if there was an error or a pattern was not found - public static bool SearchAndReplace(string filePath, string[] searchPatterns, string[] replacePatterns) - { - try - { - // Check if the file exists - if (!File.Exists(filePath)) { Console.WriteLine("[ERROR] File not found: " + filePath); return false; } - - // Read the binary data from the file as an array of bytes - byte[] fileData = File.ReadAllBytes(filePath); - - // Backup the original file by copying it to a new file with a .bak extension - string backupFilePath = filePath + ".bak"; - if (!File.Exists(backupFilePath)) - { - File.Copy(filePath, backupFilePath); - } - - // Loop through each pair of search and replace patterns and apply them to the file data - for (int k = 0; k < searchPatterns.Length; k++) - { - // Convert the hexadecimal strings to byte arrays using a helper method - byte[] searchBytes = HexStringToBytes(searchPatterns[k]); - byte[] replaceBytes = HexStringToBytes(replacePatterns[k]); - - // Find the index of the first occurrence of the search pattern in the file data using another helper method - - // Note: game versions 124 - 133 have 2 matches, 2nd one is the correct one - // game version 134 - ? has 1 match which is correct - int index = -1; - List indexes = FindPatternIndex(fileData, searchBytes); - if (indexes.Count == 1) - index = indexes[0]; - else index = indexes[1]; - - Console.WriteLine("Found offset: " + index.ToString("X")); - - // If the index is -1, it means the pattern was not found, so we return false and log an error message - if (index == -1) - { - Console.WriteLine("[ERROR] Search pattern not found: " + searchPatterns[k]); - return false; - } - - // Replace the pattern at the found index with the replace pattern, preserving original values when wildcards are encountered - // A wildcard is represented by either 00 or FF in the replace pattern, meaning that we keep the original value at that position - for (int i = 0; i < replaceBytes.Length; i++) - { - if (replaceBytes[i] != 0x00 && replaceBytes[i] != 0xFF) - { - fileData[index + i] = replaceBytes[i]; - } - else if (replaceBytes[i] == 0x00) - { - fileData[index + i] = 0x00; - } - } - - // Log a success message with the offset and file name where the patch was applied - string exeName = Path.GetFileName(filePath); - Console.WriteLine($"[Patch] Apply patch success at 0x{index:X} in {exeName}"); - } - - // Write the modified data back to the file, overwriting the original content - File.WriteAllBytes(filePath, fileData); - - return true; - } - catch (Exception ex) - { - // If any exception occurs during the patching process, we return false and log an error message with the exception details - Console.WriteLine("[ERROR] An error occurred while writing the file: " + ex.Message); - return false; - } - } - - // This helper method converts a hexadecimal string to a byte array - // It takes one parameter: - // - hex: a string of hexadecimal digits, optionally separated by spaces or question marks - // It returns a byte array corresponding to the hexadecimal values in the string - private static byte[] HexStringToBytes(string hex) - { - hex = hex.Replace(" ", "").Replace("??", "FF"); // Replace ?? with FF for wildcards - return [.. Enumerable.Range(0, hex.Length) - .Where(x => x % 2 == 0) // Take every second character in the string - .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))]; // Convert the result to an array of bytes - } - - // This helper method finds the index of the first occurrence of a pattern in a data array - // It takes two parameters: - // - data: an array of bytes representing the data to be searched in - // - pattern: an array of bytes representing the pattern to be searched for - // It returns an integer representing the index where the pattern was found, or -1 if it was not found - private static List FindPatternIndex(byte[] data, byte[] pattern) - { - List points = []; - // Loop through each possible position in the data array where the pattern could start - for (int i = 0; i < data.Length - pattern.Length + 1; i++) - { - bool found = true; // Assume that the pattern is found until proven otherwise - // Loop through each byte in the pattern and compare it with the corresponding byte in the data array - for (int j = 0; j < pattern.Length; j++) - { - // If the pattern byte is not FF (wildcard) and it does not match the data byte, then the pattern is not found at this position - if (pattern[j] != 0xFF && data[i + j] != pattern[j]) - { - found = false; - break; - } - } - // If the pattern was found at this position, return the index - if (found) - { - points.Add(i); - } - } - // If the pattern was not found in the entire data array, return -1 - return points; - } -} \ No newline at end of file diff --git a/ServerSelector/ServerSwitcher.cs b/ServerSelector/ServerSwitcher.cs index b438272..3ba0b1f 100644 --- a/ServerSelector/ServerSwitcher.cs +++ b/ServerSelector/ServerSwitcher.cs @@ -4,210 +4,174 @@ using System.IO; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -namespace ServerSelector +namespace ServerSelector; + +public class ServerSwitcher { - public class ServerSwitcher + private const string HostsStartMarker = "# begin ServerSelector entries"; + private const string HostsEndMarker = "# end ServerSelector entries"; + + private static PathUtil util = new(); + + public static bool IsUsingLocalServer() { - private const string HostsStartMarker = "# begin ServerSelector entries"; - private const string HostsEndMarker = "# end ServerSelector entries"; + return File.ReadAllText(util.SystemHostsFile).Contains("global-lobby.nikke-kr.com"); + } - public static bool IsUsingOfficalServer() + public static bool IsOffline() + { + return File.ReadAllText(util.SystemHostsFile).Contains("cloud.nikke-kr.com"); + } + + public static (bool, string?) SetBasePath(string basePath) + { + return util.SetBasePath(basePath); + } + + public static async Task CheckIntegrity() + { + if (!IsUsingLocalServer()) + return "Official server"; + + if (File.Exists(util.LauncherCertificatePath)) { - string hostsFile = File.ReadAllText(OperatingSystem.IsWindows() ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : "/etc/hosts"); - return !hostsFile.Contains("global-lobby.nikke-kr.com"); + string certList1 = await File.ReadAllTextAsync(util.LauncherCertificatePath); + + if (!certList1.Contains("Good SSL Ca")) + return "SSL Cert Patch missing Launcher"; } - public static bool IsOffline() + if (File.Exists(util.GameCertificatePath)) { - string hostsFile = File.ReadAllText(OperatingSystem.IsWindows() ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : "/etc/hosts"); - return hostsFile.Contains("cloud.nikke-kr.com"); + string certList2 = await File.ReadAllTextAsync(util.GameCertificatePath); + + if (!certList2.Contains("Good SSL Ca")) + return "SSL Cert Patch missing Game"; } - public static async Task CheckIntegrity(string gamePath, string launcherPath) + // TODO: Check sodium lib + // TODO: check hosts file + + return "OK"; + } + + public static async Task RevertHostsFile(string hostsFilePath) + { + string txt = await File.ReadAllTextAsync(hostsFilePath); + + // remove stuff + try { - if (IsUsingOfficalServer()) - return "Official server"; - if (!Directory.Exists(gamePath)) + int startIdx = txt.IndexOf(HostsStartMarker); + int endIdx; + if (startIdx == -1) { - return "Game path does not exist"; + startIdx = txt.IndexOf("cloud.nikke-kr.com"); } - if (!Directory.Exists(launcherPath)) + string endIndexStr = HostsEndMarker; + if (!txt.Contains(endIndexStr)) { - return "Launcher path does not exist"; - } - - if (!File.Exists(Path.Combine(launcherPath, "nikke_launcher.exe"))) - { - return "Launcher path is invalid. Make sure that the game executable exists in the launcher folder"; - } - - - // TODO fix this mess - string launcherCertList = launcherPath + "/intl_service/intl_cacert.pem"; - if (!File.Exists(launcherCertList)) - launcherCertList = launcherPath + "/intl_service/cacert.pem"; // older INTL sdk versions - string gameCertList = gamePath + "/nikke_Data/Plugins/x86_64/intl_cacert.pem"; - if (!File.Exists(gameCertList)) - gameCertList = gamePath + "/nikke_Data/Plugins/x86_64/cacert.pem"; // older INTL sdk versions - - if (File.Exists(launcherCertList)) - { - string certList1 = await File.ReadAllTextAsync(launcherCertList); - - if (!certList1.Contains("Good SSL Ca")) - return "SSL Cert Patch missing"; - } - - if (File.Exists(gameCertList)) - { - string certList2 = await File.ReadAllTextAsync(gameCertList); - - if (!certList2.Contains("Good SSL Ca")) - return "SSL Cert Patch missing"; - } - - // TODO: Check sodium lib - // TODO: check hosts file - - return "OK"; - } - - public static async Task RevertHostsFile(string hostsFilePath) - { - string txt = await File.ReadAllTextAsync(hostsFilePath); - - // remove stuff - try - { - - int startIdx = txt.IndexOf(HostsStartMarker); - int endIdx; - if (startIdx == -1) + // old code, find new line character before start index + for (int i = startIdx - 1; i >= 0; i--) { - startIdx = txt.IndexOf("cloud.nikke-kr.com"); - } - - string endIndexStr = HostsEndMarker; - if (!txt.Contains(endIndexStr)) - { - // old code, find new line character before start index - for (int i = startIdx - 1; i >= 0; i--) + char c = txt[i]; + if (c == '\n') { - char c = txt[i]; - if (c == '\n') - { - startIdx = i + 1; - break; - } - } - - endIndexStr = "y.io"; - endIdx = txt.IndexOf(endIndexStr) + endIndexStr.Length; - } - else - { - // add/subtract 2 to take into account newline - startIdx = txt.IndexOf(HostsStartMarker) - 2; - endIdx = txt.IndexOf(endIndexStr) + endIndexStr.Length; - } - - txt = string.Concat(txt.AsSpan(0, startIdx), txt.AsSpan(endIdx)); - - - await File.WriteAllTextAsync(hostsFilePath, txt); - } - catch - { - - } - } - - public static async Task SaveCfg(bool useOffical, string gamePath, string? launcherPath, string ip, bool offlineMode) - { - string sodiumLib = AppDomain.CurrentDomain.BaseDirectory + "sodium.dll"; - string gameSodium = gamePath + "/nikke_Data/Plugins/x86_64/sodium.dll"; - string gameAssembly = gamePath + "/GameAssembly.dll"; - string sodiumBackup = gameSodium + ".bak"; - string hostsFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "drivers/etc/hosts"); - string CAcert = await File.ReadAllTextAsync(AppDomain.CurrentDomain.BaseDirectory + "myCA.pem"); - - string launcherCertList = launcherPath + "/intl_service/intl_cacert.pem"; - if (!File.Exists(launcherCertList)) - launcherCertList = launcherPath + "/intl_service/cacert.pem"; // older INTL sdk versions - string gameCertList = gamePath + "/nikke_Data/Plugins/x86_64/intl_cacert.pem"; - if (!File.Exists(gameCertList)) - gameCertList = gamePath + "/nikke_Data/Plugins/x86_64/cacert.pem"; // older INTL sdk versions - bool supported = true; - - if (OperatingSystem.IsLinux()) - { - // for wine - hostsFilePath = gamePath + "/../../../windows/system32/drivers/etc/hosts"; - } - - if (useOffical) - { - await RevertHostsFile(hostsFilePath); - if (OperatingSystem.IsLinux()) - { - await RevertHostsFile("/etc/hosts"); - } - - try - { - // remove cert - if (OperatingSystem.IsWindows()) - { - X509Store store = new(StoreName.Root, StoreLocation.LocalMachine); - store.Open(OpenFlags.ReadWrite); - store.Remove(new X509Certificate2(X509Certificate.CreateFromCertFile(AppDomain.CurrentDomain.BaseDirectory + "myCA.pfx"))); - store.Close(); + startIdx = i + 1; + break; } } - catch - { - // may not be installed - } - // restore sodium - if (!File.Exists(sodiumBackup)) - { - throw new Exception("sodium backup does not exist. Repair the game in the launcher and switch to local server and back to official."); - } - File.Copy(sodiumBackup, gameSodium, true); - - if (File.Exists(launcherCertList)) - { - string certList = await File.ReadAllTextAsync(launcherCertList); - - int goodSslIndex1 = certList.IndexOf("Good SSL Ca"); - if (goodSslIndex1 != -1) - await File.WriteAllTextAsync(launcherCertList, certList[..goodSslIndex1]); - } - - if (File.Exists(gameCertList)) - { - string certList = await File.ReadAllTextAsync(gameCertList); - - int newCertIndex = certList.IndexOf("Good SSL Ca"); - if (newCertIndex != -1) - await File.WriteAllTextAsync(gameCertList, certList[..newCertIndex]); - } + endIndexStr = "y.io"; + endIdx = txt.IndexOf(endIndexStr) + endIndexStr.Length; } else { - // add to hosts file - string hosts = $@"{HostsStartMarker} + // add/subtract 2 to take into account newline + startIdx = txt.IndexOf(HostsStartMarker) - 2; + endIdx = txt.IndexOf(endIndexStr) + endIndexStr.Length; + } + + txt = string.Concat(txt.AsSpan(0, startIdx), txt.AsSpan(endIdx)); + + + await File.WriteAllTextAsync(hostsFilePath, txt); + } + catch + { + + } + } + + public static async Task SaveCfg(bool useOffical, string ip, bool offlineMode) + { + string CAcert = await File.ReadAllTextAsync(AppDomain.CurrentDomain.BaseDirectory + "myCA.pem"); + string sodiumLib = AppDomain.CurrentDomain.BaseDirectory + "sodium.dll"; + + bool supported = true; + if (useOffical) + { + await RevertHostsFile(util.SystemHostsFile); + if (OperatingSystem.IsLinux()) + { + await RevertHostsFile(util.WineHostsFile); + } + + try + { + // remove cert + if (OperatingSystem.IsWindows()) + { + X509Store store = new(StoreName.Root, StoreLocation.LocalMachine); + store.Open(OpenFlags.ReadWrite); + store.Remove(new X509Certificate2(X509Certificate.CreateFromCertFile(AppDomain.CurrentDomain.BaseDirectory + "myCA.pfx"))); + store.Close(); + } + } + catch + { + // may not be installed + } + + // restore sodium + if (!File.Exists(util.GameSodiumBackupPath)) + { + throw new Exception("sodium backup does not exist. Repair the game in the launcher and switch to local server and back to official."); + } + File.Copy(util.GameSodiumBackupPath, util.GameSodiumPath, true); + + if (util.LauncherCertificatePath != null && File.Exists(util.LauncherCertificatePath)) + { + string certList = await File.ReadAllTextAsync(util.LauncherCertificatePath); + + int goodSslIndex1 = certList.IndexOf("Good SSL Ca"); + if (goodSslIndex1 != -1) + await File.WriteAllTextAsync(util.LauncherCertificatePath, certList[..goodSslIndex1]); + } + + if (File.Exists(util.GameCertificatePath)) + { + string certList = await File.ReadAllTextAsync(util.GameCertificatePath); + + int newCertIndex = certList.IndexOf("Good SSL Ca"); + if (newCertIndex != -1) + await File.WriteAllTextAsync(util.GameCertificatePath, certList[..newCertIndex]); + } + } + else + { + // add to hosts file + string hosts = $@"{HostsStartMarker} {ip} global-lobby.nikke-kr.com "; - if (offlineMode) - { - hosts += $"{ip} cloud.nikke-kr.com" + Environment.NewLine; - } + if (offlineMode) + { + hosts += $"{ip} cloud.nikke-kr.com" + Environment.NewLine; + } - hosts += $@"{ip} jp-lobby.nikke-kr.com + hosts += $@"{ip} jp-lobby.nikke-kr.com {ip} us-lobby.nikke-kr.com {ip} kr-lobby.nikke-kr.com {ip} sea-lobby.nikke-kr.com @@ -224,94 +188,92 @@ namespace ServerSelector 255.255.221.21 sentry.io {HostsEndMarker}"; - await RevertHostsFile(hostsFilePath); + await RevertHostsFile(util.SystemHostsFile); - try + try + { + FileInfo fi = new(util.SystemHostsFile); + if (fi.IsReadOnly) { - FileInfo fi = new(hostsFilePath); - if (fi.IsReadOnly) - { - // try to remove readonly flag - fi.IsReadOnly = false; - } - - if (!(await File.ReadAllTextAsync(hostsFilePath)).Contains("global-lobby.nikke-kr.com")) - { - using StreamWriter w = File.AppendText(hostsFilePath); - w.WriteLine(); - w.Write(hosts); - } - } - catch - { - throw new Exception("cannot modify C:\\Windows\\System32\\drivers\\etc\\hosts file to redirect to server, check your antivirus software"); + // try to remove readonly flag + fi.IsReadOnly = false; } - // Also change /etc/hosts if running on linux - if (OperatingSystem.IsLinux()) + if (!(await File.ReadAllTextAsync(util.SystemHostsFile)).Contains("global-lobby.nikke-kr.com")) { - hostsFilePath = "/etc/hosts"; - await RevertHostsFile(hostsFilePath); - if (!(await File.ReadAllTextAsync(hostsFilePath)).Contains("global-lobby.nikke-kr.com")) - { - using StreamWriter w = File.AppendText(hostsFilePath); - w.WriteLine(); - w.Write(hosts); - } + using StreamWriter w = File.AppendText(util.SystemHostsFile); + w.WriteLine(); + w.Write(hosts); } + } + catch + { + throw new Exception($"cannot modify \"{util.SystemHostsFile}\" file to redirect to server, check your antivirus software"); + } - // trust CA. TODO is this needed? - try + // Also change hosts file in wineprefix if running on linux + if (OperatingSystem.IsLinux()) + { + await RevertHostsFile(util.WineHostsFile); + if (!(await File.ReadAllTextAsync(util.WineHostsFile)).Contains("global-lobby.nikke-kr.com")) { - if (OperatingSystem.IsWindows()) - { - X509Store store = new(StoreName.Root, StoreLocation.LocalMachine); - store.Open(OpenFlags.ReadWrite); - store.Add(new X509Certificate2(X509Certificate2.CreateFromCertFile(AppDomain.CurrentDomain.BaseDirectory + "myCA.pfx"))); - store.Close(); - } + using StreamWriter w = File.AppendText(util.WineHostsFile); + w.WriteLine(); + w.Write(hosts); } - catch { } + } - if (!File.Exists(gameSodium)) + // trust CA. TODO is this needed? + try + { + if (OperatingSystem.IsWindows()) { - throw new Exception("expected sodium library to exist at path " + gameSodium); + X509Store store = new(StoreName.Root, StoreLocation.LocalMachine); + store.Open(OpenFlags.ReadWrite); + store.Add(new X509Certificate2(X509Certificate2.CreateFromCertFile(AppDomain.CurrentDomain.BaseDirectory + "myCA.pfx"))); + store.Close(); } + } + catch { } - // copy backup if sodium size is correct - byte[] sod = await File.ReadAllBytesAsync(gameSodium); - if (sod.Length <= 307200) // TODO this is awful - { - // orignal file size, copy backup - await File.WriteAllBytesAsync(sodiumBackup, sod); - } + if (!File.Exists(util.GameSodiumPath)) + { + throw new Exception("expected sodium library to exist at path " + util.GameSodiumPath); + } - // write new sodium library - await File.WriteAllBytesAsync(gameSodium, await File.ReadAllBytesAsync(sodiumLib)); + // copy backup if sodium size is correct + byte[] sod = await File.ReadAllBytesAsync(util.GameSodiumPath); + if (sod.Length <= 307200) // TODO this is awful + { + // orignal file size, copy backup + await File.WriteAllBytesAsync(util.GameSodiumBackupPath, sod); + } - // Add generated CA certificate to launcher/game curl certificate list - if (launcherPath != null) - { - await File.WriteAllTextAsync(launcherCertList, - await File.ReadAllTextAsync(launcherCertList) - + "\nGood SSL Ca\n===============================\n" - + CAcert); - } + // write new sodium library + await File.WriteAllBytesAsync(util.GameSodiumPath, await File.ReadAllBytesAsync(sodiumLib)); - await File.WriteAllTextAsync(gameCertList, - await File.ReadAllTextAsync(gameCertList) + // Add generated CA certificate to launcher/game curl certificate list + if (util.LauncherCertificatePath != null) + { + await File.WriteAllTextAsync(util.LauncherCertificatePath, + await File.ReadAllTextAsync(util.LauncherCertificatePath) + "\nGood SSL Ca\n===============================\n" + CAcert); } - return new ServerSwitchResult(true, null, supported); + await File.WriteAllTextAsync(util.GameCertificatePath, + await File.ReadAllTextAsync(util.GameCertificatePath) + + "\nGood SSL Ca\n===============================\n" + + CAcert); } - } - public class ServerSwitchResult(bool ok, Exception? exception, bool isSupported) - { - public bool Ok { get; set; } = ok; - public Exception? Exception { get; set; } = exception; - public bool IsSupported { get; set; } = isSupported; + return new ServerSwitchResult(true, null, supported); } } + +public class ServerSwitchResult(bool ok, Exception? exception, bool isSupported) +{ + public bool Ok { get; set; } = ok; + public Exception? Exception { get; set; } = exception; + public bool IsSupported { get; set; } = isSupported; +} diff --git a/ServerSelector/Views/MainView.axaml b/ServerSelector/Views/MainView.axaml index 0e791cd..a54e6a5 100644 --- a/ServerSelector/Views/MainView.axaml +++ b/ServerSelector/Views/MainView.axaml @@ -116,7 +116,7 @@ Game root path: C:\NIKKE\ - + Server: @@ -144,7 +144,7 @@ - + diff --git a/ServerSelector/Views/MainView.axaml.cs b/ServerSelector/Views/MainView.axaml.cs index 99cc9e1..d41f8df 100644 --- a/ServerSelector/Views/MainView.axaml.cs +++ b/ServerSelector/Views/MainView.axaml.cs @@ -14,9 +14,9 @@ public partial class MainView : UserControl public MainView() { InitializeComponent(); - CmbServerSelection.SelectedIndex = ServerSwitcher.IsUsingOfficalServer() ? 0 : 1; + CmbServerSelection.SelectedIndex = ServerSwitcher.IsUsingLocalServer() ? 1 : 0; - TxtIpAddress.IsEnabled = !ServerSwitcher.IsUsingOfficalServer(); + TxtIpAddress.IsEnabled = !ServerSwitcher.IsUsingLocalServer(); ChkOffline.IsChecked = ServerSwitcher.IsOffline(); if (OperatingSystem.IsWindows() && !new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator)) @@ -57,25 +57,17 @@ public partial class MainView : UserControl } } - private string? GamePath + private string? BasePath { get { if (txtGamePath.Text == null) return null; - return Path.Combine(txtGamePath.Text, "NIKKE", "game"); - } - } - private string? LauncherPath - { - get - { - if (txtGamePath.Text == null) return null; - return Path.Combine(txtGamePath.Text, "Launcher"); + return txtGamePath.Text; } } private bool ValidatePaths(bool showMessage) { - if (string.IsNullOrEmpty(txtGamePath.Text) || LauncherPath == null) + if (string.IsNullOrEmpty(BasePath)) { SetGamePathValid(false); if (showMessage) @@ -83,20 +75,21 @@ public partial class MainView : UserControl return false; } - if (!Directory.Exists(GamePath)) + if (!Directory.Exists(BasePath)) { SetGamePathValid(false); if (showMessage) - ShowWarningMsg("game folder does not exist in the game root folder", "Error"); + ShowWarningMsg("Game path does not exist", "Error"); return false; } - if (!File.Exists(Path.Combine(LauncherPath, "nikke_launcher.exe"))) + var result = ServerSwitcher.SetBasePath(BasePath); + + if (!result.Item1) { SetGamePathValid(false); if (showMessage) - ShowWarningMsg("Game path is invalid. Make sure that nikke_launcher.exe exists in the /launcher folder", "Error"); - + ShowWarningMsg(result.Item2, "Error"); return false; } @@ -107,11 +100,11 @@ public partial class MainView : UserControl private async void UpdateIntegrityLabel() { - if (!ValidatePaths(false) || txtGamePath.Text == null || GamePath == null || LauncherPath == null) + if (!ValidatePaths(false) || txtGamePath.Text == null || BasePath == null) return; SetLoadingScreenVisible(true); - LblStatus.Text = "Status: " + await ServerSwitcher.CheckIntegrity(GamePath, LauncherPath); + LblStatus.Text = "Status: " + await ServerSwitcher.CheckIntegrity(); SetLoadingScreenVisible(false); } @@ -131,7 +124,7 @@ public partial class MainView : UserControl private async void Save_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { - if (!ValidatePaths(true) || txtGamePath.Text == null || GamePath == null || LauncherPath == null) + if (!ValidatePaths(true) || txtGamePath.Text == null || BasePath == null) return; if (CmbServerSelection.SelectedIndex == 1 && !IPAddress.TryParse(TxtIpAddress.Text, out _)) @@ -150,7 +143,7 @@ public partial class MainView : UserControl SetLoadingScreenVisible(true); try { - ServerSwitchResult res = await ServerSwitcher.SaveCfg(CmbServerSelection.SelectedIndex == 0, GamePath, LauncherPath, TxtIpAddress.Text, ChkOffline.IsChecked ?? false); + ServerSwitchResult res = await ServerSwitcher.SaveCfg(CmbServerSelection.SelectedIndex == 0, TxtIpAddress.Text, ChkOffline.IsChecked ?? false); if (!res.IsSupported) {