Upload via GUI (180 Dateien)

This commit is contained in:
2026-10-03 19:51:56 +00:00
parent 9b843ecab1
commit ddc30b0fe9
38 changed files with 7174 additions and 3688 deletions
+101
View File
@@ -0,0 +1,101 @@
using System.IO;
using System.Text.Json;
using System.Text.RegularExpressions;
using Microsoft.Win32;
namespace DeskCore;
/// <summary>
/// Erkennt, ob ein Spiel aus einem Steam- oder Epic-Link (.url) noch installiert ist.
/// Ergebnis null = "weiß ich nicht" → DeskCore lässt den Link dann sicherheitshalber stehen.
/// </summary>
public static class GameLibraries
{
private static readonly Regex SteamRun = new(@"^steam://(?:rungameid|run|launch)/(\d+)", RegexOptions.IgnoreCase);
private const string EpicPrefix = "com.epicgames.launcher://apps/";
/// <summary>Ist das Spiel hinter diesem Launcher-Link noch installiert? (null = nicht feststellbar)</summary>
public static bool? IsInstalled(string url)
{
url = url.Trim();
var m = SteamRun.Match(url);
if (m.Success) return SteamAppInstalled(m.Groups[1].Value);
if (url.StartsWith(EpicPrefix, StringComparison.OrdinalIgnoreCase)) return EpicAppInstalled(url);
return null;
}
// ---------- Steam ----------
private static (DateTime When, List<string>? Libraries) _steamCache;
/// <summary>Alle Steam-Bibliotheksordner – oder null, wenn Steam fehlt bzw. eine Bibliothek gerade nicht erreichbar ist.</summary>
private static List<string>? SteamLibraries()
{
if (DateTime.UtcNow - _steamCache.When < TimeSpan.FromSeconds(30)) return _steamCache.Libraries;
List<string>? result = null;
try
{
using var k = Registry.CurrentUser.OpenSubKey(@"Software\Valve\Steam");
if (k?.GetValue("SteamPath") is string sp && sp.Length > 0)
{
var steamPath = Path.GetFullPath(sp.Replace('/', '\\'));
var libs = new List<string> { steamPath };
var vdf = Path.Combine(steamPath, "steamapps", "libraryfolders.vdf");
if (File.Exists(vdf))
{
foreach (Match lm in Regex.Matches(File.ReadAllText(vdf), "\"path\"\\s+\"([^\"]+)\""))
libs.Add(Path.GetFullPath(lm.Groups[1].Value.Replace(@"\\", @"\")));
}
libs = libs.Distinct(StringComparer.OrdinalIgnoreCase).ToList();
// Liegt eine Bibliothek auf einem abgesteckten Laufwerk, könnte das Spiel dort sein → nichts entscheiden
result = libs.All(l => Directory.Exists(Path.Combine(l, "steamapps"))) ? libs : null;
}
}
catch { result = null; }
_steamCache = (DateTime.UtcNow, result);
return result;
}
private static bool? SteamAppInstalled(string appId)
{
var libs = SteamLibraries();
if (libs is null) return null;
return libs.Any(l => File.Exists(Path.Combine(l, "steamapps", $"appmanifest_{appId}.acf")));
}
// ---------- Epic Games ----------
private static bool? EpicAppInstalled(string url)
{
// Format: com.epicgames.launcher://apps/<namespace>%3A<katalog>%3A<AppName>?action=launch
// oder com.epicgames.launcher://apps/<AppName>?action=launch
var rest = url[EpicPrefix.Length..];
var q = rest.IndexOf('?');
if (q >= 0) rest = rest[..q];
var appName = Uri.UnescapeDataString(rest).Split(':').Last().Trim();
if (appName.Length == 0) return null;
var manifests = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"Epic", "EpicGamesLauncher", "Data", "Manifests");
if (!Directory.Exists(manifests)) return null; // Epic-Launcher nicht (mehr) vorhanden → nicht entscheiden
try
{
foreach (var f in Directory.EnumerateFiles(manifests, "*.item"))
{
using var doc = JsonDocument.Parse(File.ReadAllText(f));
var root = doc.RootElement;
if (!root.TryGetProperty("AppName", out var an) ||
!string.Equals(an.GetString(), appName, StringComparison.OrdinalIgnoreCase)) continue;
// Installationsort auf abgestecktem Laufwerk? → nicht entscheiden
if (root.TryGetProperty("InstallLocation", out var loc) && loc.GetString() is { Length: > 0 } dir)
{
var drive = new DriveInfo(Path.GetPathRoot(dir)!);
if (!drive.IsReady) return null;
}
return true;
}
return false;
}
catch { return null; }
}
}