diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 8cf5b0d7c..5afb46c13 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -23698,4 +23698,4 @@ } } ] -} +} \ No newline at end of file diff --git a/src/Ryujinx/Utilities/PlayReport/Analyzer.cs b/src/Ryujinx/Utilities/PlayReport/Analyzer.cs index ae5abbf9d..3d0963b5a 100644 --- a/src/Ryujinx/Utilities/PlayReport/Analyzer.cs +++ b/src/Ryujinx/Utilities/PlayReport/Analyzer.cs @@ -108,6 +108,25 @@ namespace Ryujinx.Ava.Utilities.PlayReport Application = appMeta, PackedValue = valuePackObject }); } + + foreach (GameSpec.MultiFormatterSpec formatSpec in spec.MultiValueFormatters.OrderBy(x => x.Priority)) + { + List packedObjects = []; + foreach (var reportKey in formatSpec.ReportKeys) + { + if (!playReport.AsDictionary().TryGetValue(reportKey, out MessagePackObject valuePackObject)) + continue; + + packedObjects.Add(valuePackObject); + } + + if (packedObjects.Count != formatSpec.ReportKeys.Length) + return FormattedValue.Unhandled; + + return formatSpec.ValueFormatter(packedObjects + .Select(packObject => new Value { Application = appMeta, PackedValue = packObject }) + .ToArray()); + } return FormattedValue.Unhandled; } @@ -178,6 +197,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport { public required string[] TitleIds { get; init; } public List SimpleValueFormatters { get; } = []; + public List MultiValueFormatters { get; } = []; /// /// Add a value formatter to the current @@ -212,6 +232,25 @@ namespace Ryujinx.Ava.Utilities.PlayReport }); return this; } + + public GameSpec AddMultiValueFormatter(string[] reportKeys, PlayReportMultiValueFormatter valueFormatter) + { + MultiValueFormatters.Add(new MultiFormatterSpec + { + Priority = SimpleValueFormatters.Count, ReportKeys = reportKeys, ValueFormatter = valueFormatter + }); + return this; + } + + public GameSpec AddMultiValueFormatter(int priority, string[] reportKeys, + PlayReportMultiValueFormatter valueFormatter) + { + MultiValueFormatters.Add(new MultiFormatterSpec + { + Priority = priority, ReportKeys = reportKeys, ValueFormatter = valueFormatter + }); + return this; + } /// /// A struct containing the data for a mapping of a key in a Play Report to a formatter for its potential value. @@ -222,6 +261,16 @@ namespace Ryujinx.Ava.Utilities.PlayReport public required string ReportKey { get; init; } public PlayReportValueFormatter ValueFormatter { get; init; } } + + /// + /// 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 required int Priority { get; init; } + public required string[] ReportKeys { get; init; } + public PlayReportMultiValueFormatter ValueFormatter { get; init; } + } } /// @@ -269,7 +318,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport } /// - /// The delegate type that powers the entire analysis system (as it currently is).
+ /// The delegate type that powers single value formatters.
/// Takes in the result value from the Play Report, and outputs: ///
/// a formatted string, @@ -279,4 +328,16 @@ namespace Ryujinx.Ava.Utilities.PlayReport /// OR a signal to reset the value that the caller is using the for. ///
public delegate Analyzer.FormattedValue PlayReportValueFormatter(Value value); + + /// + /// The delegate type that powers multiple value formatters.
+ /// Takes in the result value from the Play Report, and outputs: + ///
+ /// a formatted string, + ///
+ /// a signal that nothing was available to handle it, + ///
+ /// OR a signal to reset the value that the caller is using the for. + ///
+ public delegate Analyzer.FormattedValue PlayReportMultiValueFormatter(Value[] value); } diff --git a/src/Ryujinx/Utilities/PlayReport/PlayReports.cs b/src/Ryujinx/Utilities/PlayReport/PlayReports.cs index ce35cae89..068d6bb9a 100644 --- a/src/Ryujinx/Utilities/PlayReport/PlayReports.cs +++ b/src/Ryujinx/Utilities/PlayReport/PlayReports.cs @@ -14,7 +14,8 @@ namespace Ryujinx.Ava.Utilities.PlayReport ) .AddSpec( "0100f2c0115b6000", - spec => spec.AddValueFormatter("PlayerPosY", TearsOfTheKingdom_CurrentField)) + spec => spec + .AddValueFormatter("PlayerPosY", TearsOfTheKingdom_CurrentField)) .AddSpec( "0100000000010000", spec => @@ -40,6 +41,11 @@ namespace Ryujinx.Ava.Utilities.PlayReport .AddValueFormatter("team_circle", PokemonSVUnionCircle) ); + private static FormattedValue Botw(Value[] values) + { + return $"{values[0].BoxedValue}, {values[1].BoxedValue}"; + } + private static FormattedValue BreathOfTheWild_MasterMode(Value value) => value.BoxedValue is 1 ? "Playing Master Mode" : FormattedValue.ForceReset;