Skip to content

Commit

Permalink
Merge pull request #151 from erri120/heroic
Browse files Browse the repository at this point in the history
Add heroic support
  • Loading branch information
erri120 authored Sep 28, 2024
2 parents 0038348 + 58c8f77 commit 504d7c7
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 17 deletions.
24 changes: 12 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
continue-on-error: true

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Check formatting
run: dotnet format --verify-no-changes --severity error
Expand All @@ -26,7 +26,7 @@ jobs:
OS: ${{ matrix.os }}

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Print Debug Info
run: dotnet --info
Expand All @@ -40,14 +40,14 @@ jobs:
- name: Run Tests
run: dotnet test --no-build --no-restore --logger "GitHubActions" --collect:"XPlat Code Coverage;Format=opencover"

- name: Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage.xml
fail_ci_if_error: false
env_vars: OS
flags: ${{ env.OS }}
# - name: Codecov
# uses: codecov/codecov-action@v3
# with:
# token: ${{ secrets.CODECOV_TOKEN }}
# files: coverage.xml
# fail_ci_if_error: false
# env_vars: OS
# flags: ${{ env.OS }}

- name: Publish (Linux)
if: runner.os == 'Linux'
Expand All @@ -62,8 +62,8 @@ jobs:
run: dotnet publish other/GameFinder.Example/GameFinder.Example.csproj -o ${{ github.workspace }}/bin/${{ runner.os }} -p:PublishReadyToRun=true -p:PublishSingleFile=true -p:PublishTrimmed=false

