From 2fce4fb13c9838611e059efcf27e30c0cbd7c283 Mon Sep 17 00:00:00 2001 From: erri120 Date: Tue, 1 Oct 2024 12:31:14 +0200 Subject: [PATCH] Provide wine prefix for heroic --- CHANGELOG.md | 4 ++ src/GameFinder.Launcher.Heroic/DTOs/Json.cs | 29 ++++++++++++ .../GameFinder.Launcher.Heroic.csproj | 1 + .../HeroicGOGGame.cs | 22 +++++++++ .../HeroicGOGHandler.cs | 46 +++++++++++++++---- src/GameFinder.Wine/WinePrefix.cs | 10 +++- src/GameFinder/GameFinder.csproj | 1 + 7 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 src/GameFinder.Launcher.Heroic/HeroicGOGGame.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 544ac1b0..aa23570a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com) and this p ## [Released](https://github.com/erri120/GameFinder/releases) +## [4.3.1](https://github.com/erri120/GameFinder/compare/v4.3.0...v4.3.1) - 2024-10-01 + +- Heroic: pass along the wine prefix + ## [4.3.0](https://github.com/erri120/GameFinder/compare/v4.2.4...v4.3.0) - 2024-09-28 Adds support for Heroic. diff --git a/src/GameFinder.Launcher.Heroic/DTOs/Json.cs b/src/GameFinder.Launcher.Heroic/DTOs/Json.cs index 506ee752..a4fc851e 100644 --- a/src/GameFinder.Launcher.Heroic/DTOs/Json.cs +++ b/src/GameFinder.Launcher.Heroic/DTOs/Json.cs @@ -22,5 +22,34 @@ internal record Root( [property: JsonPropertyName("installed")] IReadOnlyList Installed ); +internal record GameConfig( + [property: JsonPropertyName("autoInstallDxvk")] bool AutoInstallDxvk, + [property: JsonPropertyName("autoInstallDxvkNvapi")] bool AutoInstallDxvkNvapi, + [property: JsonPropertyName("autoInstallVkd3d")] bool AutoInstallVkd3d, + [property: JsonPropertyName("preferSystemLibs")] bool PreferSystemLibs, + [property: JsonPropertyName("enableEsync")] bool EnableEsync, + [property: JsonPropertyName("enableMsync")] bool EnableMsync, + [property: JsonPropertyName("enableFsync")] bool EnableFsync, + [property: JsonPropertyName("nvidiaPrime")] bool NvidiaPrime, + [property: JsonPropertyName("enviromentOptions")] IReadOnlyList EnviromentOptions, + [property: JsonPropertyName("wrapperOptions")] IReadOnlyList WrapperOptions, + [property: JsonPropertyName("showFps")] bool ShowFps, + [property: JsonPropertyName("useGameMode")] bool UseGameMode, + [property: JsonPropertyName("battlEyeRuntime")] bool BattlEyeRuntime, + [property: JsonPropertyName("eacRuntime")] bool EacRuntime, + [property: JsonPropertyName("language")] string Language, + [property: JsonPropertyName("beforeLaunchScriptPath")] string BeforeLaunchScriptPath, + [property: JsonPropertyName("afterLaunchScriptPath")] string AfterLaunchScriptPath, + [property: JsonPropertyName("wineVersion")] WineVersion WineVersion, + [property: JsonPropertyName("winePrefix")] string WinePrefix, + [property: JsonPropertyName("wineCrossoverBottle")] string WineCrossoverBottle +); + +public record WineVersion( + [property: JsonPropertyName("bin")] string Bin, + [property: JsonPropertyName("name")] string Name, + [property: JsonPropertyName("type")] string Type +); + diff --git a/src/GameFinder.Launcher.Heroic/GameFinder.Launcher.Heroic.csproj b/src/GameFinder.Launcher.Heroic/GameFinder.Launcher.Heroic.csproj index 28c25fb9..88f5c2b1 100644 --- a/src/GameFinder.Launcher.Heroic/GameFinder.Launcher.Heroic.csproj +++ b/src/GameFinder.Launcher.Heroic/GameFinder.Launcher.Heroic.csproj @@ -5,5 +5,6 @@ + diff --git a/src/GameFinder.Launcher.Heroic/HeroicGOGGame.cs b/src/GameFinder.Launcher.Heroic/HeroicGOGGame.cs new file mode 100644 index 00000000..5d356dc8 --- /dev/null +++ b/src/GameFinder.Launcher.Heroic/HeroicGOGGame.cs @@ -0,0 +1,22 @@ +using GameFinder.StoreHandlers.GOG; +using GameFinder.Wine; +using NexusMods.Paths; + +namespace GameFinder.Launcher.Heroic; + +public record HeroicGOGGame( + GOGGameId Id, + string Name, + AbsolutePath Path, + AbsolutePath WinePrefixPath, + DTOs.WineVersion WineVersion) : GOGGame(Id, Name, Path) +{ + public WinePrefix GetWinePrefix() + { + return new WinePrefix + { + ConfigurationDirectory = WinePrefixPath.Combine("pfx"), + UserName = "steamuser", + }; + } +} diff --git a/src/GameFinder.Launcher.Heroic/HeroicGOGHandler.cs b/src/GameFinder.Launcher.Heroic/HeroicGOGHandler.cs index be593c78..b4adf7f0 100644 --- a/src/GameFinder.Launcher.Heroic/HeroicGOGHandler.cs +++ b/src/GameFinder.Launcher.Heroic/HeroicGOGHandler.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; @@ -20,6 +21,7 @@ public class HeroicGOGHandler : AHandler private static readonly JsonSerializerOptions JsonSerializerOptions = new() { AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip, }; /// @@ -39,24 +41,25 @@ public HeroicGOGHandler(IFileSystem fileSystem) /// public override IEnumerable> FindAllGames() { - var installedJsonFile = FindConfigDirectory(_fileSystem) - .Select(GetInstalledJsonFilePath) - .FirstOrDefault(path => path.FileExists); + var configDirectory = FindConfigDirectory(_fileSystem) + .FirstOrDefault(path => path.DirectoryExists()); - if (installedJsonFile == default) + if (configDirectory == default) { yield return new ErrorMessage("Didn't find any heroic files, this can be ignored if heroic isn't installed"); yield break; } - var games = ParseInstalledJsonFile(installedJsonFile); + var installedJsonFile = GetInstalledJsonFilePath(configDirectory); + + var games = ParseInstalledJsonFile(installedJsonFile, configDirectory); foreach (var x in games) { yield return x; } } - internal static IEnumerable> ParseInstalledJsonFile(AbsolutePath path) + internal static IEnumerable> ParseInstalledJsonFile(AbsolutePath path, AbsolutePath configPath) { using var stream = path.Open(FileMode.Open, FileAccess.Read, FileShare.Read); var root = JsonSerializer.Deserialize(stream, JsonSerializerOptions); @@ -71,7 +74,7 @@ internal static IEnumerable> ParseInstalledJsonFile OneOf res; try { - res = Parse(installed, path.FileSystem); + res = Parse(installed, configPath, path.FileSystem); } catch (Exception e) { @@ -82,15 +85,40 @@ internal static IEnumerable> ParseInstalledJsonFile } } - internal static OneOf Parse(DTOs.Installed installed, IFileSystem fileSystem) + [RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.Deserialize(JsonSerializerOptions)")] + internal static OneOf Parse( + DTOs.Installed installed, + AbsolutePath configPath, + IFileSystem fileSystem) { if (!long.TryParse(installed.AppName, NumberStyles.Integer, CultureInfo.InvariantCulture, out var id)) { return new ErrorMessage($"The value \"appName\" is not a number: \"{installed.AppName}\""); } + var gamesConfigFile = GetGamesConfigJsonFile(configPath, id.ToString(CultureInfo.InvariantCulture)); + if (!gamesConfigFile.FileExists) return new ErrorMessage($"File `{gamesConfigFile}` doesn't exist!"); + + using var stream = gamesConfigFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read); + using var doc = JsonDocument.Parse(stream, new JsonDocumentOptions + { + AllowTrailingCommas = true, + CommentHandling = JsonCommentHandling.Skip, + }); + + var element = doc.RootElement.GetProperty(id.ToString(CultureInfo.InvariantCulture)); + var gameConfig = element.Deserialize(); + if (gameConfig is null) return new ErrorMessage($"Unable to deserialize `{gamesConfigFile}`"); + var path = fileSystem.FromUnsanitizedFullPath(installed.InstallPath); - return new GOGGame(GOGGameId.From(id), installed.AppName, path); + var winePrefixPath = fileSystem.FromUnsanitizedFullPath(gameConfig.WinePrefix); + + return new HeroicGOGGame(GOGGameId.From(id), installed.AppName, path, winePrefixPath, gameConfig.WineVersion); + } + + internal static AbsolutePath GetGamesConfigJsonFile(AbsolutePath configPath, string name) + { + return configPath.Combine("GamesConfig").Combine($"{name}.json"); } internal static AbsolutePath GetInstalledJsonFilePath(AbsolutePath configPath) diff --git a/src/GameFinder.Wine/WinePrefix.cs b/src/GameFinder.Wine/WinePrefix.cs index b0974e6b..cfeb22f1 100644 --- a/src/GameFinder.Wine/WinePrefix.cs +++ b/src/GameFinder.Wine/WinePrefix.cs @@ -6,4 +6,12 @@ namespace GameFinder.Wine; /// Represents a wine prefix. /// [PublicAPI] -public record WinePrefix : AWinePrefix; +public record WinePrefix : AWinePrefix +{ + public string? UserName { get; init; } + + protected override string GetUserName() + { + return UserName ?? base.GetUserName(); + } +} diff --git a/src/GameFinder/GameFinder.csproj b/src/GameFinder/GameFinder.csproj index d13d241c..52f18b20 100644 --- a/src/GameFinder/GameFinder.csproj +++ b/src/GameFinder/GameFinder.csproj @@ -21,6 +21,7 @@ +