From 30a534edcdc7d72882a291448ac2a889848eb18f Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Sat, 8 Feb 2025 01:26:05 -0600 Subject: [PATCH] misc: chore: [ci skip] generify Formatter Specs to be able to run formatters of different types at interleaving priorities --- src/Ryujinx/Utilities/PlayReport/Analyzer.cs | 50 +------- src/Ryujinx/Utilities/PlayReport/Specs.cs | 124 +++++++++++++++---- 2 files changed, 105 insertions(+), 69 deletions(-) diff --git a/src/Ryujinx/Utilities/PlayReport/Analyzer.cs b/src/Ryujinx/Utilities/PlayReport/Analyzer.cs index 0b4130da5..668eb526c 100644 --- a/src/Ryujinx/Utilities/PlayReport/Analyzer.cs +++ b/src/Ryujinx/Utilities/PlayReport/Analyzer.cs @@ -103,56 +103,12 @@ namespace Ryujinx.Ava.Utilities.PlayReport if (!_specs.TryGetFirst(s => runningGameId.EqualsAnyIgnoreCase(s.TitleIds), out GameSpec spec)) return FormattedValue.Unhandled; - foreach (FormatterSpec formatSpec in spec.SimpleValueFormatters.OrderBy(x => x.Priority)) + foreach (FormatterSpecBase formatSpec in spec.ValueFormatters.OrderBy(x => x.Priority)) { - if (!playReport.ReportData.AsDictionary().TryGetValue(formatSpec.ReportKey, out MessagePackObject valuePackObject)) + if (!formatSpec.Format(appMeta, playReport, out FormattedValue value)) continue; - return formatSpec.Formatter(new SingleValue(valuePackObject) - { - Application = appMeta, - PlayReport = playReport - }); - } - - foreach (MultiFormatterSpec formatSpec in spec.MultiValueFormatters.OrderBy(x => x.Priority)) - { - List packedObjects = []; - foreach (var reportKey in formatSpec.ReportKeys) - { - if (!playReport.ReportData.AsDictionary().TryGetValue(reportKey, out MessagePackObject valuePackObject)) - continue; - - packedObjects.Add(valuePackObject); - } - - if (packedObjects.Count != formatSpec.ReportKeys.Length) - return FormattedValue.Unhandled; - - return formatSpec.Formatter(new MultiValue(packedObjects) - { - Application = appMeta, - PlayReport = playReport - }); - } - - foreach (SparseMultiFormatterSpec formatSpec in spec.SparseMultiValueFormatters.OrderBy(x => x.Priority)) - { - Dictionary packedObjects = []; - foreach (var reportKey in formatSpec.ReportKeys) - { - if (!playReport.ReportData.AsDictionary().TryGetValue(reportKey, out MessagePackObject valuePackObject)) - continue; - - packedObjects.Add(reportKey, valuePackObject); - } - - return formatSpec.Formatter( - new SparseMultiValue(packedObjects) - { - Application = appMeta, - PlayReport = playReport - }); + return value; } return FormattedValue.Unhandled; diff --git a/src/Ryujinx/Utilities/PlayReport/Specs.cs b/src/Ryujinx/Utilities/PlayReport/Specs.cs index 649813b7a..3c80198b9 100644 --- a/src/Ryujinx/Utilities/PlayReport/Specs.cs +++ b/src/Ryujinx/Utilities/PlayReport/Specs.cs @@ -1,4 +1,7 @@ using FluentAvalonia.Core; +using MsgPack; +using Ryujinx.Ava.Utilities.AppLibrary; +using System; using System.Collections.Generic; using System.Linq; @@ -11,10 +14,11 @@ namespace Ryujinx.Ava.Utilities.PlayReport /// public class GameSpec { + private int _lastPriority; + public required string[] TitleIds { get; init; } - public List SimpleValueFormatters { get; } = []; - public List MultiValueFormatters { get; } = []; - public List SparseMultiValueFormatters { get; } = []; + + public List ValueFormatters { get; } = []; /// @@ -25,7 +29,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport /// The function which can return a potential formatted value. /// The current , for chaining convenience. public GameSpec AddValueFormatter(string reportKey, ValueFormatter valueFormatter) - => AddValueFormatter(SimpleValueFormatters.Count, reportKey, valueFormatter); + => AddValueFormatter(_lastPriority++, reportKey, valueFormatter); /// /// Add a value formatter at a specific priority to the current @@ -38,9 +42,9 @@ namespace Ryujinx.Ava.Utilities.PlayReport public GameSpec AddValueFormatter(int priority, string reportKey, ValueFormatter valueFormatter) { - SimpleValueFormatters.Add(new FormatterSpec + ValueFormatters.Add(new FormatterSpec { - Priority = priority, ReportKey = reportKey, Formatter = valueFormatter + Priority = priority, ReportKeys = [reportKey], Formatter = valueFormatter }); return this; } @@ -53,7 +57,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport /// The function which can format the values. /// The current , for chaining convenience. public GameSpec AddMultiValueFormatter(string[] reportKeys, MultiValueFormatter valueFormatter) - => AddMultiValueFormatter(MultiValueFormatters.Count, reportKeys, valueFormatter); + => AddMultiValueFormatter(_lastPriority++, reportKeys, valueFormatter); /// /// Add a multi-value formatter at a specific priority to the current @@ -66,7 +70,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport public GameSpec AddMultiValueFormatter(int priority, string[] reportKeys, MultiValueFormatter valueFormatter) { - MultiValueFormatters.Add(new MultiFormatterSpec + ValueFormatters.Add(new MultiFormatterSpec { Priority = priority, ReportKeys = reportKeys, Formatter = valueFormatter }); @@ -84,7 +88,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport /// The function which can format the values. /// The current , for chaining convenience. public GameSpec AddSparseMultiValueFormatter(string[] reportKeys, SparseMultiValueFormatter valueFormatter) - => AddSparseMultiValueFormatter(SparseMultiValueFormatters.Count, reportKeys, valueFormatter); + => AddSparseMultiValueFormatter(_lastPriority++, reportKeys, valueFormatter); /// /// Add a multi-value formatter at a specific priority to the current @@ -100,7 +104,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport public GameSpec AddSparseMultiValueFormatter(int priority, string[] reportKeys, SparseMultiValueFormatter valueFormatter) { - SparseMultiValueFormatters.Add(new SparseMultiFormatterSpec + ValueFormatters.Add(new SparseMultiFormatterSpec { Priority = priority, ReportKeys = reportKeys, Formatter = valueFormatter }); @@ -111,30 +115,106 @@ namespace Ryujinx.Ava.Utilities.PlayReport /// /// A struct containing the data for a mapping of a key in a Play Report to a formatter for its potential value. /// - public struct FormatterSpec + public class FormatterSpec : FormatterSpecBase { - public required int Priority { get; init; } - public required string ReportKey { get; init; } - public ValueFormatter Formatter { get; init; } + public override bool GetData(Horizon.Prepo.Types.PlayReport playReport, out object result) + { + if (!playReport.ReportData.AsDictionary().TryGetValue(ReportKeys[0], out MessagePackObject valuePackObject)) + { + result = null; + return false; + } + + result = valuePackObject; + return true; + } } /// /// A struct containing the data for a mapping of an arbitrary key set in a Play Report to a formatter for their potential values. /// - public struct MultiFormatterSpec + public class MultiFormatterSpec : FormatterSpecBase { - public required int Priority { get; init; } - public required string[] ReportKeys { get; init; } - public MultiValueFormatter Formatter { get; init; } + public override bool GetData(Horizon.Prepo.Types.PlayReport playReport, out object result) + { + List packedObjects = []; + foreach (var reportKey in ReportKeys) + { + if (!playReport.ReportData.AsDictionary().TryGetValue(reportKey, out MessagePackObject valuePackObject)) + { + result = null; + return false; + } + + packedObjects.Add(valuePackObject); + } + + result = packedObjects; + return true; + } } /// /// A struct containing the data for a mapping of an arbitrary key set in a Play Report to a formatter for their sparsely populated potential values. /// - public struct SparseMultiFormatterSpec + public class SparseMultiFormatterSpec : FormatterSpecBase { - public required int Priority { get; init; } - public required string[] ReportKeys { get; init; } - public SparseMultiValueFormatter Formatter { get; init; } + public override bool GetData(Horizon.Prepo.Types.PlayReport playReport, out object result) + { + Dictionary packedObjects = []; + foreach (var reportKey in ReportKeys) + { + if (!playReport.ReportData.AsDictionary().TryGetValue(reportKey, out MessagePackObject valuePackObject)) + continue; + + packedObjects.Add(reportKey, valuePackObject); + } + + result = packedObjects; + return true; + } + } + + public abstract class FormatterSpecBase + { + public abstract bool GetData(Horizon.Prepo.Types.PlayReport playReport, out object data); + + public int Priority { get; init; } + public string[] ReportKeys { get; init; } + public Delegate Formatter { get; init; } + + public bool Format(ApplicationMetadata appMeta, Horizon.Prepo.Types.PlayReport playReport, out FormattedValue formattedValue) + { + formattedValue = default; + if (!GetData(playReport, out object data)) + return false; + + if (data is FormattedValue fv) + { + formattedValue = fv; + return true; + } + + if (Formatter is ValueFormatter vf && data is MessagePackObject mpo) + { + formattedValue = vf(new SingleValue(mpo) { Application = appMeta, PlayReport = playReport }); + return true; + } + + if (Formatter is MultiValueFormatter mvf && data is List messagePackObjects) + { + formattedValue = mvf(new MultiValue(messagePackObjects) { Application = appMeta, PlayReport = playReport }); + return true; + } + + if (Formatter is SparseMultiValueFormatter smvf && + data is Dictionary sparseMessagePackObjects) + { + formattedValue = smvf(new SparseMultiValue(sparseMessagePackObjects) { Application = appMeta, PlayReport = playReport }); + return true; + } + + throw new InvalidOperationException("Formatter delegate is not of a known type!"); + } } }