using System.Diagnostics; using System.IO.Compression; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.RegularExpressions; using Microsoft.Win32; namespace BSO.Launcher; /// /// Bus-Simulator 2012 finden (alle Steam-Bibliotheken), BSO in den Spielordner installieren (dinput8.dll + BSO\), /// BSO.json fuer die Mod schreiben (Name = Kontoname, Server) und das Spiel ueber Steam starten. /// public static class Game { public const string SteamAppId = "253770"; const string GameFolderName = "Bus-Simulator 2012"; const string BinFolder = "Bin_High_Win64"; // BSO laeuft nur in der 64-Bit-Version const string ExeName = "BusSimulator2012.exe"; /// Spielordner (der mit Bin_High_Win64) oder null. public static string? FindFolder(Settings settings) { if (IsGameFolder(settings.GameFolder)) return settings.GameFolder; foreach (string library in SteamLibraries()) { string folder = Path.Combine(library, "steamapps", "common", GameFolderName); if (IsGameFolder(folder)) return folder; } return null; } public static bool IsGameFolder(string? folder) => !string.IsNullOrEmpty(folder) && File.Exists(Path.Combine(folder, BinFolder, ExeName)); public static string Bin(string folder) => Path.Combine(folder, BinFolder); static IEnumerable SteamLibraries() { string? steam = null; try { steam = Registry.CurrentUser.OpenSubKey(@"Software\Valve\Steam")?.GetValue("SteamPath") as string; } catch { } steam ??= Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Steam"); steam = steam.Replace('/', '\\'); yield return steam; string vdf = Path.Combine(steam, "steamapps", "libraryfolders.vdf"); if (!File.Exists(vdf)) yield break; foreach (Match m in Regex.Matches(File.ReadAllText(vdf), "\"path\"\\s+\"([^\"]+)\"")) yield return m.Groups[1].Value.Replace(@"\\", @"\"); } /// Installierte BSO-Version (aus BSO.dll) oder null. public static string? ModVersion(string folder) { string dll = Path.Combine(Bin(folder), "BSO", "BSO.dll"); if (!File.Exists(dll) || !File.Exists(Path.Combine(Bin(folder), "dinput8.dll"))) return null; return FileVersionInfo.GetVersionInfo(dll).ProductVersion?.Split('+')[0]; } /// Die Mod braucht die .NET 10 Runtime (x64) - der Launcher selbst bringt seine mit, die Mod nicht. public static bool DotnetInstalled() { string shared = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet", "shared", "Microsoft.NETCore.App"); return Directory.Exists(shared) && Directory.GetDirectories(shared, "10.*").Length > 0; } public const string DotnetDownload = "https://dotnet.microsoft.com/download/dotnet/10.0"; public static bool IsRunning() => Process.GetProcessesByName(Path.GetFileNameWithoutExtension(ExeName)).Length > 0; /// Mod-Paket (BSO-Mod.zip: dinput8.dll, BSO\*) in den Spielordner entpacken. BSO.json bleibt. public static void InstallMod(string folder, byte[] zip) { if (IsRunning()) throw new Exception("Erst Bus-Simulator 2012 schliessen - solange es laeuft, sind die Mod-Dateien gesperrt"); string bin = Bin(folder); using var archive = new ZipArchive(new MemoryStream(zip), ZipArchiveMode.Read); foreach (ZipArchiveEntry entry in archive.Entries) { if (entry.FullName.EndsWith('/')) continue; string target = Path.GetFullPath(Path.Combine(bin, entry.FullName)); if (!target.StartsWith(Path.GetFullPath(bin) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) throw new Exception("Ungueltiger Pfad im Mod-Paket: " + entry.FullName); Directory.CreateDirectory(Path.GetDirectoryName(target)!); entry.ExtractToFile(target, true); } LauncherLog.Info("BSO installiert nach " + bin); } /// Einstellungen der Mod (BSO\BSO.json) - Name = Kontoname, Server, automatisch verbinden. public static void WriteModConfig(string folder, string? name, string server, bool autoConnect) { string file = Path.Combine(Bin(folder), "BSO", "BSO.json"); JsonObject json; try { json = File.Exists(file) ? JsonNode.Parse(File.ReadAllText(file)) as JsonObject ?? new JsonObject() : new JsonObject(); } catch { json = new JsonObject(); } if (!string.IsNullOrEmpty(name)) json["Name"] = name; json["Server"] = server; json["AutoConnect"] = autoConnect; Directory.CreateDirectory(Path.GetDirectoryName(file)!); File.WriteAllText(file, json.ToJsonString(new JsonSerializerOptions { WriteIndented = true })); } /// Ueber Steam starten (Overlay, Steam-Anmeldung des Spiels). public static void Launch() => Process.Start(new ProcessStartInfo("steam://rungameid/" + SteamAppId) { UseShellExecute = true }); /// BSO aus dem Spielordner entfernen (Deinstallieren). Gibt Probleme als Text zurueck. public static List RemoveBso(Settings settings) { var problems = new List(); string? folder = FindFolder(settings); if (folder == null) return problems; string bin = Bin(folder); try { File.Delete(Path.Combine(bin, "dinput8.dll")); } catch (Exception e) { problems.Add("dinput8.dll: " + e.Message); } try { if (Directory.Exists(Path.Combine(bin, "BSO"))) Directory.Delete(Path.Combine(bin, "BSO"), true); } catch (Exception e) { problems.Add("BSO-Ordner: " + e.Message); } return problems; } }