Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 36 additions & 7 deletions source/Libraries/SteamLibrary/ModInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using SteamLibrary.Models;
using SteamLibrary.Services;

namespace SteamLibrary
{
internal class ModInfo
internal class ModInfo : ISteamApp
{
public enum ModType
{
Expand All @@ -17,7 +20,7 @@ public enum ModType
}

public string Name { get; private set; }
public GameID GameId { get; private set; }
public GameID Id { get; private set; }
public string InstallFolder { get; private set; }
public List<string> Categories { get; private set; }
public string Developer { get; private set; }
Expand Down Expand Up @@ -49,7 +52,7 @@ private ModInfo(ModType type, string installFolder)
InstallFolder = installFolder;
Name = "Unknown Mod";
Links = new List<Link>();
GameId = new GameID();
Id = new GameID();
Developer = "Unknown";
Categories = new List<string>();
}
Expand Down Expand Up @@ -106,16 +109,16 @@ static public ModInfo GetFromFolder(string path, ModType modType)
ModInfo modInfo = new ModInfo(modType, path);
if (modType == ModType.HL)
{
modInfo.GameId.AppID = halfLife;
modInfo.Id.AppID = halfLife;
PopulateModInfoFromLibList(ref modInfo, gameInfoPath);
}
else
{
PopulateModInfoFromGameInfo(ref modInfo, gameInfoPath);
}

modInfo.GameId.AppType = GameID.GameType.GameMod;
modInfo.GameId.ModID = GetModFolderCRC(dirInfo.Name);
modInfo.Id.AppType = GameID.GameType.GameMod;
modInfo.Id.ModID = GetModFolderCRC(dirInfo.Name);

return modInfo;
}
Expand All @@ -141,7 +144,7 @@ static private void PopulateModInfoFromGameInfo(ref ModInfo modInfo, string path
var gameInfo = new KeyValue();
gameInfo.ReadFileAsText(path);

modInfo.GameId.AppID = gameInfo["FileSystem"]["SteamAppId"].AsUnsignedInteger();
modInfo.Id.AppID = gameInfo["FileSystem"]["SteamAppId"].AsUnsignedInteger();
modInfo.Name = gameInfo["game"].Value;

if (gameInfo["developer"] != KeyValue.Invalid)
Expand Down Expand Up @@ -279,5 +282,31 @@ static private string FindIcon(string modPath, string rawIconPath)

return null;
}

public GameMetadata ToGame()
{
var game = new GameMetadata
{
GameId = Id,
Name = Name.RemoveTrademarks().Trim(),
InstallDirectory = InstallFolder,
IsInstalled = true,
Developers = new HashSet<MetadataProperty> { new MetadataNameProperty(Developer) },
Links = Links,
Tags = Categories?.Select(a => new MetadataNameProperty(a)).Cast<MetadataProperty>().ToHashSet(),
Platforms = new HashSet<MetadataProperty> { new MetadataSpecProperty("pc_windows") },
Source = SourceNames.GetSource(IsOwned, BackendAppInfo?.Type),
};

if (!IconPath.IsNullOrEmpty() && File.Exists(IconPath))
{
game.Icon = new MetadataFile(IconPath);
}

return game;
}

public bool IsOwned => true;
public BackendAppInfo BackendAppInfo { get; set; }
}
}
178 changes: 175 additions & 3 deletions source/Libraries/SteamLibrary/Models/ApiResponseModels.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
using System;
using System.Collections.Generic;
using Playnite.SDK.Models;
using SteamKit2;
using SteamLibrary.Services;
using SteamLibrary.Services.Base;

namespace SteamLibrary.Models
{
Expand All @@ -19,7 +24,7 @@ public class GetSharedLibraryAppsResponse
public string owner_steamid { get; set; }
}

public class FamilySharedApp
public class FamilySharedApp : ISteamApp
{
public int appid { get; set; }
public string[] owner_steamids { get; set; }
Expand All @@ -36,6 +41,48 @@ public class FamilySharedApp
public uint app_type { get; set; }
public uint[] content_descriptors { get; set; }
public string sort_as { get; set; }

public string NiceAppType =>
app_type switch
{
1 => "game",
2 => "application",
4 => "tool",
8 => "demo",
8192 => "soundtrack",
_ => $"{app_type}"
};

public string NiceExcludeReason =>
exclude_reason switch
{
0 => "none",
1 => "ineligible",
3 => "free",
6 => "type_not_shared", // guessed by looking at data
_ => $"{exclude_reason}"
};

// we probably don't want free apps coming from family members (they do exist)
public bool IsImportable => (NiceExcludeReason == "none" || NiceExcludeReason == "free")
&& (NiceAppType == "game" || NiceAppType == "demo" || NiceAppType == "tool");

public GameMetadata ToGame()
{
return new GameMetadata
{
GameId = Id,
Name = name.RemoveTrademarks().Trim(),
LastActivity = SteamApiServiceBase.GetLastPlayedDateTime(rt_last_played),
Playtime = rt_playtime * 60,
Platforms = new HashSet<MetadataProperty> { new MetadataSpecProperty("pc_windows") },
Source = SourceNames.GetSource(IsOwned, BackendAppInfo?.Type),
};
}

public GameID Id => (uint)appid;
public bool IsOwned { get; set; }
public BackendAppInfo BackendAppInfo { get; set; }
}

public class GetClientAppListResponse
Expand All @@ -44,7 +91,7 @@ public class GetClientAppListResponse
public SteamClientApp[] apps { get; set; }
}