- name: Upload Artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: GameFinder.Example-${{ runner.os }}-${{ github.sha }}
name: GameFinder.Example-${{ env.OS }}-${{ github.sha }}
path: ${{ github.workspace }}/bin/${{ runner.os }}/GameFinder*
if-no-files-found: error
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
$current_version = $env:INPUT_VERSION.StartsWith('v') ? $env:INPUT_VERSION.Substring(1) : $env:INPUT_VERSION
echo "current_version=$current_version" >> $env:GITHUB_OUTPUT
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Print Debug Info
run: dotnet --info
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/validate-codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Validate
shell: bash
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.0](https://github.com/erri120/GameFinder/compare/v4.2.4...v4.3.0) - 2024-09-28

Adds support for Heroic.

## [4.2.4](https://github.com/erri120/GameFinder/compare/v4.2.3...v4.2.4) - 2024-07-04

- Steam: find Flatpak installation
Expand Down
6 changes: 3 additions & 3 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="FluentResults" Version="3.15.2" />
<PackageVersion Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageVersion Include="NexusMods.Paths" Version="0.9.4" />
<PackageVersion Include="NexusMods.Paths.TestingHelpers" Version="0.9.4" />
<PackageVersion Include="NexusMods.Paths" Version="0.10.0" />
<PackageVersion Include="NexusMods.Paths.TestingHelpers" Version="0.10.0" />
<PackageVersion Include="NLog" Version="5.2.8" />
<PackageVersion Include="NLog.Extensions.Logging" Version="5.3.8" />
<PackageVersion Include="OneOf" Version="3.0.271" />
Expand Down Expand Up @@ -49,4 +49,4 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
</ItemGroup>
</Project>
</Project>
7 changes: 7 additions & 0 deletions GameFinder.sln
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameFinder.StoreHandlers.Xb
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameFinder", "src\GameFinder\GameFinder.csproj", "{8F9D4B7D-5269-4053-BB20-9DB7B5F8DF8E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameFinder.Launcher.Heroic", "src\GameFinder.Launcher.Heroic\GameFinder.Launcher.Heroic.csproj", "{F0805D2B-7B2C-4F54-A09A-D1FBE02F3D35}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -179,6 +181,10 @@ Global
{8F9D4B7D-5269-4053-BB20-9DB7B5F8DF8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8F9D4B7D-5269-4053-BB20-9DB7B5F8DF8E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8F9D4B7D-5269-4053-BB20-9DB7B5F8DF8E}.Release|Any CPU.Build.0 = Release|Any CPU
{F0805D2B-7B2C-4F54-A09A-D1FBE02F3D35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F0805D2B-7B2C-4F54-A09A-D1FBE02F3D35}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F0805D2B-7B2C-4F54-A09A-D1FBE02F3D35}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F0805D2B-7B2C-4F54-A09A-D1FBE02F3D35}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{58A62CC7-4A61-4218-9F93-CE85FDF00E15} = {B765999C-AB3D-495F-9F5E-9108F2D4FE33}
Expand Down Expand Up @@ -206,5 +212,6 @@ Global
{2F75FCA7-DCE5-4162-B2AE-24994D14FC03} = {B765999C-AB3D-495F-9F5E-9108F2D4FE33}
{7D04AF8D-E8AE-44DB-AB36-5452594DC789} = {9C6671E1-A94D-4C76-9F61-78B491A62408}
{8F9D4B7D-5269-4053-BB20-9DB7B5F8DF8E} = {EFB4E7DB-DE24-4BDE-B660-DAF1394ECC21}
{F0805D2B-7B2C-4F54-A09A-D1FBE02F3D35} = {B765999C-AB3D-495F-9F5E-9108F2D4FE33}
EndGlobalSection
EndGlobal
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- [Origin](#origin) [![Nuget](https://img.shields.io/nuget/v/GameFinder.StoreHandlers.Origin)](https://www.nuget.org/packages/GameFinder.StoreHandlers.Origin)
- [EA Desktop](#ea-desktop) [![Nuget](https://img.shields.io/nuget/v/GameFinder.StoreHandlers.EADesktop)](https://www.nuget.org/packages/GameFinder.StoreHandlers.EADesktop)
- [Xbox Game Pass](#xbox-game-pass) [![Nuget](https://img.shields.io/nuget/v/GameFinder.StoreHandlers.Xbox)](https://www.nuget.org/packages/GameFinder.StoreHandlers.Xbox)
- Heroic [![Nuget](https://img.shields.io/nuget/v/GameFinder.Launcher.Heroic)](https://www.nuget.org/packages/GameFinder.Launcher.Heroic)

The following launchers are not yet supported or support has been dropped:

Expand Down
1 change: 1 addition & 0 deletions other/GameFinder.Example/GameFinder.Example.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\GameFinder.Launcher.Heroic\GameFinder.Launcher.Heroic.csproj" />
<ProjectReference Include="..\..\src\GameFinder.StoreHandlers.EGS\GameFinder.StoreHandlers.EGS.csproj" />
<ProjectReference Include="..\..\src\GameFinder.StoreHandlers.GOG\GameFinder.StoreHandlers.GOG.csproj" />
<ProjectReference Include="..\..\src\GameFinder.StoreHandlers.Origin\GameFinder.StoreHandlers.Origin.csproj" />
Expand Down
3 changes: 3 additions & 0 deletions other/GameFinder.Example/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,7 @@ public class Options

[Option("bottles", HelpText = "Search for wine prefixes managed with bottles")]
public bool Bottles { get; set; } = true;

[Option("heroic", HelpText = "Search for games from Heroic")]
public bool Heroic { get; set; } = true;
}
13 changes: 13 additions & 0 deletions other/GameFinder.Example/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Runtime.InteropServices;
using CommandLine;
using GameFinder.Common;
using GameFinder.Launcher.Heroic;
using GameFinder.RegistryUtils;
using GameFinder.StoreHandlers.EADesktop;
using GameFinder.StoreHandlers.EADesktop.Crypto;
Expand Down Expand Up @@ -108,6 +109,11 @@ private static void Run(Options options, ILogger logger)
if (OperatingSystem.IsLinux())
{
if (options.Steam) RunSteamHandler(realFileSystem, registry: null);
if (options.Heroic)
{
RunHeroicGOGHandler(realFileSystem);
}

var winePrefixes = new List<AWinePrefix>();

if (options.Wine)
Expand Down Expand Up @@ -148,6 +154,13 @@ private static void RunGOGHandler(IRegistry registry, IFileSystem fileSystem)
LogGamesAndErrors(handler.FindAllGames(), logger);
}

private static void RunHeroicGOGHandler(IFileSystem fileSystem)
{
var logger = _provider.CreateLogger(nameof(HeroicGOGHandler));
var handler = new HeroicGOGHandler(fileSystem);
LogGamesAndErrors(handler.FindAllGames(), logger);
}

private static void RunEGSHandler(IRegistry registry, IFileSystem fileSystem)
{
var logger = _provider.CreateLogger(nameof(EGSHandler));
Expand Down
26 changes: 26 additions & 0 deletions src/GameFinder.Launcher.Heroic/DTOs/Json.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace GameFinder.Launcher.Heroic.DTOs;

internal record Installed(
[property: JsonPropertyName("platform")] string Platform,
[property: JsonPropertyName("executable")] string Executable,
[property: JsonPropertyName("install_path")] string InstallPath,
[property: JsonPropertyName("install_size")] string InstallSize,
[property: JsonPropertyName("is_dlc")] bool IsDlc,
[property: JsonPropertyName("version")] string Version,
[property: JsonPropertyName("appName")] string AppName,
[property: JsonPropertyName("installedDLCs")] IReadOnlyList<object> InstalledDLCs,
[property: JsonPropertyName("language")] string Language,
[property: JsonPropertyName("versionEtag")] string VersionEtag,
[property: JsonPropertyName("buildId")] string BuildId,
[property: JsonPropertyName("pinnedVersion")] bool PinnedVersion
);

internal record Root(
[property: JsonPropertyName("installed")] IReadOnlyList<Installed> Installed
);



Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsTrimmable>false</IsTrimmable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\GameFinder.StoreHandlers.GOG\GameFinder.StoreHandlers.GOG.csproj" />
</ItemGroup>
</Project>
113 changes: 113 additions & 0 deletions src/GameFinder.Launcher.Heroic/HeroicGOGHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.Json;
using GameFinder.Common;
using GameFinder.StoreHandlers.GOG;
using JetBrains.Annotations;
using NexusMods.Paths;
using OneOf;

namespace GameFinder.Launcher.Heroic;

[PublicAPI]
public class HeroicGOGHandler : AHandler<GOGGame, GOGGameId>
{
private readonly IFileSystem _fileSystem;

private static readonly JsonSerializerOptions JsonSerializerOptions = new()
{
AllowTrailingCommas = true,
};

/// <summary>
/// Constructor.
/// </summary>
public HeroicGOGHandler(IFileSystem fileSystem)
{
_fileSystem = fileSystem;
}

/// <inheritdoc/>
public override Func<GOGGame, GOGGameId> IdSelector { get; } = static game => game.Id;

/// <inheritdoc/>
public override IEqualityComparer<GOGGameId>? IdEqualityComparer => null;

/// <inheritdoc/>
public override IEnumerable<OneOf<GOGGame, ErrorMessage>> FindAllGames()
{
var installedJsonFile = FindConfigDirectory(_fileSystem)
.Select(GetInstalledJsonFilePath)
.FirstOrDefault(path => path.FileExists);

if (installedJsonFile == 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);
foreach (var x in games)
{
yield return x;
}
}

internal static IEnumerable<OneOf<GOGGame, ErrorMessage>> ParseInstalledJsonFile(AbsolutePath path)
{
using var stream = path.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
var root = JsonSerializer.Deserialize<DTOs.Root>(stream, JsonSerializerOptions);
if (root is null)
{
yield return new ErrorMessage($"Unable to deserialize `{path}`");
yield break;
}

foreach (var installed in root.Installed)
{
OneOf<GOGGame, ErrorMessage> res;
try
{
res = Parse(installed, path.FileSystem);
}
catch (Exception e)
{
res = new ErrorMessage(e, $"Unable to parse in {path}: `{installed}`");
}

yield return res;
}
}

internal static OneOf<GOGGame, ErrorMessage> Parse(DTOs.Installed installed, 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 path = fileSystem.FromUnsanitizedFullPath(installed.InstallPath);
return new GOGGame(GOGGameId.From(id), installed.AppName, path);
}

internal static AbsolutePath GetInstalledJsonFilePath(AbsolutePath configPath)
{
return configPath.Combine("gog_store").Combine("installed.json");
}

internal static IEnumerable<AbsolutePath> FindConfigDirectory(IFileSystem fileSystem)
{
yield return fileSystem.GetKnownPath(KnownPath.XDG_CONFIG_HOME).Combine("heroic");

// Flatpak installation
yield return fileSystem.GetKnownPath(KnownPath.HomeDirectory)
.Combine(".var")
.Combine("app")
.Combine("com.heroicgameslauncher.hgl")
.Combine("config")
.Combine("heroic");
}
}

0 comments on commit 504d7c7

Please sign in to comment.