using Gommon;
using MsgPack;
using Ryujinx.Ava.Utilities.AppLibrary;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
namespace Ryujinx.Ava.Utilities.PlayReport
{
///
/// The entrypoint for the Play Report analysis system.
///
public class Analyzer
{
private readonly List _specs = [];
public string[] TitleIds => Specs.SelectMany(x => x.TitleIds).ToArray();
public IReadOnlyList Specs => new ReadOnlyCollection(_specs);
///
/// Add an analysis spec matching a specific game by title ID, with the provided spec configuration.
///
/// The ID of the game to listen to Play Reports in.
/// The configuration function for the analysis spec.
/// The current , for chaining convenience.
public Analyzer AddSpec(string titleId, Func transform)
{
Guard.Ensure(ulong.TryParse(titleId, NumberStyles.HexNumber, null, out _),
$"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
_specs.Add(transform(new GameSpec { TitleIds = [titleId] }));
return this;
}
///
/// Add an analysis spec matching a specific game by title ID, with the provided spec configuration.
///
/// The ID of the game to listen to Play Reports in.
/// The configuration function for the analysis spec.
/// The current , for chaining convenience.
public Analyzer AddSpec(string titleId, Action transform)
{
Guard.Ensure(ulong.TryParse(titleId, NumberStyles.HexNumber, null, out _),
$"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
_specs.Add(new GameSpec { TitleIds = [titleId] }.Apply(transform));
return this;
}
///
/// Add an analysis spec matching a specific set of games by title IDs, with the provided spec configuration.
///
/// The IDs of the games to listen to Play Reports in.
/// The configuration function for the analysis spec.
/// The current , for chaining convenience.
public Analyzer AddSpec(IEnumerable titleIds,
Func transform)
{
string[] tids = titleIds.ToArray();
Guard.Ensure(tids.All(x => ulong.TryParse(x, NumberStyles.HexNumber, null, out _)),
$"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
_specs.Add(transform(new GameSpec { TitleIds = [..tids] }));
return this;
}
///
/// Add an analysis spec matching a specific set of games by title IDs, with the provided spec configuration.
///
/// The IDs of the games to listen to Play Reports in.
/// The configuration function for the analysis spec.
/// The current , for chaining convenience.
public Analyzer AddSpec(IEnumerable titleIds, Action transform)
{
string[] tids = titleIds.ToArray();
Guard.Ensure(tids.All(x => ulong.TryParse(x, NumberStyles.HexNumber, null, out _)),
$"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
_specs.Add(new GameSpec { TitleIds = [..tids] }.Apply(transform));
return this;
}
///
/// Runs the configured for the specified game title ID.
///
/// The game currently running.
/// The Application metadata information, including localized game name and play time information.
/// The Play Report received from HLE.
/// A struct representing a possible formatted value.
public FormattedValue Format(
string runningGameId,
ApplicationMetadata appMeta,
Horizon.Prepo.Types.PlayReport playReport
)
{
if (!playReport.ReportData.IsDictionary)
return FormattedValue.Unhandled;
if (!_specs.TryGetFirst(s => runningGameId.EqualsAnyIgnoreCase(s.TitleIds), out GameSpec spec))
return FormattedValue.Unhandled;
foreach (FormatterSpecBase formatSpec in spec.ValueFormatters.OrderBy(x => x.Priority))
{
if (!formatSpec.Format(appMeta, playReport, out FormattedValue value))
continue;
return value;
}
return FormattedValue.Unhandled;
}
}
}