using System.Xml; using System.Xml.Linq; using CVRManager.Models; namespace CVRManager.Services; public static class AccountService { const string ProfilePrefix = "cvrm-"; public record Found(string Name, string Username, string UserId, string AccessKey); public static string ProfileName(GameAccount a) => ProfilePrefix + a.Id; static string ProfilePath(string gameDir, GameAccount a) => Path.Combine(gameDir, "ChilloutVR_Data", $"autologin-{ProfileName(a)}.profile"); public static string WriteProfile(string gameDir, GameAccount a) { var key = TokenVault.Unprotect(a.AccessKey); if (key.Length == 0) throw new InvalidOperationException($"The access key for {a.Name} could not be read; add the account again."); var doc = new XElement("LoginProfile", new XElement("UserId", a.UserId), new XElement("Username", a.Username), new XElement("AccessKey", key)); File.WriteAllText(ProfilePath(gameDir, a), doc.ToString()); return ProfileName(a); } public static IEnumerable Scan(IEnumerable gameDirs) { foreach (var dir in gameDirs) { var data = Path.Combine(dir, "ChilloutVR_Data"); if (!Directory.Exists(data)) continue; foreach (var file in Directory.EnumerateFiles(data, "autologin*.profile")) { if (Path.GetFileName(file).StartsWith("autologin-" + ProfilePrefix, StringComparison.OrdinalIgnoreCase)) continue; if (Read(file) is { } found) yield return found; } } } static Found? Read(string path) { try { var root = XDocument.Load(path).Root; if (root == null || root.Name != "LoginProfile") return null; string Value(string name) => root.Element(name)?.Value.Trim() ?? ""; var username = Value("Username"); var key = Value("AccessKey"); if (username.Length == 0 || key.Length == 0) return null; var stem = Path.GetFileNameWithoutExtension(path)["autologin".Length..].TrimStart('_', '-'); return new Found(stem.Length > 0 ? stem : username, username, Value("UserId"), key); } catch (Exception e) when (e is IOException or UnauthorizedAccessException or XmlException) { return null; } } }