mirror of
https://github.com/EpinelPS/EpinelPS.git
synced 2025-12-12 15:04:36 +01:00
Fix a few bugs in server selector, do not hardcode paths
This commit is contained in:
99
ServerSelector/PathUtil.cs
Normal file
99
ServerSelector/PathUtil.cs
Normal file
@@ -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");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the directory where the (game name) and Launcher directories are located
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="basePath">directory where the (game name) and Launcher directories are located</param>
|
||||||
|
/// <returns>Return (bool, string) where if the operation is successful, true is returned. If it fails, the string contains more details.</returns>
|
||||||
|
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 <Game Path>/launcher folder");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!File.Exists(GameCertificatePath))
|
||||||
|
{
|
||||||
|
return (false, $"Path is invalid. File \"{GameCertificatePath}\" does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (true, 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<int> 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<int> 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<int> FindPatternIndex(byte[] data, byte[] pattern)
|
|
||||||
{
|
|
||||||
List<int> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,68 +4,49 @@ using System.IO;
|
|||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace ServerSelector
|
namespace ServerSelector;
|
||||||
{
|
|
||||||
public class ServerSwitcher
|
public class ServerSwitcher
|
||||||
{
|
{
|
||||||
private const string HostsStartMarker = "# begin ServerSelector entries";
|
private const string HostsStartMarker = "# begin ServerSelector entries";
|
||||||
private const string HostsEndMarker = "# end ServerSelector entries";
|
private const string HostsEndMarker = "# end ServerSelector entries";
|
||||||
|
|
||||||
public static bool IsUsingOfficalServer()
|
private static PathUtil util = new();
|
||||||
|
|
||||||
|
public static bool IsUsingLocalServer()
|
||||||
{
|
{
|
||||||
string hostsFile = File.ReadAllText(OperatingSystem.IsWindows() ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : "/etc/hosts");
|
return File.ReadAllText(util.SystemHostsFile).Contains("global-lobby.nikke-kr.com");
|
||||||
return !hostsFile.Contains("global-lobby.nikke-kr.com");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsOffline()
|
public static bool IsOffline()
|
||||||
{
|
{
|
||||||
string hostsFile = File.ReadAllText(OperatingSystem.IsWindows() ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : "/etc/hosts");
|
return File.ReadAllText(util.SystemHostsFile).Contains("cloud.nikke-kr.com");
|
||||||
return hostsFile.Contains("cloud.nikke-kr.com");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<string> CheckIntegrity(string gamePath, string launcherPath)
|
public static (bool, string?) SetBasePath(string basePath)
|
||||||
{
|
{
|
||||||
if (IsUsingOfficalServer())
|
return util.SetBasePath(basePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<string> CheckIntegrity()
|
||||||
|
{
|
||||||
|
if (!IsUsingLocalServer())
|
||||||
return "Official server";
|
return "Official server";
|
||||||
|
|
||||||
if (!Directory.Exists(gamePath))
|
if (File.Exists(util.LauncherCertificatePath))
|
||||||
{
|
{
|
||||||
return "Game path does not exist";
|
string certList1 = await File.ReadAllTextAsync(util.LauncherCertificatePath);
|
||||||
}
|
|
||||||
|
|
||||||
if (!Directory.Exists(launcherPath))
|
|
||||||
{
|
|
||||||
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"))
|
if (!certList1.Contains("Good SSL Ca"))
|
||||||
return "SSL Cert Patch missing";
|
return "SSL Cert Patch missing Launcher";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (File.Exists(gameCertList))
|
if (File.Exists(util.GameCertificatePath))
|
||||||
{
|
{
|
||||||
string certList2 = await File.ReadAllTextAsync(gameCertList);
|
string certList2 = await File.ReadAllTextAsync(util.GameCertificatePath);
|
||||||
|
|
||||||
if (!certList2.Contains("Good SSL Ca"))
|
if (!certList2.Contains("Good SSL Ca"))
|
||||||
return "SSL Cert Patch missing";
|
return "SSL Cert Patch missing Game";
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check sodium lib
|
// TODO: Check sodium lib
|
||||||
@@ -124,35 +105,18 @@ namespace ServerSelector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<ServerSwitchResult> SaveCfg(bool useOffical, string gamePath, string? launcherPath, string ip, bool offlineMode)
|
public static async Task<ServerSwitchResult> SaveCfg(bool useOffical, 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 CAcert = await File.ReadAllTextAsync(AppDomain.CurrentDomain.BaseDirectory + "myCA.pem");
|
||||||
|
string sodiumLib = AppDomain.CurrentDomain.BaseDirectory + "sodium.dll";
|
||||||
|
|
||||||
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;
|
bool supported = true;
|
||||||
|
|
||||||
if (OperatingSystem.IsLinux())
|
|
||||||
{
|
|
||||||
// for wine
|
|
||||||
hostsFilePath = gamePath + "/../../../windows/system32/drivers/etc/hosts";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useOffical)
|
if (useOffical)
|
||||||
{
|
{
|
||||||
await RevertHostsFile(hostsFilePath);
|
await RevertHostsFile(util.SystemHostsFile);
|
||||||
if (OperatingSystem.IsLinux())
|
if (OperatingSystem.IsLinux())
|
||||||
{
|
{
|
||||||
await RevertHostsFile("/etc/hosts");
|
await RevertHostsFile(util.WineHostsFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -172,28 +136,28 @@ namespace ServerSelector
|
|||||||
}
|
}
|
||||||
|
|
||||||
// restore sodium
|
// restore sodium
|
||||||
if (!File.Exists(sodiumBackup))
|
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.");
|
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);
|
File.Copy(util.GameSodiumBackupPath, util.GameSodiumPath, true);
|
||||||
|
|
||||||
if (File.Exists(launcherCertList))
|
if (util.LauncherCertificatePath != null && File.Exists(util.LauncherCertificatePath))
|
||||||
{
|
{
|
||||||
string certList = await File.ReadAllTextAsync(launcherCertList);
|
string certList = await File.ReadAllTextAsync(util.LauncherCertificatePath);
|
||||||
|
|
||||||
int goodSslIndex1 = certList.IndexOf("Good SSL Ca");
|
int goodSslIndex1 = certList.IndexOf("Good SSL Ca");
|
||||||
if (goodSslIndex1 != -1)
|
if (goodSslIndex1 != -1)
|
||||||
await File.WriteAllTextAsync(launcherCertList, certList[..goodSslIndex1]);
|
await File.WriteAllTextAsync(util.LauncherCertificatePath, certList[..goodSslIndex1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (File.Exists(gameCertList))
|
if (File.Exists(util.GameCertificatePath))
|
||||||
{
|
{
|
||||||
string certList = await File.ReadAllTextAsync(gameCertList);
|
string certList = await File.ReadAllTextAsync(util.GameCertificatePath);
|
||||||
|
|
||||||
int newCertIndex = certList.IndexOf("Good SSL Ca");
|
int newCertIndex = certList.IndexOf("Good SSL Ca");
|
||||||
if (newCertIndex != -1)
|
if (newCertIndex != -1)
|
||||||
await File.WriteAllTextAsync(gameCertList, certList[..newCertIndex]);
|
await File.WriteAllTextAsync(util.GameCertificatePath, certList[..newCertIndex]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -224,37 +188,36 @@ namespace ServerSelector
|
|||||||
255.255.221.21 sentry.io
|
255.255.221.21 sentry.io
|
||||||
{HostsEndMarker}";
|
{HostsEndMarker}";
|
||||||
|
|
||||||
await RevertHostsFile(hostsFilePath);
|
await RevertHostsFile(util.SystemHostsFile);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FileInfo fi = new(hostsFilePath);
|
FileInfo fi = new(util.SystemHostsFile);
|
||||||
if (fi.IsReadOnly)
|
if (fi.IsReadOnly)
|
||||||
{
|
{
|
||||||
// try to remove readonly flag
|
// try to remove readonly flag
|
||||||
fi.IsReadOnly = false;
|
fi.IsReadOnly = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(await File.ReadAllTextAsync(hostsFilePath)).Contains("global-lobby.nikke-kr.com"))
|
if (!(await File.ReadAllTextAsync(util.SystemHostsFile)).Contains("global-lobby.nikke-kr.com"))
|
||||||
{
|
{
|
||||||
using StreamWriter w = File.AppendText(hostsFilePath);
|
using StreamWriter w = File.AppendText(util.SystemHostsFile);
|
||||||
w.WriteLine();
|
w.WriteLine();
|
||||||
w.Write(hosts);
|
w.Write(hosts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
throw new Exception("cannot modify C:\\Windows\\System32\\drivers\\etc\\hosts file to redirect to server, check your antivirus software");
|
throw new Exception($"cannot modify \"{util.SystemHostsFile}\" file to redirect to server, check your antivirus software");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also change /etc/hosts if running on linux
|
// Also change hosts file in wineprefix if running on linux
|
||||||
if (OperatingSystem.IsLinux())
|
if (OperatingSystem.IsLinux())
|
||||||
{
|
{
|
||||||
hostsFilePath = "/etc/hosts";
|
await RevertHostsFile(util.WineHostsFile);
|
||||||
await RevertHostsFile(hostsFilePath);
|
if (!(await File.ReadAllTextAsync(util.WineHostsFile)).Contains("global-lobby.nikke-kr.com"))
|
||||||
if (!(await File.ReadAllTextAsync(hostsFilePath)).Contains("global-lobby.nikke-kr.com"))
|
|
||||||
{
|
{
|
||||||
using StreamWriter w = File.AppendText(hostsFilePath);
|
using StreamWriter w = File.AppendText(util.WineHostsFile);
|
||||||
w.WriteLine();
|
w.WriteLine();
|
||||||
w.Write(hosts);
|
w.Write(hosts);
|
||||||
}
|
}
|
||||||
@@ -273,33 +236,33 @@ namespace ServerSelector
|
|||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
if (!File.Exists(gameSodium))
|
if (!File.Exists(util.GameSodiumPath))
|
||||||
{
|
{
|
||||||
throw new Exception("expected sodium library to exist at path " + gameSodium);
|
throw new Exception("expected sodium library to exist at path " + util.GameSodiumPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy backup if sodium size is correct
|
// copy backup if sodium size is correct
|
||||||
byte[] sod = await File.ReadAllBytesAsync(gameSodium);
|
byte[] sod = await File.ReadAllBytesAsync(util.GameSodiumPath);
|
||||||
if (sod.Length <= 307200) // TODO this is awful
|
if (sod.Length <= 307200) // TODO this is awful
|
||||||
{
|
{
|
||||||
// orignal file size, copy backup
|
// orignal file size, copy backup
|
||||||
await File.WriteAllBytesAsync(sodiumBackup, sod);
|
await File.WriteAllBytesAsync(util.GameSodiumBackupPath, sod);
|
||||||
}
|
}
|
||||||
|
|
||||||
// write new sodium library
|
// write new sodium library
|
||||||
await File.WriteAllBytesAsync(gameSodium, await File.ReadAllBytesAsync(sodiumLib));
|
await File.WriteAllBytesAsync(util.GameSodiumPath, await File.ReadAllBytesAsync(sodiumLib));
|
||||||
|
|
||||||
// Add generated CA certificate to launcher/game curl certificate list
|
// Add generated CA certificate to launcher/game curl certificate list
|
||||||
if (launcherPath != null)
|
if (util.LauncherCertificatePath != null)
|
||||||
{
|
{
|
||||||
await File.WriteAllTextAsync(launcherCertList,
|
await File.WriteAllTextAsync(util.LauncherCertificatePath,
|
||||||
await File.ReadAllTextAsync(launcherCertList)
|
await File.ReadAllTextAsync(util.LauncherCertificatePath)
|
||||||
+ "\nGood SSL Ca\n===============================\n"
|
+ "\nGood SSL Ca\n===============================\n"
|
||||||
+ CAcert);
|
+ CAcert);
|
||||||
}
|
}
|
||||||
|
|
||||||
await File.WriteAllTextAsync(gameCertList,
|
await File.WriteAllTextAsync(util.GameCertificatePath,
|
||||||
await File.ReadAllTextAsync(gameCertList)
|
await File.ReadAllTextAsync(util.GameCertificatePath)
|
||||||
+ "\nGood SSL Ca\n===============================\n"
|
+ "\nGood SSL Ca\n===============================\n"
|
||||||
+ CAcert);
|
+ CAcert);
|
||||||
}
|
}
|
||||||
@@ -314,4 +277,3 @@ namespace ServerSelector
|
|||||||
public Exception? Exception { get; set; } = exception;
|
public Exception? Exception { get; set; } = exception;
|
||||||
public bool IsSupported { get; set; } = isSupported;
|
public bool IsSupported { get; set; } = isSupported;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@
|
|||||||
|
|
||||||
<TextBlock VerticalAlignment="Center" Margin="5" Grid.Row="0" Grid.Column="0" ToolTip.Tip="The folder with the game and launcher folders.">Game root path: </TextBlock>
|
<TextBlock VerticalAlignment="Center" Margin="5" Grid.Row="0" Grid.Column="0" ToolTip.Tip="The folder with the game and launcher folders.">Game root path: </TextBlock>
|
||||||
<TextBox x:Name="txtGamePath" Grid.Row="0" Grid.Column="1" TextChanged="GamePath_TextChanged" ToolTip.Tip="The folder with the game and launcher folders.">C:\NIKKE\</TextBox>
|
<TextBox x:Name="txtGamePath" Grid.Row="0" Grid.Column="1" TextChanged="GamePath_TextChanged" ToolTip.Tip="The folder with the game and launcher folders.">C:\NIKKE\</TextBox>
|
||||||
<Button x:Name="btnSelectGamePath" Grid.Row="0" Grid.Column="2" Classes="utility" Click="BtnSelectGamePath_Click">...</Button>
|
<Button x:Name="btnSelectGamePath" Grid.Row="0" Grid.Column="2" Classes="utility" Click="BtnSelectGamePath_Click" Margin="5,0,0,0">...</Button>
|
||||||
|
|
||||||
<TextBlock VerticalAlignment="Center" Margin="5" Grid.Row="4" Grid.Column="0">Server: </TextBlock>
|
<TextBlock VerticalAlignment="Center" Margin="5" Grid.Row="4" Grid.Column="0">Server: </TextBlock>
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem Header="Android">
|
<TabItem Header="Android" IsVisible="False">
|
||||||
<Grid Margin="5">
|
<Grid Margin="5">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ public partial class MainView : UserControl
|
|||||||
public MainView()
|
public MainView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
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();
|
ChkOffline.IsChecked = ServerSwitcher.IsOffline();
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows() && !new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator))
|
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
|
get
|
||||||
{
|
{
|
||||||
if (txtGamePath.Text == null) return null;
|
if (txtGamePath.Text == null) return null;
|
||||||
return Path.Combine(txtGamePath.Text, "NIKKE", "game");
|
return txtGamePath.Text;
|
||||||
}
|
|
||||||
}
|
|
||||||
private string? LauncherPath
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (txtGamePath.Text == null) return null;
|
|
||||||
return Path.Combine(txtGamePath.Text, "Launcher");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private bool ValidatePaths(bool showMessage)
|
private bool ValidatePaths(bool showMessage)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(txtGamePath.Text) || LauncherPath == null)
|
if (string.IsNullOrEmpty(BasePath))
|
||||||
{
|
{
|
||||||
SetGamePathValid(false);
|
SetGamePathValid(false);
|
||||||
if (showMessage)
|
if (showMessage)
|
||||||
@@ -83,20 +75,21 @@ public partial class MainView : UserControl
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Directory.Exists(GamePath))
|
if (!Directory.Exists(BasePath))
|
||||||
{
|
{
|
||||||
SetGamePathValid(false);
|
SetGamePathValid(false);
|
||||||
if (showMessage)
|
if (showMessage)
|
||||||
ShowWarningMsg("game folder does not exist in the game root folder", "Error");
|
ShowWarningMsg("Game path does not exist", "Error");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!File.Exists(Path.Combine(LauncherPath, "nikke_launcher.exe")))
|
var result = ServerSwitcher.SetBasePath(BasePath);
|
||||||
|
|
||||||
|
if (!result.Item1)
|
||||||
{
|
{
|
||||||
SetGamePathValid(false);
|
SetGamePathValid(false);
|
||||||
if (showMessage)
|
if (showMessage)
|
||||||
ShowWarningMsg("Game path is invalid. Make sure that nikke_launcher.exe exists in the <Game Path>/launcher folder", "Error");
|
ShowWarningMsg(result.Item2, "Error");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,11 +100,11 @@ public partial class MainView : UserControl
|
|||||||
|
|
||||||
private async void UpdateIntegrityLabel()
|
private async void UpdateIntegrityLabel()
|
||||||
{
|
{
|
||||||
if (!ValidatePaths(false) || txtGamePath.Text == null || GamePath == null || LauncherPath == null)
|
if (!ValidatePaths(false) || txtGamePath.Text == null || BasePath == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SetLoadingScreenVisible(true);
|
SetLoadingScreenVisible(true);
|
||||||
LblStatus.Text = "Status: " + await ServerSwitcher.CheckIntegrity(GamePath, LauncherPath);
|
LblStatus.Text = "Status: " + await ServerSwitcher.CheckIntegrity();
|
||||||
SetLoadingScreenVisible(false);
|
SetLoadingScreenVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +124,7 @@ public partial class MainView : UserControl
|
|||||||
|
|
||||||
private async void Save_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
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;
|
return;
|
||||||
|
|
||||||
if (CmbServerSelection.SelectedIndex == 1 && !IPAddress.TryParse(TxtIpAddress.Text, out _))
|
if (CmbServerSelection.SelectedIndex == 1 && !IPAddress.TryParse(TxtIpAddress.Text, out _))
|
||||||
@@ -150,7 +143,7 @@ public partial class MainView : UserControl
|
|||||||
SetLoadingScreenVisible(true);
|
SetLoadingScreenVisible(true);
|
||||||
try
|
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)
|
if (!res.IsSupported)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user