Compare commits
8 Commits
Canary-1.2
...
c40ba4e2ae
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c40ba4e2ae | ||
|
|
9c226dcc7a | ||
|
|
30a534edcd | ||
|
|
1d88771d1b | ||
|
|
7294589272 | ||
|
|
ae69b1e495 | ||
|
|
ab07537ed5 | ||
|
|
61af464b64 |
@@ -1,5 +1,6 @@
|
||||
using MsgPack;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Prepo.Types;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Threading;
|
||||
@@ -8,7 +9,7 @@ namespace Ryujinx.Horizon
|
||||
{
|
||||
public static class HorizonStatic
|
||||
{
|
||||
internal static void HandlePlayReport(MessagePackObject report) =>
|
||||
internal static void HandlePlayReport(PlayReport report) =>
|
||||
new Thread(() => PlayReport?.Invoke(report))
|
||||
{
|
||||
Name = "HLE.PlayReportEvent",
|
||||
@@ -16,7 +17,7 @@ namespace Ryujinx.Horizon
|
||||
Priority = ThreadPriority.AboveNormal
|
||||
}.Start();
|
||||
|
||||
public static event Action<MessagePackObject> PlayReport;
|
||||
public static event Action<PlayReport> PlayReport;
|
||||
|
||||
[field: ThreadStatic]
|
||||
public static HorizonOptions Options { get; private set; }
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Gommon;
|
||||
using MsgPack;
|
||||
using MsgPack.Serialization;
|
||||
using Ryujinx.Common.Logging;
|
||||
@@ -12,19 +11,12 @@ using Ryujinx.Horizon.Sdk.Sf;
|
||||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using ApplicationId = Ryujinx.Horizon.Sdk.Ncm.ApplicationId;
|
||||
|
||||
namespace Ryujinx.Horizon.Prepo.Ipc
|
||||
{
|
||||
partial class PrepoService : IPrepoService
|
||||
{
|
||||
enum PlayReportKind
|
||||
{
|
||||
Normal,
|
||||
System,
|
||||
}
|
||||
|
||||
private readonly ArpApi _arp;
|
||||
private readonly PrepoServicePermissionLevel _permissionLevel;
|
||||
private ulong _systemSessionId;
|
||||
@@ -196,10 +188,17 @@ namespace Ryujinx.Horizon.Prepo.Ipc
|
||||
{
|
||||
return PrepoResult.InvalidBufferSize;
|
||||
}
|
||||
|
||||
|
||||
StringBuilder builder = new();
|
||||
MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(reportBuffer.ToArray());
|
||||
|
||||
PlayReport playReport = new()
|
||||
{
|
||||
Kind = playReportKind,
|
||||
Room = gameRoom,
|
||||
ReportData = deserializedReport
|
||||
};
|
||||
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("PlayReport log:");
|
||||
builder.AppendLine($" Kind: {playReportKind}");
|
||||
@@ -209,10 +208,12 @@ namespace Ryujinx.Horizon.Prepo.Ipc
|
||||
if (pid != 0)
|
||||
{
|
||||
builder.AppendLine($" Pid: {pid}");
|
||||
playReport.Pid = pid;
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($" ApplicationId: {applicationId}");
|
||||
playReport.AppId = applicationId;
|
||||
}
|
||||
|
||||
Result result = _arp.GetApplicationInstanceId(out ulong applicationInstanceId, pid);
|
||||
@@ -223,17 +224,20 @@ namespace Ryujinx.Horizon.Prepo.Ipc
|
||||
|
||||
_arp.GetApplicationLaunchProperty(out ApplicationLaunchProperty applicationLaunchProperty, applicationInstanceId).AbortOnFailure();
|
||||
|
||||
playReport.Version = applicationLaunchProperty.Version;
|
||||
|
||||
builder.AppendLine($" ApplicationVersion: {applicationLaunchProperty.Version}");
|
||||
|
||||
if (!userId.IsNull)
|
||||
{
|
||||
builder.AppendLine($" UserId: {userId}");
|
||||
playReport.UserId = userId;
|
||||
}
|
||||
|
||||
builder.AppendLine($" Room: {gameRoom}");
|
||||
builder.AppendLine($" Report: {MessagePackObjectFormatter.Format(deserializedReport)}");
|
||||
|
||||
HorizonStatic.HandlePlayReport(deserializedReport);
|
||||
HorizonStatic.HandlePlayReport(playReport);
|
||||
|
||||
Logger.Info?.Print(LogClass.ServicePrepo, builder.ToString());
|
||||
|
||||
|
||||
24
src/Ryujinx.Horizon/Prepo/Types/PlayReport.cs
Normal file
24
src/Ryujinx.Horizon/Prepo/Types/PlayReport.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using MsgPack;
|
||||
using Ryujinx.Horizon.Sdk.Account;
|
||||
using Ryujinx.Horizon.Sdk.Ncm;
|
||||
|
||||
namespace Ryujinx.Horizon.Prepo.Types
|
||||
{
|
||||
public struct PlayReport
|
||||
{
|
||||
public PlayReportKind Kind { get; init; }
|
||||
public string Room { get; init; }
|
||||
public MessagePackObject ReportData { get; init; }
|
||||
|
||||
public ApplicationId? AppId;
|
||||
public ulong? Pid;
|
||||
public uint Version;
|
||||
public Uid? UserId;
|
||||
}
|
||||
|
||||
public enum PlayReportKind
|
||||
{
|
||||
Normal,
|
||||
System,
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@
|
||||
"ID": "MenuBarFileOpenAppletOpenMiiApplet",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Mii-Bearbeitungsapplet",
|
||||
"el_GR": "",
|
||||
"en_US": "Mii Edit Applet",
|
||||
"es_ES": "Applet Editor Mii",
|
||||
@@ -176,7 +176,7 @@
|
||||
"ID": "SettingsTabSystemMemoryManagerModeSoftware",
|
||||
"Translations": {
|
||||
"ar_SA": "البرنامج",
|
||||
"de_DE": "",
|
||||
"de_DE": "Programme",
|
||||
"el_GR": "Λογισμικό",
|
||||
"en_US": "Software",
|
||||
"es_ES": "",
|
||||
@@ -326,7 +326,7 @@
|
||||
"ID": "MenuBarFileOpenFromFileError",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Keine Anwendungen im ausgewählten Datei gefunden.",
|
||||
"el_GR": "",
|
||||
"en_US": "No applications found in selected file.",
|
||||
"es_ES": "No se encontraron aplicaciones en el archivo seleccionado.",
|
||||
@@ -376,7 +376,7 @@
|
||||
"ID": "MenuBarFileLoadDlcFromFolder",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "DLC aus Ordner laden",
|
||||
"el_GR": "",
|
||||
"en_US": "Load DLC From Folder",
|
||||
"es_ES": "Cargar DLC Desde Carpeta",
|
||||
@@ -401,7 +401,7 @@
|
||||
"ID": "MenuBarFileLoadTitleUpdatesFromFolder",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Titel-Updates aus Ordner laden",
|
||||
"el_GR": "",
|
||||
"en_US": "Load Title Updates From Folder",
|
||||
"es_ES": "Cargar Actualizaciones de Títulos Desde Carpeta",
|
||||
@@ -576,7 +576,7 @@
|
||||
"ID": "MenuBarOptionsStartGamesWithoutUI",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Spiele ohne Benutzeroberfläche starten",
|
||||
"el_GR": "",
|
||||
"en_US": "Start Games with UI Hidden",
|
||||
"es_ES": "",
|
||||
@@ -751,7 +751,7 @@
|
||||
"ID": "MenuBarActionsScanAmiiboBin",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Amiibo scannen (aus Bin-Datei)",
|
||||
"el_GR": "",
|
||||
"en_US": "Scan An Amiibo (From Bin)",
|
||||
"es_ES": "",
|
||||
@@ -851,7 +851,7 @@
|
||||
"ID": "MenuBarActionsInstallKeys",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Schlüssel installieren",
|
||||
"el_GR": "",
|
||||
"en_US": "Install Keys",
|
||||
"es_ES": "",
|
||||
@@ -876,7 +876,7 @@
|
||||
"ID": "MenuBarFileActionsInstallKeysFromFile",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Schlüssel aus KEYS oder ZIP installieren",
|
||||
"el_GR": "",
|
||||
"en_US": "Install keys from KEYS or ZIP",
|
||||
"es_ES": "Instalar keys de KEYS o ZIP",
|
||||
@@ -901,7 +901,7 @@
|
||||
"ID": "MenuBarFileActionsInstallKeysFromFolder",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Schlüssel aus einem Verzeichnis installieren",
|
||||
"el_GR": "",
|
||||
"en_US": "Install keys from a directory",
|
||||
"es_ES": "Instalar keys de un directorio",
|
||||
@@ -1001,7 +1001,7 @@
|
||||
"ID": "MenuBarActionsXCITrimmer",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "XCI-Dateien trimmen",
|
||||
"el_GR": "",
|
||||
"en_US": "Trim XCI Files",
|
||||
"es_ES": "Recortar archivos XCI",
|
||||
@@ -1226,7 +1226,7 @@
|
||||
"ID": "MenuBarHelpFaqAndGuides",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "FAQ & Anleitungen",
|
||||
"el_GR": "",
|
||||
"en_US": "FAQ & Guides",
|
||||
"es_ES": "",
|
||||
@@ -1251,7 +1251,7 @@
|
||||
"ID": "MenuBarHelpFaq",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "FAQ & Fehlerbehebung Seite",
|
||||
"el_GR": "",
|
||||
"en_US": "FAQ & Troubleshooting Page",
|
||||
"es_ES": "",
|
||||
@@ -1276,7 +1276,7 @@
|
||||
"ID": "MenuBarHelpFaqTooltip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Öffnet die FAQ- und Fehlerbehebungsseite im offiziellen Ryujinx-Wiki",
|
||||
"el_GR": "",
|
||||
"en_US": "Opens the FAQ and Troubleshooting page on the official Ryujinx wiki",
|
||||
"es_ES": "",
|
||||
@@ -1301,7 +1301,7 @@
|
||||
"ID": "MenuBarHelpSetup",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Setup- und Konfigurationsanleitung",
|
||||
"el_GR": "",
|
||||
"en_US": "Setup & Configuration Guide",
|
||||
"es_ES": "",
|
||||
@@ -1326,7 +1326,7 @@
|
||||
"ID": "MenuBarHelpSetupTooltip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Öffnet die Setup- und Konfigurationsanleitung im offiziellen Ryujinx-Wiki",
|
||||
"el_GR": "",
|
||||
"en_US": "Opens the Setup & Configuration guide on the official Ryujinx wiki",
|
||||
"es_ES": "",
|
||||
@@ -1351,7 +1351,7 @@
|
||||
"ID": "MenuBarHelpMultiplayer",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Multiplayer (LDN/LAN) Anleitung",
|
||||
"el_GR": "",
|
||||
"en_US": "Multiplayer (LDN/LAN) Guide",
|
||||
"es_ES": "",
|
||||
@@ -1376,7 +1376,7 @@
|
||||
"ID": "MenuBarHelpMultiplayerTooltip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Öffnet die Multiplayer-Anleitung im offiziellen Ryujinx-Wiki",
|
||||
"el_GR": "",
|
||||
"en_US": "Opens the Multiplayer guide on the official Ryujinx wiki",
|
||||
"es_ES": "",
|
||||
@@ -1526,7 +1526,7 @@
|
||||
"ID": "GameListHeaderDeveloper",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Entwickelt von {0}",
|
||||
"el_GR": "",
|
||||
"en_US": "Developed by {0}",
|
||||
"es_ES": "",
|
||||
@@ -1826,7 +1826,7 @@
|
||||
"ID": "GameListHeaderCompatibilityStatus",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Kompatibilität:",
|
||||
"el_GR": "",
|
||||
"en_US": "Compatibility:",
|
||||
"es_ES": "",
|
||||
@@ -23026,7 +23026,7 @@
|
||||
"ID": "SettingsTabSystemVSyncModeTooltip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Emulierte vertikale Synchronisation. \"Switch\" emuliert die 60Hz-Bildwiederholfrequenz der Switch. \"Unbounded\" ist eine unbegrenzte Bildwiederholfrequenz.",
|
||||
"el_GR": "",
|
||||
"en_US": "Emulated Vertical Sync. 'Switch' emulates the Switch's refresh rate of 60Hz. 'Unbounded' is an unbounded refresh rate.",
|
||||
"es_ES": "",
|
||||
@@ -23051,7 +23051,7 @@
|
||||
"ID": "SettingsTabSystemVSyncModeTooltipCustom",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Emulierte vertikale Synchronisation. \"Switch\" emuliert die 60Hz-Bildwiederholfrequenz der Switch. „Unbounded“ ist eine unbegrenzte Bildwiederholfrequenz. „Benutzerdefinierte Bildwiederholfrequenz“ emuliert die angegebene benutzerdefinierte Bildwiederholfrequenz.",
|
||||
"el_GR": "",
|
||||
"en_US": "Emulated Vertical Sync. 'Switch' emulates the Switch's refresh rate of 60Hz. 'Unbounded' is an unbounded refresh rate. 'Custom Refresh Rate' emulates the specified custom refresh rate.",
|
||||
"es_ES": "",
|
||||
@@ -23076,7 +23076,7 @@
|
||||
"ID": "SettingsTabSystemEnableCustomVSyncIntervalTooltip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Ermöglicht es dem Benutzer, eine emulierte Bildwiederholfrequenz festzulegen. In einigen Titeln kann dies die Geschwindigkeit der Spiel-Logik erhöhen oder verringern. In anderen Titeln kann dies dazu führen, dass die FPS auf ein Vielfaches der Bildwiederholfrequenz begrenzt werden oder zu unvorhersehbarem Verhalten führen. Dies ist eine experimentelle Funktion, ohne Garantien dafür, wie sich das Gameplay auswirkt. \n\nLassen Sie diese Option deaktiviert, wenn Sie sich nicht sicher sind.",
|
||||
"el_GR": "",
|
||||
"en_US": "Allows the user to specify an emulated refresh rate. In some titles, this may speed up or slow down the rate of gameplay logic. In other titles, it may allow for capping FPS at some multiple of the refresh rate, or lead to unpredictable behavior. This is an experimental feature, with no guarantees for how gameplay will be affected. \n\nLeave OFF if unsure.",
|
||||
"es_ES": "",
|
||||
@@ -23101,7 +23101,7 @@
|
||||
"ID": "SettingsTabSystemCustomVSyncIntervalValueTooltip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Der Zielwert für die benutzerdefinierte Bildwiederholfrequenz.",
|
||||
"el_GR": "",
|
||||
"en_US": "The custom refresh rate target value.",
|
||||
"es_ES": "",
|
||||
@@ -23126,7 +23126,7 @@
|
||||
"ID": "SettingsTabSystemCustomVSyncIntervalSliderTooltip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Die benutzerdefinierte Bildwiederholfrequenz als Prozentsatz der normalen Switch-Bildwiederholfrequenz.",
|
||||
"el_GR": "",
|
||||
"en_US": "The custom refresh rate, as a percentage of the normal Switch refresh rate.",
|
||||
"es_ES": "",
|
||||
@@ -23151,7 +23151,7 @@
|
||||
"ID": "SettingsTabSystemCustomVSyncIntervalPercentage",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Benutzerdefinierte Bildwiederholfrequenz %:",
|
||||
"el_GR": "",
|
||||
"en_US": "Custom Refresh Rate %:",
|
||||
"es_ES": "",
|
||||
@@ -23176,7 +23176,7 @@
|
||||
"ID": "SettingsTabSystemCustomVSyncIntervalValue",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Wert für benutzerdefinierte Bildwiederholfrequenz:",
|
||||
"el_GR": "",
|
||||
"en_US": "Custom Refresh Rate Value:",
|
||||
"es_ES": "",
|
||||
@@ -23226,7 +23226,7 @@
|
||||
"ID": "SettingsTabHotkeysToggleVSyncModeHotkey",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "VSync-Modus umschalten:",
|
||||
"el_GR": "",
|
||||
"en_US": "Toggle VSync mode:",
|
||||
"es_ES": "",
|
||||
@@ -23251,7 +23251,7 @@
|
||||
"ID": "SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Benutzerdefinierte Bildwiederholfrequenz erhöhen:",
|
||||
"el_GR": "",
|
||||
"en_US": "Raise custom refresh rate",
|
||||
"es_ES": "",
|
||||
@@ -23276,7 +23276,7 @@
|
||||
"ID": "SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Benutzerdefinierte Bildwiederholfrequenz senken:",
|
||||
"el_GR": "",
|
||||
"en_US": "Lower custom refresh rate:",
|
||||
"es_ES": "",
|
||||
@@ -23301,7 +23301,7 @@
|
||||
"ID": "CompatibilityListLastUpdated",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Zuletzt aktualisiert: {0}",
|
||||
"el_GR": "",
|
||||
"en_US": "Last updated: {0}",
|
||||
"es_ES": "",
|
||||
@@ -23326,7 +23326,7 @@
|
||||
"ID": "CompatibilityListWarning",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Diese Kompatibilitätsliste könnte veraltete Einträge enthalten. Teste dennoch Spiele im \"Ingame\"-Status.",
|
||||
"el_GR": "",
|
||||
"en_US": "This compatibility list might contain out of date entries.\nDo not be opposed to testing games in the \"Ingame\" status.",
|
||||
"es_ES": "",
|
||||
@@ -23351,7 +23351,7 @@
|
||||
"ID": "CompatibilityListSearchBoxWatermark",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Kompatibilitätseinträge durchsuchen...",
|
||||
"el_GR": "",
|
||||
"en_US": "Search compatibility entries...",
|
||||
"es_ES": "",
|
||||
@@ -23401,7 +23401,7 @@
|
||||
"ID": "CompatibilityListOnlyShowOwnedGames",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Nur eigene Spiele anzeigen",
|
||||
"el_GR": "",
|
||||
"en_US": "Only show owned games",
|
||||
"es_ES": "",
|
||||
@@ -23426,7 +23426,7 @@
|
||||
"ID": "CompatibilityListPlayable",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Spielbar",
|
||||
"el_GR": "",
|
||||
"en_US": "Playable",
|
||||
"es_ES": "",
|
||||
@@ -23451,7 +23451,7 @@
|
||||
"ID": "CompatibilityListIngame",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Im Spiel",
|
||||
"el_GR": "",
|
||||
"en_US": "Ingame",
|
||||
"es_ES": "",
|
||||
@@ -23626,7 +23626,7 @@
|
||||
"ID": "CompatibilityListBootsTooltip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Startet, kommt aber nicht über den Titelbildschirm hinaus.",
|
||||
"el_GR": "",
|
||||
"en_US": "Boots but does not make it past the title screen.",
|
||||
"es_ES": "",
|
||||
@@ -23651,7 +23651,7 @@
|
||||
"ID": "CompatibilityListNothingTooltip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Startet nicht oder zeigt keine Anzeichen von Aktivität.",
|
||||
"el_GR": "",
|
||||
"en_US": "Does not boot or shows no signs of activity.",
|
||||
"es_ES": "",
|
||||
@@ -23676,7 +23676,7 @@
|
||||
"ID": "ExtractAocListHeader",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"de_DE": "Wähle ein DLC zum Extrahieren aus",
|
||||
"el_GR": "",
|
||||
"en_US": "Select a DLC to Extract",
|
||||
"es_ES": "",
|
||||
|
||||
@@ -10,6 +10,7 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.HLE.Loaders.Processes;
|
||||
using Ryujinx.Horizon;
|
||||
using Ryujinx.Horizon.Prepo.Types;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
@@ -124,7 +125,7 @@ namespace Ryujinx.Ava
|
||||
_currentApp = null;
|
||||
}
|
||||
|
||||
private static void HandlePlayReport(MessagePackObject playReport)
|
||||
private static void HandlePlayReport(PlayReport playReport)
|
||||
{
|
||||
if (_discordClient is null) return;
|
||||
if (!TitleIDs.CurrentApplication.Value.HasValue) return;
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Runs the configured <see cref="GameSpec.FormatterSpec"/> for the specified game title ID.
|
||||
/// Runs the configured <see cref="FormatterSpec"/> for the specified game title ID.
|
||||
/// </summary>
|
||||
/// <param name="runningGameId">The game currently running.</param>
|
||||
/// <param name="appMeta">The Application metadata information, including localized game name and play time information.</param>
|
||||
@@ -94,54 +94,21 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
public FormattedValue Format(
|
||||
string runningGameId,
|
||||
ApplicationMetadata appMeta,
|
||||
MessagePackObject playReport
|
||||
Horizon.Prepo.Types.PlayReport playReport
|
||||
)
|
||||
{
|
||||
if (!playReport.IsDictionary)
|
||||
if (!playReport.ReportData.IsDictionary)
|
||||
return FormattedValue.Unhandled;
|
||||
|
||||
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.AsDictionary().TryGetValue(formatSpec.ReportKey, out MessagePackObject valuePackObject))
|
||||
if (!formatSpec.Format(appMeta, playReport, out FormattedValue value))
|
||||
continue;
|
||||
|
||||
return formatSpec.Formatter(new Value { Application = appMeta, PackedValue = valuePackObject });
|
||||
}
|
||||
|
||||
foreach (MultiFormatterSpec formatSpec in spec.MultiValueFormatters.OrderBy(x => x.Priority))
|
||||
{
|
||||
List<MessagePackObject> 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.Formatter(packedObjects
|
||||
.Select(packObject => new Value { Application = appMeta, PackedValue = packObject })
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
foreach (SparseMultiFormatterSpec formatSpec in spec.SparseMultiValueFormatters.OrderBy(x => x.Priority))
|
||||
{
|
||||
Dictionary<string, Value> packedObjects = [];
|
||||
foreach (var reportKey in formatSpec.ReportKeys)
|
||||
{
|
||||
if (!playReport.AsDictionary().TryGetValue(reportKey, out MessagePackObject valuePackObject))
|
||||
continue;
|
||||
|
||||
packedObjects.Add(reportKey, new Value { Application = appMeta, PackedValue = valuePackObject });
|
||||
}
|
||||
|
||||
return formatSpec.Formatter(packedObjects);
|
||||
return value;
|
||||
}
|
||||
|
||||
return FormattedValue.Unhandled;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
{
|
||||
/// <summary>
|
||||
/// The delegate type that powers single value formatters.<br/>
|
||||
@@ -12,7 +10,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
/// <br/>
|
||||
/// OR a signal to reset the value that the caller is using the <see cref="Analyzer"/> for.
|
||||
/// </summary>
|
||||
public delegate FormattedValue ValueFormatter(Value value);
|
||||
public delegate FormattedValue SingleValueFormatter(SingleValue value);
|
||||
|
||||
/// <summary>
|
||||
/// The delegate type that powers multiple value formatters.<br/>
|
||||
@@ -24,7 +22,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
/// <br/>
|
||||
/// OR a signal to reset the value that the caller is using the <see cref="Analyzer"/> for.
|
||||
/// </summary>
|
||||
public delegate FormattedValue MultiValueFormatter(Value[] value);
|
||||
public delegate FormattedValue MultiValueFormatter(MultiValue value);
|
||||
|
||||
/// <summary>
|
||||
/// The delegate type that powers multiple value formatters.
|
||||
@@ -38,5 +36,5 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
/// <br/>
|
||||
/// OR a signal to reset the value that the caller is using the <see cref="Analyzer"/> for.
|
||||
/// </summary>
|
||||
public delegate FormattedValue SparseMultiValueFormatter(Dictionary<string, Value> values);
|
||||
public delegate FormattedValue SparseMultiValueFormatter(SparseMultiValue value);
|
||||
}
|
||||
|
||||
74
src/Ryujinx/Utilities/PlayReport/MatchedValues.cs
Normal file
74
src/Ryujinx/Utilities/PlayReport/MatchedValues.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using MsgPack;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
{
|
||||
public abstract class MatchedValue<T>
|
||||
{
|
||||
protected MatchedValue(T matched)
|
||||
{
|
||||
Matched = matched;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The currently running application's <see cref="ApplicationMetadata"/>.
|
||||
/// </summary>
|
||||
public ApplicationMetadata Application { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The entire play report.
|
||||
/// </summary>
|
||||
public Horizon.Prepo.Types.PlayReport PlayReport { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The matched value from the Play Report.
|
||||
/// </summary>
|
||||
public T Matched { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The input data to a <see cref="SingleValueFormatter"/>,
|
||||
/// containing the currently running application's <see cref="ApplicationMetadata"/>,
|
||||
/// and the matched <see cref="MessagePackObject"/> from the Play Report.
|
||||
/// </summary>
|
||||
public class SingleValue : MatchedValue<Value>
|
||||
{
|
||||
public SingleValue(Value matched) : base(matched)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The input data to a <see cref="MultiValueFormatter"/>,
|
||||
/// containing the currently running application's <see cref="ApplicationMetadata"/>,
|
||||
/// and the matched <see cref="MessagePackObject"/>s from the Play Report.
|
||||
/// </summary>
|
||||
public class MultiValue : MatchedValue<Value[]>
|
||||
{
|
||||
public MultiValue(Value[] matched) : base(matched)
|
||||
{
|
||||
}
|
||||
|
||||
public MultiValue(IEnumerable<MessagePackObject> matched) : base(Value.ConvertPackedObjects(matched))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The input data to a <see cref="SparseMultiValueFormatter"/>,
|
||||
/// containing the currently running application's <see cref="ApplicationMetadata"/>,
|
||||
/// and the matched <see cref="MessagePackObject"/>s from the Play Report.
|
||||
/// </summary>
|
||||
public class SparseMultiValue : MatchedValue<Dictionary<string, Value>>
|
||||
{
|
||||
public SparseMultiValue(Dictionary<string, Value> matched) : base(matched)
|
||||
{
|
||||
}
|
||||
|
||||
public SparseMultiValue(Dictionary<string, MessagePackObject> matched) : base(Value.ConvertPackedObjectMap(matched))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,28 +39,28 @@
|
||||
.AddValueFormatter("team_circle", PokemonSVUnionCircle)
|
||||
);
|
||||
|
||||
private static FormattedValue BreathOfTheWild_MasterMode(Value value)
|
||||
=> value.BoxedValue is 1 ? "Playing Master Mode" : FormattedValue.ForceReset;
|
||||
private static FormattedValue BreathOfTheWild_MasterMode(SingleValue value)
|
||||
=> value.Matched.BoxedValue is 1 ? "Playing Master Mode" : FormattedValue.ForceReset;
|
||||
|
||||
private static FormattedValue TearsOfTheKingdom_CurrentField(Value value) =>
|
||||
value.DoubleValue switch
|
||||
private static FormattedValue TearsOfTheKingdom_CurrentField(SingleValue value) =>
|
||||
value.Matched.DoubleValue switch
|
||||
{
|
||||
> 800d => "Exploring the Sky Islands",
|
||||
< -201d => "Exploring the Depths",
|
||||
_ => "Roaming Hyrule"
|
||||
};
|
||||
|
||||
private static FormattedValue SuperMarioOdyssey_AssistMode(Value value)
|
||||
=> value.BoxedValue is 1 ? "Playing in Assist Mode" : "Playing in Regular Mode";
|
||||
private static FormattedValue SuperMarioOdyssey_AssistMode(SingleValue value)
|
||||
=> value.Matched.BoxedValue is 1 ? "Playing in Assist Mode" : "Playing in Regular Mode";
|
||||
|
||||
private static FormattedValue SuperMarioOdysseyChina_AssistMode(Value value)
|
||||
=> value.BoxedValue is 1 ? "Playing in 帮助模式" : "Playing in 普通模式";
|
||||
private static FormattedValue SuperMarioOdysseyChina_AssistMode(SingleValue value)
|
||||
=> value.Matched.BoxedValue is 1 ? "Playing in 帮助模式" : "Playing in 普通模式";
|
||||
|
||||
private static FormattedValue SuperMario3DWorldOrBowsersFury(Value value)
|
||||
=> value.BoxedValue is 0 ? "Playing Super Mario 3D World" : "Playing Bowser's Fury";
|
||||
private static FormattedValue SuperMario3DWorldOrBowsersFury(SingleValue value)
|
||||
=> value.Matched.BoxedValue is 0 ? "Playing Super Mario 3D World" : "Playing Bowser's Fury";
|
||||
|
||||
private static FormattedValue MarioKart8Deluxe_Mode(Value value)
|
||||
=> value.StringValue switch
|
||||
private static FormattedValue MarioKart8Deluxe_Mode(SingleValue value)
|
||||
=> value.Matched.StringValue switch
|
||||
{
|
||||
// Single Player
|
||||
"Single" => "Single Player",
|
||||
@@ -87,11 +87,11 @@
|
||||
_ => FormattedValue.ForceReset
|
||||
};
|
||||
|
||||
private static FormattedValue PokemonSVUnionCircle(Value value)
|
||||
=> value.BoxedValue is 0 ? "Playing Alone" : "Playing in a group";
|
||||
private static FormattedValue PokemonSVUnionCircle(SingleValue value)
|
||||
=> value.Matched.BoxedValue is 0 ? "Playing Alone" : "Playing in a group";
|
||||
|
||||
private static FormattedValue PokemonSVArea(Value value)
|
||||
=> value.StringValue switch
|
||||
private static FormattedValue PokemonSVArea(SingleValue value)
|
||||
=> value.Matched.StringValue switch
|
||||
{
|
||||
// Base Game Locations
|
||||
"a_w01" => "South Area One",
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
public class GameSpec
|
||||
{
|
||||
private int _lastPriority;
|
||||
|
||||
public required string[] TitleIds { get; init; }
|
||||
public List<FormatterSpec> SimpleValueFormatters { get; } = [];
|
||||
public List<MultiFormatterSpec> MultiValueFormatters { get; } = [];
|
||||
public List<SparseMultiFormatterSpec> SparseMultiValueFormatters { get; } = [];
|
||||
|
||||
public List<FormatterSpecBase> ValueFormatters { get; } = [];
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -24,8 +28,8 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
/// <param name="reportKey">The key name to match.</param>
|
||||
/// <param name="valueFormatter">The function which can return a potential formatted value.</param>
|
||||
/// <returns>The current <see cref="GameSpec"/>, for chaining convenience.</returns>
|
||||
public GameSpec AddValueFormatter(string reportKey, ValueFormatter valueFormatter)
|
||||
=> AddValueFormatter(SimpleValueFormatters.Count, reportKey, valueFormatter);
|
||||
public GameSpec AddValueFormatter(string reportKey, SingleValueFormatter valueFormatter)
|
||||
=> AddValueFormatter(_lastPriority++, reportKey, valueFormatter);
|
||||
|
||||
/// <summary>
|
||||
/// Add a value formatter at a specific priority to the current <see cref="GameSpec"/>
|
||||
@@ -36,11 +40,11 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
/// <param name="valueFormatter">The function which can return a potential formatted value.</param>
|
||||
/// <returns>The current <see cref="GameSpec"/>, for chaining convenience.</returns>
|
||||
public GameSpec AddValueFormatter(int priority, string reportKey,
|
||||
ValueFormatter valueFormatter)
|
||||
SingleValueFormatter 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
|
||||
/// <param name="valueFormatter">The function which can format the values.</param>
|
||||
/// <returns>The current <see cref="GameSpec"/>, for chaining convenience.</returns>
|
||||
public GameSpec AddMultiValueFormatter(string[] reportKeys, MultiValueFormatter valueFormatter)
|
||||
=> AddMultiValueFormatter(MultiValueFormatters.Count, reportKeys, valueFormatter);
|
||||
=> AddMultiValueFormatter(_lastPriority++, reportKeys, valueFormatter);
|
||||
|
||||
/// <summary>
|
||||
/// Add a multi-value formatter at a specific priority to the current <see cref="GameSpec"/>
|
||||
@@ -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
|
||||
/// <param name="valueFormatter">The function which can format the values.</param>
|
||||
/// <returns>The current <see cref="GameSpec"/>, for chaining convenience.</returns>
|
||||
public GameSpec AddSparseMultiValueFormatter(string[] reportKeys, SparseMultiValueFormatter valueFormatter)
|
||||
=> AddSparseMultiValueFormatter(SparseMultiValueFormatters.Count, reportKeys, valueFormatter);
|
||||
=> AddSparseMultiValueFormatter(_lastPriority++, reportKeys, valueFormatter);
|
||||
|
||||
/// <summary>
|
||||
/// Add a multi-value formatter at a specific priority to the current <see cref="GameSpec"/>
|
||||
@@ -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,101 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
/// <summary>
|
||||
/// A struct containing the data for a mapping of a key in a Play Report to a formatter for its potential value.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A struct containing the data for a mapping of an arbitrary key set in a Play Report to a formatter for their potential values.
|
||||
/// </summary>
|
||||
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<MessagePackObject> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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<string, MessagePackObject> 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;
|
||||
}
|
||||
|
||||
switch (Formatter)
|
||||
{
|
||||
case SingleValueFormatter svf when data is MessagePackObject mpo:
|
||||
formattedValue = svf(new SingleValue(mpo) { Application = appMeta, PlayReport = playReport });
|
||||
return true;
|
||||
case MultiValueFormatter mvf when data is List<MessagePackObject> messagePackObjects:
|
||||
formattedValue = mvf(new MultiValue(messagePackObjects) { Application = appMeta, PlayReport = playReport });
|
||||
return true;
|
||||
case SparseMultiValueFormatter smvf when
|
||||
data is Dictionary<string, MessagePackObject> sparseMessagePackObjects:
|
||||
formattedValue = smvf(new SparseMultiValue(sparseMessagePackObjects) { Application = appMeta, PlayReport = playReport });
|
||||
return true;
|
||||
default:
|
||||
throw new InvalidOperationException("Formatter delegate is not of a known type!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
using MsgPack;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
{
|
||||
/// <summary>
|
||||
/// The input data to a <see cref="ValueFormatter"/>,
|
||||
/// containing the currently running application's <see cref="ApplicationMetadata"/>,
|
||||
/// The base input data to a ValueFormatter delegate,
|
||||
/// and the matched <see cref="MessagePackObject"/> from the Play Report.
|
||||
/// </summary>
|
||||
public class Value
|
||||
public readonly struct Value
|
||||
{
|
||||
/// <summary>
|
||||
/// The currently running application's <see cref="ApplicationMetadata"/>.
|
||||
/// </summary>
|
||||
public ApplicationMetadata Application { get; init; }
|
||||
public Value(MessagePackObject packedValue)
|
||||
{
|
||||
PackedValue = packedValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The matched value from the Play Report.
|
||||
@@ -37,6 +37,17 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
: boxed.ToString();
|
||||
}
|
||||
|
||||
public static implicit operator Value(MessagePackObject matched) => new(matched);
|
||||
|
||||
public static Value[] ConvertPackedObjects(IEnumerable<MessagePackObject> packObjects)
|
||||
=> packObjects.Select(packObject => new Value(packObject)).ToArray();
|
||||
|
||||
public static Dictionary<string, Value> ConvertPackedObjectMap(Dictionary<string, MessagePackObject> packObjects)
|
||||
=> packObjects.ToDictionary(
|
||||
x => x.Key,
|
||||
x => new Value(x.Value)
|
||||
);
|
||||
|
||||
#region AsX accessors
|
||||
|
||||
public bool BooleanValue => PackedValue.AsBoolean();
|
||||
@@ -57,7 +68,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A potential formatted value returned by a <see cref="ValueFormatter"/>.
|
||||
/// A potential formatted value returned by a ValueFormatter delegate.
|
||||
/// </summary>
|
||||
public readonly struct FormattedValue
|
||||
{
|
||||
@@ -103,28 +114,47 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
/// <summary>
|
||||
/// Return this to tell the caller there is no value to return.
|
||||
/// </summary>
|
||||
public static FormattedValue Unhandled => default;
|
||||
public static readonly FormattedValue Unhandled = default;
|
||||
|
||||
/// <summary>
|
||||
/// Return this to suggest the caller reset the value it's using the <see cref="Analyzer"/> for.
|
||||
/// </summary>
|
||||
public static FormattedValue ForceReset => new() { Handled = true, Reset = true };
|
||||
public static readonly FormattedValue ForceReset = new() { Handled = true, Reset = true };
|
||||
|
||||
/// <summary>
|
||||
/// A delegate singleton you can use to always return <see cref="ForceReset"/> in a <see cref="ValueFormatter"/>.
|
||||
/// A delegate singleton you can use to always return <see cref="ForceReset"/> in a <see cref="SingleValueFormatter"/>.
|
||||
/// </summary>
|
||||
public static readonly ValueFormatter SingleAlwaysResets = _ => ForceReset;
|
||||
public static readonly SingleValueFormatter SingleAlwaysResets = _ => ForceReset;
|
||||
|
||||
/// <summary>
|
||||
/// A delegate singleton you can use to always return <see cref="ForceReset"/> in a <see cref="MultiValueFormatter"/>.
|
||||
/// </summary>
|
||||
public static readonly MultiValueFormatter MultiAlwaysResets = _ => ForceReset;
|
||||
|
||||
/// <summary>
|
||||
/// A delegate singleton you can use to always return <see cref="ForceReset"/> in a <see cref="SparseMultiValueFormatter"/>.
|
||||
/// </summary>
|
||||
public static readonly SparseMultiValueFormatter SparseMultiAlwaysResets = _ => ForceReset;
|
||||
|
||||
/// <summary>
|
||||
/// A delegate factory you can use to always return the specified
|
||||
/// <paramref name="formattedValue"/> in a <see cref="ValueFormatter"/>.
|
||||
/// <paramref name="formattedValue"/> in a <see cref="SingleValueFormatter"/>.
|
||||
/// </summary>
|
||||
/// <param name="formattedValue">The string to always return for this delegate instance.</param>
|
||||
public static ValueFormatter AlwaysReturns(string formattedValue) => _ => formattedValue;
|
||||
public static SingleValueFormatter SingleAlwaysReturns(string formattedValue) => _ => formattedValue;
|
||||
|
||||
/// <summary>
|
||||
/// A delegate factory you can use to always return the specified
|
||||
/// <paramref name="formattedValue"/> in a <see cref="MultiValueFormatter"/>.
|
||||
/// </summary>
|
||||
/// <param name="formattedValue">The string to always return for this delegate instance.</param>
|
||||
public static MultiValueFormatter MultiAlwaysReturns(string formattedValue) => _ => formattedValue;
|
||||
|
||||
/// <summary>
|
||||
/// A delegate factory you can use to always return the specified
|
||||
/// <paramref name="formattedValue"/> in a <see cref="SparseMultiValueFormatter"/>.
|
||||
/// </summary>
|
||||
/// <param name="formattedValue">The string to always return for this delegate instance.</param>
|
||||
public static SparseMultiValueFormatter SparseMultiAlwaysReturns(string formattedValue) => _ => formattedValue;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user