From d288818cb6c1adee45628d4773302c63442689e1 Mon Sep 17 00:00:00 2001 From: Ckuma Date: Wed, 17 Jun 2026 14:06:28 -0400 Subject: [PATCH] Detect Star Citizen installs on any drive and custom library folders (C#) Port the Python detection improvements to the C# (WinForms) app. DetectInstallations previously only checked the fixed layout ":\Program Files\Roberts Space Industries\StarCitizen" on drives C-G, so an install in a custom library folder (e.g. G:\Games\StarCitizen) was never found. - Scan every fixed drive via DriveInfo, not a hardcoded list. - Cover common custom roots (Games\StarCitizen, bare StarCitizen, etc.). - Read the authoritative install path from the RSI Launcher log (%APPDATA%\rsilauncher\logs\log.log). All candidates are still verified against \logbackups on disk before being offered. --- csharp/Form1.cs | 100 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 14 deletions(-) diff --git a/csharp/Form1.cs b/csharp/Form1.cs index 8696fb7..df34e26 100644 --- a/csharp/Form1.cs +++ b/csharp/Form1.cs @@ -3,6 +3,7 @@ using System.Drawing; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Forms; @@ -49,25 +50,34 @@ private void DetectInstallations() detectedPaths.Clear(); comboEnvironment.Items.Clear(); - // Common installation paths to check - string[] drivesToCheck = { "C", "D", "E", "F", "G" }; string[] environments = { "LIVE", "PTU", "EPTU", "TECH-PREVIEW" }; - foreach (var drive in drivesToCheck) + var seenBases = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var rawBase in GetCandidateBasePaths()) { - string basePath = $@"{drive}:\Program Files\Roberts Space Industries\StarCitizen"; + string basePath; + try { basePath = Path.GetFullPath(rawBase); } + catch { continue; } - if (Directory.Exists(basePath)) + if (!seenBases.Add(basePath) || !Directory.Exists(basePath)) + continue; + + string driveLetter = basePath.Length > 0 + ? char.ToUpperInvariant(basePath[0]).ToString() + : ""; + + foreach (var env in environments) { - foreach (var env in environments) - { - string logPath = Path.Combine(basePath, env, "logbackups"); - if (Directory.Exists(logPath)) - { - string key = drive == "C" ? env : $"{env} ({drive}:)"; - detectedPaths[key] = logPath; - } - } + string logPath = Path.Combine(basePath, env, "logbackups"); + if (!Directory.Exists(logPath)) + continue; + + string key = driveLetter == "C" ? env : $"{env} ({driveLetter}:)"; + // If two installs collide on the same key, disambiguate by full path. + if (detectedPaths.TryGetValue(key, out var existing) && existing != logPath) + key = $"{env} ({basePath})"; + if (!detectedPaths.ContainsKey(key)) + detectedPaths[key] = logPath; } } @@ -94,6 +104,68 @@ private void DetectInstallations() } } + // Candidate Star Citizen base install directories: common roots on every + // fixed drive, plus any custom library folder recorded by the RSI Launcher. + private static List GetCandidateBasePaths() + { + var bases = new List(); + string[] commonRoots = + { + @"Program Files\Roberts Space Industries\StarCitizen", + @"Roberts Space Industries\StarCitizen", + @"Games\Roberts Space Industries\StarCitizen", + @"Games\StarCitizen", + @"StarCitizen", + }; + + foreach (var drive in DriveInfo.GetDrives()) + { + if (!drive.IsReady || drive.DriveType != DriveType.Fixed) + continue; + foreach (var root in commonRoots) + bases.Add(Path.Combine(drive.RootDirectory.FullName, root)); + } + + // User AppData. + bases.Add(Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + @"Roberts Space Industries\StarCitizen")); + + // Authoritative source for custom library folders. + bases.AddRange(PathsFromLauncherLog()); + return bases; + } + + // Star Citizen base dirs recorded by the RSI Launcher log. The launcher's + // settings store is encrypted, but it logs plaintext lines such as + // "Installing Star Citizen LIVE ... at G:\Games\StarCitizen" - the only + // reliable way to find an install in a custom library folder. + private static List PathsFromLauncherLog() + { + var bases = new List(); + string logPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + @"rsilauncher\logs\log.log"); + if (!File.Exists(logPath)) + return bases; + + string content; + try { content = File.ReadAllText(logPath); } + catch { return bases; } + + // Backslashes are JSON-escaped (doubled) in the log; the match is + // non-greedy so it stops at the shallowest StarCitizen directory. + var rx = new Regex(@"[A-Za-z]:(?:\\\\[^\\""(),]+)*?\\\\StarCitizen(?![A-Za-z0-9])"); + var seen = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (Match m in rx.Matches(content)) + { + string p = m.Value.Replace(@"\\", @"\"); + if (seen.Add(p)) + bases.Add(p); + } + return bases; + } + private void comboEnvironment_SelectedIndexChanged(object sender, EventArgs e) { string selected = comboEnvironment.SelectedItem?.ToString();