public class SteamClientApp
public class SteamClientApp : ISteamApp
{
public ulong appid { get; set; }
public string app { get; set; }
Expand All @@ -53,6 +100,31 @@ public class SteamClientApp
public string bytes_required { get; set; }
public bool running { get; set; }
public bool installed { get; set; }

public GameMetadata ToGame()
{
return new GameMetadata
{
GameId = Id,
Name = app.RemoveTrademarks().Trim(),
InstallSize = GetInstallSize(),
Platforms = new HashSet<MetadataProperty> { new MetadataSpecProperty("pc_windows") },
Source = SourceNames.GetSource(IsOwned, BackendAppInfo?.Type),
};
}

public GameID Id => appid;
public bool IsOwned { get; set; } = true;
public BackendAppInfo BackendAppInfo { get; set; }


private ulong? GetInstallSize()
{
if (ulong.TryParse(bytes_required, out ulong size))
return size;

return null;
}
}

public class GetOwnedGamesResponse
Expand All @@ -61,11 +133,111 @@ public class GetOwnedGamesResponse
public List<OwnedGame> games { get; set; }
}

public class OwnedGame
public class OwnedGame : ISteamApp
{
public int appid { get; set; }
public string name { get; set; }
public uint playtime_forever { get; set; }
public uint rtime_last_played { get; set; }

public bool IncludePlaytime { get; set; }

public GameMetadata ToGame()
{
var output = new GameMetadata
{
GameId = Id,
Name = name.RemoveTrademarks().Trim(),
Platforms = new HashSet<MetadataProperty> {new MetadataSpecProperty("pc_windows")},
Source = SourceNames.GetSource(IsOwned, BackendAppInfo?.Type),
};

if (IncludePlaytime)
{
output.Playtime = playtime_forever * 60;
output.LastActivity = SteamApiServiceBase.GetLastPlayedDateTime(rtime_last_played);
}

return output;
}

public GameID Id => (uint) appid;
public bool IsOwned => true;
public BackendAppInfo BackendAppInfo { get; set; }

}

public interface ISteamApp
{
GameID Id { get; }
GameMetadata ToGame();
public BackendAppInfo BackendAppInfo { get; set; }
bool IsOwned { get; }
}

public class LocalSteamApp : ISteamApp
{
public GameID Id { get; set; }
public string Name { get; set; }
public string InstallDir { get; set; }

public GameMetadata ToGame()
{
return new GameMetadata()
{
GameId = Id,
Name = Name,
InstallDirectory = InstallDir,
IsInstalled = true,
Platforms = new HashSet<MetadataProperty> { new MetadataSpecProperty("pc_windows") },
Source = SourceNames.GetSource(IsOwned, BackendAppInfo?.Type),
};
}

public bool IsOwned => true;
public BackendAppInfo BackendAppInfo { get; set; }

}

public class ExtraIdSteamApp : ISteamApp
{
public GameID Id { get; set; }
public string Name { get; set; }

public GameMetadata ToGame()
{
return new GameMetadata
{
GameId = Id,
Name = Name,
Platforms = new HashSet<MetadataProperty> { new MetadataSpecProperty("pc_windows") },
Source = SourceNames.GetSource(IsOwned, BackendAppInfo?.Type),
};
}

public bool IsOwned => true;
public BackendAppInfo BackendAppInfo { get; set; }

}

public class BackendOwnSteamDbApp : ISteamApp
{
public BackendOwnSteamDbApp(BackendAppInfo item)
{
BackendAppInfo = item;
}

public GameMetadata ToGame() =>
new GameMetadata
{
Name = BackendAppInfo.Name.RemoveTrademarks(),
GameId = Id,
Platforms = new HashSet<MetadataProperty> {new MetadataSpecProperty("pc_windows")},
Source = SourceNames.GetSource(IsOwned, BackendAppInfo?.Type),
};

public GameID Id => BackendAppInfo.AppId;
public bool IsOwned => true;
public BackendAppInfo BackendAppInfo { get; set; }
}
}
54 changes: 54 additions & 0 deletions source/Libraries/SteamLibrary/Models/BackendModels.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#nullable enable
using System.Collections.Generic;

namespace SteamLibrary.Models
{
// TODO move to playnite backend repo
public class BackendSteamDbItemsRequest
{
public List<ulong>? AppIds { get; set; }
}

/// <summary>
/// Playnite backend metadata for steam app
/// </summary>
public class BackendAppInfo
{
private string? type;
public uint AppId { get; set; }

public string? Name
{
get => name ?? $"Unknown App {AppId}";
set => name = value;
}

/// <summary>
/// Normalized to lowercase. Original metadata has "Game" and "game" values
/// </summary>
public string? Type
{
get => type?.ToLowerInvariant() ?? "unknown";
set => type = value;
}

public bool IsGame => Type == "game";
public bool IsFree => Type == "demo" || Type == "beta";
public bool IsApp => Type == "application";
public bool IsMedia => Type == "music" || Type == "video";
public bool IsTool => Type == "tool";
public bool IsUseless => Type == "config" || Type == "dlc" || Type == "unknown";

public Dictionary<string, string>? LocalizedNames { get; set; }

public void LocalizeName(string lang)
{
if (LocalizedNames?.TryGetValue(lang, out var appName) == true && !string.IsNullOrWhiteSpace(appName))
{
Name = appName;
}
}

private string? name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public TResponse Get<TResponse>(string baseUrl, Dictionary<string, string> param
}
}

protected static DateTime? GetLastPlayedDateTime(uint unixEpochSeconds)
public static DateTime? GetLastPlayedDateTime(uint unixEpochSeconds)
{
if (unixEpochSeconds == 0)
return null;
Expand Down
Loading