Compare commits
26 Commits
Canary-1.2
...
940ecbbc76
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
940ecbbc76 | ||
|
|
ae16360685 | ||
|
|
45b1794a45 | ||
|
|
39fbbb39e0 | ||
|
|
11a68a204f | ||
|
|
e4e38c4bdd | ||
|
|
5f5c76107c | ||
|
|
9c226dcc7a | ||
|
|
30a534edcd | ||
|
|
1d88771d1b | ||
|
|
a92475b8fd | ||
|
|
4d5ae23b0b | ||
|
|
4cb2170ad1 | ||
|
|
4e8157688e | ||
|
|
5085af0050 | ||
|
|
fc01f7b58f | ||
|
|
fa463c51f8 | ||
|
|
3a8e6e3117 | ||
|
|
7f4a161ca5 | ||
|
|
df1c7613e8 | ||
|
|
6d37d79cc7 | ||
|
|
afdfcc1720 | ||
|
|
fe94224d01 | ||
|
|
fbe1a7d966 | ||
|
|
5dc7fb4495 | ||
|
|
2640f083b6 |
@@ -1,5 +1,6 @@
|
|||||||
using MsgPack;
|
using MsgPack;
|
||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
|
using Ryujinx.Horizon.Prepo.Types;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -8,7 +9,7 @@ namespace Ryujinx.Horizon
|
|||||||
{
|
{
|
||||||
public static class HorizonStatic
|
public static class HorizonStatic
|
||||||
{
|
{
|
||||||
internal static void HandlePlayReport(MessagePackObject report) =>
|
internal static void HandlePlayReport(PlayReport report) =>
|
||||||
new Thread(() => PlayReport?.Invoke(report))
|
new Thread(() => PlayReport?.Invoke(report))
|
||||||
{
|
{
|
||||||
Name = "HLE.PlayReportEvent",
|
Name = "HLE.PlayReportEvent",
|
||||||
@@ -16,7 +17,7 @@ namespace Ryujinx.Horizon
|
|||||||
Priority = ThreadPriority.AboveNormal
|
Priority = ThreadPriority.AboveNormal
|
||||||
}.Start();
|
}.Start();
|
||||||
|
|
||||||
public static event Action<MessagePackObject> PlayReport;
|
public static event Action<PlayReport> PlayReport;
|
||||||
|
|
||||||
[field: ThreadStatic]
|
[field: ThreadStatic]
|
||||||
public static HorizonOptions Options { get; private set; }
|
public static HorizonOptions Options { get; private set; }
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using Gommon;
|
|
||||||
using MsgPack;
|
using MsgPack;
|
||||||
using MsgPack.Serialization;
|
using MsgPack.Serialization;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
@@ -12,19 +11,12 @@ using Ryujinx.Horizon.Sdk.Sf;
|
|||||||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
|
||||||
using ApplicationId = Ryujinx.Horizon.Sdk.Ncm.ApplicationId;
|
using ApplicationId = Ryujinx.Horizon.Sdk.Ncm.ApplicationId;
|
||||||
|
|
||||||
namespace Ryujinx.Horizon.Prepo.Ipc
|
namespace Ryujinx.Horizon.Prepo.Ipc
|
||||||
{
|
{
|
||||||
partial class PrepoService : IPrepoService
|
partial class PrepoService : IPrepoService
|
||||||
{
|
{
|
||||||
enum PlayReportKind
|
|
||||||
{
|
|
||||||
Normal,
|
|
||||||
System,
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly ArpApi _arp;
|
private readonly ArpApi _arp;
|
||||||
private readonly PrepoServicePermissionLevel _permissionLevel;
|
private readonly PrepoServicePermissionLevel _permissionLevel;
|
||||||
private ulong _systemSessionId;
|
private ulong _systemSessionId;
|
||||||
@@ -196,10 +188,17 @@ namespace Ryujinx.Horizon.Prepo.Ipc
|
|||||||
{
|
{
|
||||||
return PrepoResult.InvalidBufferSize;
|
return PrepoResult.InvalidBufferSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder builder = new();
|
StringBuilder builder = new();
|
||||||
MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(reportBuffer.ToArray());
|
MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(reportBuffer.ToArray());
|
||||||
|
|
||||||
|
PlayReport playReport = new()
|
||||||
|
{
|
||||||
|
Kind = playReportKind,
|
||||||
|
Room = gameRoom,
|
||||||
|
ReportData = deserializedReport
|
||||||
|
};
|
||||||
|
|
||||||
builder.AppendLine();
|
builder.AppendLine();
|
||||||
builder.AppendLine("PlayReport log:");
|
builder.AppendLine("PlayReport log:");
|
||||||
builder.AppendLine($" Kind: {playReportKind}");
|
builder.AppendLine($" Kind: {playReportKind}");
|
||||||
@@ -209,10 +208,12 @@ namespace Ryujinx.Horizon.Prepo.Ipc
|
|||||||
if (pid != 0)
|
if (pid != 0)
|
||||||
{
|
{
|
||||||
builder.AppendLine($" Pid: {pid}");
|
builder.AppendLine($" Pid: {pid}");
|
||||||
|
playReport.Pid = pid;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
builder.AppendLine($" ApplicationId: {applicationId}");
|
builder.AppendLine($" ApplicationId: {applicationId}");
|
||||||
|
playReport.AppId = applicationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result result = _arp.GetApplicationInstanceId(out ulong applicationInstanceId, pid);
|
Result result = _arp.GetApplicationInstanceId(out ulong applicationInstanceId, pid);
|
||||||
@@ -223,17 +224,20 @@ namespace Ryujinx.Horizon.Prepo.Ipc
|
|||||||
|
|
||||||
_arp.GetApplicationLaunchProperty(out ApplicationLaunchProperty applicationLaunchProperty, applicationInstanceId).AbortOnFailure();
|
_arp.GetApplicationLaunchProperty(out ApplicationLaunchProperty applicationLaunchProperty, applicationInstanceId).AbortOnFailure();
|
||||||
|
|
||||||
|
playReport.Version = applicationLaunchProperty.Version;
|
||||||
|
|
||||||
builder.AppendLine($" ApplicationVersion: {applicationLaunchProperty.Version}");
|
builder.AppendLine($" ApplicationVersion: {applicationLaunchProperty.Version}");
|
||||||
|
|
||||||
if (!userId.IsNull)
|
if (!userId.IsNull)
|
||||||
{
|
{
|
||||||
builder.AppendLine($" UserId: {userId}");
|
builder.AppendLine($" UserId: {userId}");
|
||||||
|
playReport.UserId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.AppendLine($" Room: {gameRoom}");
|
builder.AppendLine($" Room: {gameRoom}");
|
||||||
builder.AppendLine($" Report: {MessagePackObjectFormatter.Format(deserializedReport)}");
|
builder.AppendLine($" Report: {MessagePackObjectFormatter.Format(deserializedReport)}");
|
||||||
|
|
||||||
HorizonStatic.HandlePlayReport(deserializedReport);
|
HorizonStatic.HandlePlayReport(playReport);
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.ServicePrepo, builder.ToString());
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -582,6 +582,10 @@ namespace Ryujinx.Ava
|
|||||||
Rainbow.Disable();
|
Rainbow.Disable();
|
||||||
Rainbow.Reset();
|
Rainbow.Reset();
|
||||||
|
|
||||||
|
// Reload settings when the game is turned off
|
||||||
|
// (resets custom settings if there were any)
|
||||||
|
Program.ReloadConfig();
|
||||||
|
|
||||||
_isStopped = true;
|
_isStopped = true;
|
||||||
Stop();
|
Stop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<Styles
|
<Styles
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||||
xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia">
|
xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia">
|
||||||
<Design.PreviewWith>
|
<Design.PreviewWith>
|
||||||
<Border Height="2000"
|
<Border Height="2000"
|
||||||
@@ -30,7 +31,8 @@
|
|||||||
<Button
|
<Button
|
||||||
Name="btnRem"
|
Name="btnRem"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Content="Add" />
|
Content="Add"
|
||||||
|
Classes="red"/>
|
||||||
<TextBox
|
<TextBox
|
||||||
Width="100"
|
Width="100"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@@ -41,7 +43,11 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<ui:NumberBox Value="1" />
|
<ui:NumberBox Value="1" />
|
||||||
|
<MenuItem
|
||||||
|
Header="123 0000"
|
||||||
|
ToolTip.Tip="What this"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
</Border>
|
</Border>
|
||||||
</Design.PreviewWith>
|
</Design.PreviewWith>
|
||||||
<Style Selector="DropDownButton">
|
<Style Selector="DropDownButton">
|
||||||
@@ -373,6 +379,16 @@
|
|||||||
<Setter Property="Background"
|
<Setter Property="Background"
|
||||||
Value="{DynamicResource AppListHoverBackgroundColor}" />
|
Value="{DynamicResource AppListHoverBackgroundColor}" />
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style Selector="Button.red /template/ ContentPresenter">
|
||||||
|
<Setter Property="CornerRadius" Value="4"/>
|
||||||
|
<Setter Property="Background" Value="red"/>
|
||||||
|
<Setter Property="Foreground" Value="White"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Button.red:pointerover /template/ ContentPresenter">
|
||||||
|
<Setter Property="CornerRadius" Value="4"/>
|
||||||
|
<Setter Property="Background" Value="{DynamicResource WarningBackgroundColor}" />
|
||||||
|
<Setter Property="Foreground" Value="White"/>
|
||||||
|
</Style>
|
||||||
<Styles.Resources>
|
<Styles.Resources>
|
||||||
<SolidColorBrush x:Key="ThemeAccentColorBrush"
|
<SolidColorBrush x:Key="ThemeAccentColorBrush"
|
||||||
Color="{DynamicResource SystemAccentColor}" />
|
Color="{DynamicResource SystemAccentColor}" />
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
<ResourceDictionary.ThemeDictionaries>
|
<ResourceDictionary.ThemeDictionaries>
|
||||||
<ResourceDictionary x:Key="Default">
|
<ResourceDictionary x:Key="Default">
|
||||||
@@ -12,11 +12,13 @@
|
|||||||
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
|
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
|
||||||
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
|
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
|
||||||
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
|
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
|
||||||
|
<Color x:Key="WarningBackgroundColor">#FF6347</Color>
|
||||||
<Color x:Key="SecondaryTextColor">#A0000000</Color>
|
<Color x:Key="SecondaryTextColor">#A0000000</Color>
|
||||||
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
|
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
|
||||||
<Color x:Key="Switch">#FF2EEAC9</Color>
|
<Color x:Key="Switch">#FF2EEAC9</Color>
|
||||||
<Color x:Key="Unbounded">#FFFF4554</Color>
|
<Color x:Key="Unbounded">#FFFF4554</Color>
|
||||||
<Color x:Key="Custom">#6483F5</Color>
|
<Color x:Key="Custom">#6483F5</Color>
|
||||||
|
<Color x:Key="Warning">#800080</Color>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
<ResourceDictionary x:Key="Light">
|
<ResourceDictionary x:Key="Light">
|
||||||
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
|
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
|
||||||
@@ -29,11 +31,13 @@
|
|||||||
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
|
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
|
||||||
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
|
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
|
||||||
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
|
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
|
||||||
|
<Color x:Key="WarningBackgroundColor">#FF6347</Color>
|
||||||
<Color x:Key="SecondaryTextColor">#A0000000</Color>
|
<Color x:Key="SecondaryTextColor">#A0000000</Color>
|
||||||
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
|
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
|
||||||
<Color x:Key="Switch">#13c3a4</Color>
|
<Color x:Key="Switch">#13c3a4</Color>
|
||||||
<Color x:Key="Unbounded">#FFFF4554</Color>
|
<Color x:Key="Unbounded">#FFFF4554</Color>
|
||||||
<Color x:Key="Custom">#6483F5</Color>
|
<Color x:Key="Custom">#6483F5</Color>
|
||||||
|
<Color x:Key="Warning">#800080</Color>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
<ResourceDictionary x:Key="Dark">
|
<ResourceDictionary x:Key="Dark">
|
||||||
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
|
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
|
||||||
@@ -46,11 +50,13 @@
|
|||||||
<Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color>
|
<Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color>
|
||||||
<Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color>
|
<Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color>
|
||||||
<Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color>
|
<Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color>
|
||||||
|
<Color x:Key="WarningBackgroundColor">#FF6347</Color>
|
||||||
<Color x:Key="SecondaryTextColor">#A0FFFFFF</Color>
|
<Color x:Key="SecondaryTextColor">#A0FFFFFF</Color>
|
||||||
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
|
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
|
||||||
<Color x:Key="Switch">#FF2EEAC9</Color>
|
<Color x:Key="Switch">#FF2EEAC9</Color>
|
||||||
<Color x:Key="Unbounded">#FFFF4554</Color>
|
<Color x:Key="Unbounded">#FFFF4554</Color>
|
||||||
<Color x:Key="Custom">#6483F5</Color>
|
<Color x:Key="Custom">#6483F5</Color>
|
||||||
|
<Color x:Key="Warning">#FFA500</Color>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ResourceDictionary.ThemeDictionaries>
|
</ResourceDictionary.ThemeDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -2722,6 +2722,31 @@
|
|||||||
"zh_TW": "建立桌面捷徑,啟動選取的應用程式"
|
"zh_TW": "建立桌面捷徑,啟動選取的應用程式"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "GameListContextMenuEditGameConfiguration",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "تعديل تكوين اللعبة",
|
||||||
|
"de_DE": "Spielkonfiguration bearbeiten",
|
||||||
|
"el_GR": "Επεξεργασία ρυθμίσεων παιχνιδιού",
|
||||||
|
"en_US": "Edit Game Configuration",
|
||||||
|
"es_ES": "Editar configuración del juego",
|
||||||
|
"fr_FR": "Modifier la configuration du jeu",
|
||||||
|
"he_IL": "ערוך את הגדרת המשחק",
|
||||||
|
"it_IT": "Modifica configurazione gioco",
|
||||||
|
"ja_JP": "ゲーム設定を編集",
|
||||||
|
"ko_KR": "게임 설정 편집",
|
||||||
|
"no_NO": "Rediger spillkonfig.",
|
||||||
|
"pl_PL": "Edytuj konfigurację gry",
|
||||||
|
"pt_BR": "Editar configuração do jogo",
|
||||||
|
"ru_RU": "Редактировать конфигурацию игры",
|
||||||
|
"sv_SE": "Redigera spelkonfiguration",
|
||||||
|
"th_TH": "แก้ไขการตั้งค่าเกม",
|
||||||
|
"tr_TR": "Oyunun yapılandırmasını düzenle",
|
||||||
|
"uk_UA": "Редагувати конфігурацію гри",
|
||||||
|
"zh_CN": "编辑游戏配置",
|
||||||
|
"zh_TW": "編輯遊戲設定"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "GameListContextMenuCreateShortcutToolTipMacOS",
|
"ID": "GameListContextMenuCreateShortcutToolTipMacOS",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
@@ -2747,6 +2772,31 @@
|
|||||||
"zh_TW": "在 macOS 的應用程式資料夾中建立捷徑,啟動選取的應用程式"
|
"zh_TW": "在 macOS 的應用程式資料夾中建立捷徑,啟動選取的應用程式"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "EditGameConfigurationToolTip",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "ينشئ تكوينًا مستقلًا للعبة الحالية",
|
||||||
|
"de_DE": "Erstellt eine unabhängige Konfiguration für das aktuelle Spiel",
|
||||||
|
"el_GR": "Δημιουργεί μια ανεξάρτητη διαμόρφωση για το τρέχον παιχνίδι",
|
||||||
|
"en_US": "Creates an independent configuration for the current game",
|
||||||
|
"es_ES": "Crea una configuración independiente para el juego actual",
|
||||||
|
"fr_FR": "Crée une configuration indépendante pour le jeu en cours",
|
||||||
|
"he_IL": "יוצר תצורה עצמאית למשחק הנוכחי",
|
||||||
|
"it_IT": "Crea una configurazione indipendente per il gioco attuale",
|
||||||
|
"ja_JP": "現在のゲーム用の独立した設定を作成します",
|
||||||
|
"ko_KR": "현재 게임에 대한 독립적인 설정을 생성합니다",
|
||||||
|
"no_NO": "Oppretter en uavhengig konfigurasjon for det gjeldende spillet",
|
||||||
|
"pl_PL": "Tworzy niezależną konfigurację dla bieżącej gry",
|
||||||
|
"pt_BR": "Cria uma configuração independente para o jogo atual",
|
||||||
|
"ru_RU": "Создает независимую конфигурацию для текущей игры",
|
||||||
|
"sv_SE": "Skapar en oberoende konfiguration för det aktuella spelet",
|
||||||
|
"th_TH": "สร้างการกำหนดค่าที่เป็นอิสระสำหรับเกมปัจจุบัน",
|
||||||
|
"tr_TR": "Mevcut oyun için bağımsız bir yapılandırma oluşturur",
|
||||||
|
"uk_UA": "Створює незалежну конфігурацію для поточної гри",
|
||||||
|
"zh_CN": "为当前游戏创建独立的配置",
|
||||||
|
"zh_TW": "為當前遊戲創建獨立的配置"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "GameListContextMenuShowCompatEntry",
|
"ID": "GameListContextMenuShowCompatEntry",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
@@ -3272,6 +3322,31 @@
|
|||||||
"zh_TW": "設定"
|
"zh_TW": "設定"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "SettingsWithInfo",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "{0} - إعدادات",
|
||||||
|
"de_DE": "Einstellungen - {0}",
|
||||||
|
"el_GR": "Ρυθμίσεις - {0}",
|
||||||
|
"en_US": "Settings - {0}",
|
||||||
|
"es_ES": "Configuración - {0}",
|
||||||
|
"fr_FR": "Paramètres - {0}",
|
||||||
|
"he_IL": "{0} - הגדרות",
|
||||||
|
"it_IT": "Impostazioni - {0}",
|
||||||
|
"ja_JP": "設定 - {0}",
|
||||||
|
"ko_KR": "설정 - {0}",
|
||||||
|
"no_NO": "Innstillinger - {0}",
|
||||||
|
"pl_PL": "Ustawienia - {0}",
|
||||||
|
"pt_BR": "Configurações - {0}",
|
||||||
|
"ru_RU": "Параметры - {0}",
|
||||||
|
"sv_SE": "Inställningar - {0}",
|
||||||
|
"th_TH": "ตั้งค่า - {0}",
|
||||||
|
"tr_TR": "Ayarlar - {0}",
|
||||||
|
"uk_UA": "Налаштування - {0}",
|
||||||
|
"zh_CN": "设置 - {0}",
|
||||||
|
"zh_TW": "設定 - {0}"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "SettingsTabGeneral",
|
"ID": "SettingsTabGeneral",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
@@ -23672,6 +23747,31 @@
|
|||||||
"zh_TW": ""
|
"zh_TW": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "UserConfigurationHeader",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "User Config",
|
||||||
|
"es_ES": "",
|
||||||
|
"fr_FR": "",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "ExtractAocListHeader",
|
"ID": "ExtractAocListHeader",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
@@ -23696,6 +23796,56 @@
|
|||||||
"zh_CN": "选择一个要解压的 DLC",
|
"zh_CN": "选择一个要解压的 DLC",
|
||||||
"zh_TW": ""
|
"zh_TW": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": "GameInfoRpcImage",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "Rich Presence Image",
|
||||||
|
"es_ES": "",
|
||||||
|
"fr_FR": "",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": "GameInfoRpcDynamic",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "Dynamic Rich Presence",
|
||||||
|
"es_ES": "",
|
||||||
|
"fr_FR": "",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,8 @@ using Ryujinx.Common.Logging;
|
|||||||
using Ryujinx.HLE;
|
using Ryujinx.HLE;
|
||||||
using Ryujinx.HLE.Loaders.Processes;
|
using Ryujinx.HLE.Loaders.Processes;
|
||||||
using Ryujinx.Horizon;
|
using Ryujinx.Horizon;
|
||||||
|
using Ryujinx.Horizon.Prepo.Types;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.Ava
|
namespace Ryujinx.Ava
|
||||||
@@ -37,6 +39,9 @@ namespace Ryujinx.Ava
|
|||||||
private static RichPresence _discordPresencePlaying;
|
private static RichPresence _discordPresencePlaying;
|
||||||
private static ApplicationMetadata _currentApp;
|
private static ApplicationMetadata _currentApp;
|
||||||
|
|
||||||
|
public static bool HasAssetImage(string titleId) => TitleIDs.DiscordGameAssetKeys.ContainsIgnoreCase(titleId);
|
||||||
|
public static bool HasAnalyzer(string titleId) => PlayReports.Analyzer.TitleIds.ContainsIgnoreCase(titleId);
|
||||||
|
|
||||||
public static void Initialize()
|
public static void Initialize()
|
||||||
{
|
{
|
||||||
_discordPresenceMain = new RichPresence
|
_discordPresenceMain = new RichPresence
|
||||||
@@ -120,7 +125,7 @@ namespace Ryujinx.Ava
|
|||||||
_currentApp = null;
|
_currentApp = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandlePlayReport(MessagePackObject playReport)
|
private static void HandlePlayReport(PlayReport playReport)
|
||||||
{
|
{
|
||||||
if (_discordClient is null) return;
|
if (_discordClient is null) return;
|
||||||
if (!TitleIDs.CurrentApplication.Value.HasValue) return;
|
if (!TitleIDs.CurrentApplication.Value.HasValue) return;
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ namespace Ryujinx.Ava
|
|||||||
public static double DesktopScaleFactor { get; set; } = 1.0;
|
public static double DesktopScaleFactor { get; set; } = 1.0;
|
||||||
public static string Version { get; private set; }
|
public static string Version { get; private set; }
|
||||||
public static string ConfigurationPath { get; private set; }
|
public static string ConfigurationPath { get; private set; }
|
||||||
|
public static string GlobalConfigurationPath { get; private set; }
|
||||||
public static bool PreviewerDetached { get; private set; }
|
public static bool PreviewerDetached { get; private set; }
|
||||||
public static bool UseHardwareAcceleration { get; private set; }
|
public static bool UseHardwareAcceleration { get; private set; }
|
||||||
|
|
||||||
@@ -155,11 +156,43 @@ namespace Ryujinx.Ava
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool FindGameConfig(string gameDir)
|
||||||
|
{
|
||||||
|
if (File.Exists(gameDir))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetDirGameUserConfig(string gameId, bool rememberGlobalDir = false, bool changeFolderForGame = false)
|
||||||
|
{
|
||||||
|
string gameDir = Path.Combine(AppDataManager.GamesDirPath, gameId, ReleaseInformation.ConfigName);
|
||||||
|
|
||||||
|
// Should load with the game if there is a custom setting for the game
|
||||||
|
if (rememberGlobalDir)
|
||||||
|
{
|
||||||
|
GlobalConfigurationPath = ConfigurationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeFolderForGame)
|
||||||
|
{
|
||||||
|
ConfigurationPath = gameDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
return gameDir;
|
||||||
|
}
|
||||||
|
|
||||||
public static void ReloadConfig()
|
public static void ReloadConfig()
|
||||||
{
|
{
|
||||||
|
//It is necessary that when a user setting appears, the global setting remains available
|
||||||
|
GlobalConfigurationPath = null;
|
||||||
|
|
||||||
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
|
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
|
||||||
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
|
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
|
||||||
|
|
||||||
|
|
||||||
// Now load the configuration as the other subsystems are now registered
|
// Now load the configuration as the other subsystems are now registered
|
||||||
if (File.Exists(localConfigurationPath))
|
if (File.Exists(localConfigurationPath))
|
||||||
{
|
{
|
||||||
@@ -232,8 +265,35 @@ namespace Ryujinx.Ava
|
|||||||
_ => ConfigurationState.Instance.HideCursor,
|
_ => ConfigurationState.Instance.HideCursor,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Check if memoryManagerMode was overridden.
|
||||||
|
if (CommandLineState.OverrideMemoryManagerMode is not null)
|
||||||
|
if (Enum.TryParse(CommandLineState.OverrideMemoryManagerMode, true, out MemoryManagerMode result))
|
||||||
|
{
|
||||||
|
ConfigurationState.Instance.System.MemoryManagerMode.Value = result;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if hardware-acceleration was overridden.
|
// Check if PPTC was overridden.
|
||||||
|
if (CommandLineState.OverridePPTC is not null)
|
||||||
|
if (Enum.TryParse(CommandLineState.OverridePPTC, true, out bool result))
|
||||||
|
{
|
||||||
|
ConfigurationState.Instance.System.EnablePtc.Value = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if region was overridden.
|
||||||
|
if (CommandLineState.OverrideSystemRegion is not null)
|
||||||
|
if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out Ryujinx.HLE.HOS.SystemState.RegionCode result))
|
||||||
|
{
|
||||||
|
ConfigurationState.Instance.System.Region.Value = (Utilities.Configuration.System.Region)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if language was overridden.
|
||||||
|
if (CommandLineState.OverrideSystemLanguage is not null)
|
||||||
|
if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out Ryujinx.HLE.HOS.SystemState.SystemLanguage result))
|
||||||
|
{
|
||||||
|
ConfigurationState.Instance.System.Language.Value = (Utilities.Configuration.System.Language)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if hardware-acceleration was overridden. MemoryManagerMode ( outdated! )
|
||||||
if (CommandLineState.OverrideHardwareAcceleration != null)
|
if (CommandLineState.OverrideHardwareAcceleration != null)
|
||||||
UseHardwareAcceleration = CommandLineState.OverrideHardwareAcceleration.Value;
|
UseHardwareAcceleration = CommandLineState.OverrideHardwareAcceleration.Value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,11 @@
|
|||||||
Header="{ext:Locale GameListContextMenuCreateShortcut}"
|
Header="{ext:Locale GameListContextMenuCreateShortcut}"
|
||||||
Icon="{ext:Icon fa-solid fa-bookmark}"
|
Icon="{ext:Icon fa-solid fa-bookmark}"
|
||||||
ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" />
|
ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" />
|
||||||
|
<MenuItem
|
||||||
|
Click="EditGameConfiguration_Click"
|
||||||
|
Header="{ext:Locale GameListContextMenuEditGameConfiguration}"
|
||||||
|
Icon="{ext:Icon fa-solid fa-gear}"
|
||||||
|
ToolTip.Tip="{ext:Locale EditGameConfigurationToolTip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
IsVisible="{Binding HasCompatibilityEntry}"
|
IsVisible="{Binding HasCompatibilityEntry}"
|
||||||
Click="OpenApplicationCompatibility_Click"
|
Click="OpenApplicationCompatibility_Click"
|
||||||
|
|||||||
@@ -386,13 +386,26 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
viewModel.SelectedApplication.Icon
|
viewModel.SelectedApplication.Icon
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async void EditGameConfiguration_Click(object sender, RoutedEventArgs args)
|
||||||
|
{
|
||||||
|
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||||
|
{
|
||||||
|
await new UserConfigWindows(viewModel).ShowDialog((Window)viewModel.TopLevel);
|
||||||
|
|
||||||
|
//just checking for file presence
|
||||||
|
viewModel.SelectedApplication.UserConfig = File.Exists(Program.GetDirGameUserConfig(viewModel.SelectedApplication.IdString,false,false));
|
||||||
|
|
||||||
|
viewModel.RefreshView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async void OpenApplicationCompatibility_Click(object sender, RoutedEventArgs args)
|
public async void OpenApplicationCompatibility_Click(object sender, RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||||
await CompatibilityList.Show(viewModel.SelectedApplication.IdString);
|
await CompatibilityList.Show(viewModel.SelectedApplication.IdString);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OpenApplicationData_Click(object sender, RoutedEventArgs args)
|
public async void OpenApplicationData_Click(object sender, RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||||
xmlns:ext="using:Ryujinx.Ava.Common.Markup"
|
xmlns:ext="using:Ryujinx.Ava.Common.Markup"
|
||||||
xmlns:viewModels="using:Ryujinx.Ava.UI.ViewModels"
|
xmlns:viewModels="using:Ryujinx.Ava.UI.ViewModels"
|
||||||
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Ryujinx.Ava.UI.Controls.ApplicationDataView"
|
x:Class="Ryujinx.Ava.UI.Controls.ApplicationDataView"
|
||||||
@@ -85,6 +86,49 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Margin="0, 10, 0, 10" Height="1" BorderBrush="Gray" Background="Gray" />
|
<Separator Margin="0, 10, 0, 10" Height="1" BorderBrush="Gray" Background="Gray" />
|
||||||
|
<StackPanel Orientation="Vertical" Spacing="5">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||||
|
<ui:SymbolIcon Foreground="ForestGreen" Symbol="Checkmark" IsVisible="{Binding AppData.HasRichPresenceAsset}"/>
|
||||||
|
<TextBlock
|
||||||
|
Foreground="ForestGreen"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
IsVisible="{Binding AppData.HasRichPresenceAsset}"
|
||||||
|
Text="{ext:Locale GameInfoRpcImage}"
|
||||||
|
TextAlignment="Start"
|
||||||
|
TextWrapping="Wrap" >
|
||||||
|
</TextBlock>
|
||||||
|
<ui:SymbolIcon Foreground="Red" Symbol="Cancel" IsVisible="{Binding !AppData.HasRichPresenceAsset}"/>
|
||||||
|
<TextBlock
|
||||||
|
Foreground="Red"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
IsVisible="{Binding !AppData.HasRichPresenceAsset}"
|
||||||
|
Text="{ext:Locale GameInfoRpcImage}"
|
||||||
|
TextAlignment="Start"
|
||||||
|
TextWrapping="Wrap" >
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||||
|
<ui:SymbolIcon Foreground="ForestGreen" Symbol="Checkmark" IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"/>
|
||||||
|
<TextBlock
|
||||||
|
Foreground="ForestGreen"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"
|
||||||
|
Text="{ext:Locale GameInfoRpcDynamic}"
|
||||||
|
TextAlignment="Start"
|
||||||
|
TextWrapping="Wrap" >
|
||||||
|
</TextBlock>
|
||||||
|
<ui:SymbolIcon Foreground="Red" Symbol="Cancel" IsVisible="{Binding !AppData.HasDynamicRichPresenceSupport}"/>
|
||||||
|
<TextBlock
|
||||||
|
Foreground="Red"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
IsVisible="{Binding !AppData.HasDynamicRichPresenceSupport}"
|
||||||
|
Text="{ext:Locale GameInfoRpcDynamic}"
|
||||||
|
TextAlignment="Start"
|
||||||
|
TextWrapping="Wrap" >
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
<Separator Margin="0, 10, 0, 10" Height="1" BorderBrush="Gray" Background="Gray" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
IsVisible="{Binding AppData.HasLdnGames}"
|
IsVisible="{Binding AppData.HasLdnGames}"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
Focusable="True"
|
Focusable="True"
|
||||||
@@ -73,12 +74,18 @@
|
|||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
IsVisible="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).ShowNames}">
|
IsVisible="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).ShowNames}">
|
||||||
<TextBlock
|
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
HorizontalAlignment="Center"
|
<TextBlock
|
||||||
VerticalAlignment="Center"
|
Text="{Binding Name}"
|
||||||
Text="{Binding Name}"
|
TextAlignment="Center"
|
||||||
TextAlignment="Center"
|
TextWrapping="Wrap" />
|
||||||
TextWrapping="Wrap" />
|
<TextBlock
|
||||||
|
IsVisible="{Binding UserConfig}"
|
||||||
|
Text="{ext:Locale UserConfigurationHeader}"
|
||||||
|
TextAlignment="Center"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Foreground="{DynamicResource Warning}" />
|
||||||
|
</StackPanel>
|
||||||
</Panel>
|
</Panel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
@@ -86,10 +93,28 @@
|
|||||||
Margin="5,5,0,0"
|
Margin="5,5,0,0"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
FontSize="16"
|
FontSize="18"
|
||||||
Foreground="{DynamicResource FavoriteApplicationIconColor}"
|
Foreground="{DynamicResource FavoriteApplicationIconColor}"
|
||||||
IsVisible="{Binding Favorite}"
|
IsVisible="{Binding Favorite}"
|
||||||
Symbol="StarFilled" />
|
Symbol="StarFilled" />
|
||||||
|
<Grid IsVisible="{Binding !$parent[UserControl].((viewModels:MainWindowViewModel)DataContext).ShowNames}">
|
||||||
|
<Border
|
||||||
|
Margin="15,35,5,15"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Width="90"
|
||||||
|
Height="20"
|
||||||
|
CornerRadius="4"
|
||||||
|
IsVisible="{Binding UserConfig}"
|
||||||
|
Background="{DynamicResource ThemeContentBackgroundColor}">
|
||||||
|
<TextBlock
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{ext:Locale UserConfigurationHeader}"
|
||||||
|
TextAlignment="Center"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
Focusable="True"
|
Focusable="True"
|
||||||
@@ -145,6 +146,13 @@
|
|||||||
Text="{Binding Converter={x:Static helpers:MultiplayerInfoConverter.Instance}}"
|
Text="{Binding Converter={x:Static helpers:MultiplayerInfoConverter.Instance}}"
|
||||||
TextAlignment="Start"
|
TextAlignment="Start"
|
||||||
TextWrapping="Wrap"/>
|
TextWrapping="Wrap"/>
|
||||||
|
<TextBlock
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
IsVisible="{Binding UserConfig}"
|
||||||
|
Text="{ext:Locale UserConfigurationHeader}"
|
||||||
|
TextAlignment="Start"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Foreground="{DynamicResource Warning}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Grid.Column="4"
|
Grid.Column="4"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
LocaleKeys.CompatibilityListNothing or
|
LocaleKeys.CompatibilityListNothing or
|
||||||
LocaleKeys.CompatibilityListBoots or
|
LocaleKeys.CompatibilityListBoots or
|
||||||
LocaleKeys.CompatibilityListMenus => Brushes.Red,
|
LocaleKeys.CompatibilityListMenus => Brushes.Red,
|
||||||
LocaleKeys.CompatibilityListIngame => Brushes.Yellow,
|
LocaleKeys.CompatibilityListIngame => Brushes.DarkOrange,
|
||||||
_ => Brushes.ForestGreen
|
_ => Brushes.ForestGreen
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1076,7 +1076,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
_rendererWaitEvent.WaitOne();
|
_rendererWaitEvent.WaitOne();
|
||||||
|
|
||||||
AppHost?.Start();
|
AppHost?.Start();
|
||||||
|
|
||||||
AppHost?.DisposeContext();
|
AppHost?.DisposeContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1541,6 +1541,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
await PerformanceCheck();
|
await PerformanceCheck();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// If a configuration is found in the "/games/xxxxxxxxxxxxxx" folder, the program will load the user setting.
|
||||||
|
if (ConfigurationFileFormat.TryLoad(Program.GetDirGameUserConfig(application.IdBaseString), out ConfigurationFileFormat configurationFileFormat))
|
||||||
|
{
|
||||||
|
// Loads the user configuration, having previously changed the global configuration to the user configuration
|
||||||
|
ConfigurationState.Instance.Load(configurationFileFormat, Program.GetDirGameUserConfig(application.IdBaseString, true, true), application.IdBaseString);
|
||||||
|
}
|
||||||
|
|
||||||
Logger.RestartTime();
|
Logger.RestartTime();
|
||||||
|
|
||||||
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path, ConfigurationState.Instance.System.Language, application.Id);
|
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path, ConfigurationState.Instance.System.Language, application.Id);
|
||||||
@@ -1585,6 +1592,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
|
Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
|
||||||
gameThread.Start();
|
gameThread.Start();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SwitchToRenderer(bool startFullscreen) =>
|
public void SwitchToRenderer(bool startFullscreen) =>
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using Avalonia.Collections;
|
using Avalonia.Collections;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Humanizer;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using Ryujinx.Audio.Backends.OpenAL;
|
using Ryujinx.Audio.Backends.OpenAL;
|
||||||
using Ryujinx.Audio.Backends.SDL2;
|
using Ryujinx.Audio.Backends.SDL2;
|
||||||
@@ -26,6 +28,7 @@ using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -68,6 +71,46 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public SettingsHacksViewModel DirtyHacks { get; }
|
public SettingsHacksViewModel DirtyHacks { get; }
|
||||||
|
|
||||||
|
private Bitmap _gameIcon;
|
||||||
|
private string _gameTitle;
|
||||||
|
private string _gameId;
|
||||||
|
public Bitmap GameIcon
|
||||||
|
{
|
||||||
|
get => _gameIcon;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_gameIcon != value)
|
||||||
|
{
|
||||||
|
_gameIcon = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GameTitle
|
||||||
|
{
|
||||||
|
get => _gameTitle;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_gameTitle != value)
|
||||||
|
{
|
||||||
|
_gameTitle = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GameId
|
||||||
|
{
|
||||||
|
get => _gameId;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_gameId != value)
|
||||||
|
{
|
||||||
|
_gameId = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public int ResolutionScale
|
public int ResolutionScale
|
||||||
{
|
{
|
||||||
get => _resolutionScale;
|
get => _resolutionScale;
|
||||||
@@ -329,7 +372,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public bool IsInvalidLdnPassphraseVisible { get; set; }
|
public bool IsInvalidLdnPassphraseVisible { get; set; }
|
||||||
|
|
||||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
|
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this(false)
|
||||||
{
|
{
|
||||||
_virtualFileSystem = virtualFileSystem;
|
_virtualFileSystem = virtualFileSystem;
|
||||||
_contentManager = contentManager;
|
_contentManager = contentManager;
|
||||||
@@ -342,7 +385,48 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SettingsViewModel()
|
public SettingsViewModel(VirtualFileSystem virtualFileSystem,
|
||||||
|
ContentManager contentManager,
|
||||||
|
string gamePath,
|
||||||
|
string gameName,
|
||||||
|
string gameId,
|
||||||
|
byte[] gameIconData,
|
||||||
|
bool enableToLoadCustomConfig) : this(enableToLoadCustomConfig)
|
||||||
|
{
|
||||||
|
_virtualFileSystem = virtualFileSystem;
|
||||||
|
_contentManager = contentManager;
|
||||||
|
|
||||||
|
if (gameIconData != null && gameIconData.Length > 0)
|
||||||
|
{
|
||||||
|
using (var ms = new MemoryStream(gameIconData))
|
||||||
|
{
|
||||||
|
GameIcon = new Bitmap(ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GameTitle = gameName;
|
||||||
|
GameId = gameId;
|
||||||
|
|
||||||
|
if (enableToLoadCustomConfig) // During the game. If there is no user config, then load the global config window
|
||||||
|
{
|
||||||
|
string gameDir = Program.GetDirGameUserConfig(gameId, false, true);
|
||||||
|
if (ConfigurationFileFormat.TryLoad(gameDir, out ConfigurationFileFormat configurationFileFormat))
|
||||||
|
{
|
||||||
|
ConfigurationState.Instance.Load(configurationFileFormat, gameDir, gameId);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadCurrentConfiguration(); // Needed to load custom configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Program.PreviewerDetached)
|
||||||
|
{
|
||||||
|
Task.Run(LoadTimeZones);
|
||||||
|
|
||||||
|
DirtyHacks = new SettingsHacksViewModel(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SettingsViewModel(bool noLoadGlobalConfig = false)
|
||||||
{
|
{
|
||||||
GameDirectories = [];
|
GameDirectories = [];
|
||||||
AutoloadDirectories = [];
|
AutoloadDirectories = [];
|
||||||
@@ -357,7 +441,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
if (Program.PreviewerDetached)
|
if (Program.PreviewerDetached)
|
||||||
{
|
{
|
||||||
Task.Run(LoadAvailableGpus);
|
Task.Run(LoadAvailableGpus);
|
||||||
LoadCurrentConfiguration();
|
|
||||||
|
if (!noLoadGlobalConfig)// Default is false, but loading custom config avoids double call
|
||||||
|
{
|
||||||
|
LoadCurrentConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
DirtyHacks = new SettingsHacksViewModel(this);
|
DirtyHacks = new SettingsHacksViewModel(this);
|
||||||
}
|
}
|
||||||
@@ -468,27 +556,32 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
ConfigurationState config = ConfigurationState.Instance;
|
ConfigurationState config = ConfigurationState.Instance;
|
||||||
|
|
||||||
// User Interface
|
//It is necessary that the data is used from the global configuration file
|
||||||
EnableDiscordIntegration = config.EnableDiscordIntegration;
|
if (string.IsNullOrEmpty(GameId))
|
||||||
CheckUpdatesOnStart = config.CheckUpdatesOnStart;
|
|
||||||
ShowConfirmExit = config.ShowConfirmExit;
|
|
||||||
RememberWindowState = config.RememberWindowState;
|
|
||||||
ShowTitleBar = config.ShowTitleBar;
|
|
||||||
HideCursor = (int)config.HideCursor.Value;
|
|
||||||
|
|
||||||
GameDirectories.Clear();
|
|
||||||
GameDirectories.AddRange(config.UI.GameDirs.Value);
|
|
||||||
|
|
||||||
AutoloadDirectories.Clear();
|
|
||||||
AutoloadDirectories.AddRange(config.UI.AutoloadDirs.Value);
|
|
||||||
|
|
||||||
BaseStyleIndex = config.UI.BaseStyle.Value switch
|
|
||||||
{
|
{
|
||||||
"Auto" => 0,
|
// User Interface
|
||||||
"Light" => 1,
|
EnableDiscordIntegration = config.EnableDiscordIntegration;
|
||||||
"Dark" => 2,
|
CheckUpdatesOnStart = config.CheckUpdatesOnStart;
|
||||||
_ => 0
|
ShowConfirmExit = config.ShowConfirmExit;
|
||||||
};
|
RememberWindowState = config.RememberWindowState;
|
||||||
|
ShowTitleBar = config.ShowTitleBar;
|
||||||
|
HideCursor = (int)config.HideCursor.Value;
|
||||||
|
|
||||||
|
GameDirectories.Clear();
|
||||||
|
GameDirectories.AddRange(config.UI.GameDirs.Value);
|
||||||
|
|
||||||
|
AutoloadDirectories.Clear();
|
||||||
|
AutoloadDirectories.AddRange(config.UI.AutoloadDirs.Value);
|
||||||
|
|
||||||
|
BaseStyleIndex = config.UI.BaseStyle.Value switch
|
||||||
|
{
|
||||||
|
"Auto" => 0,
|
||||||
|
"Light" => 1,
|
||||||
|
"Dark" => 2,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
EnableDockedMode = config.System.EnableDockedMode;
|
EnableDockedMode = config.System.EnableDockedMode;
|
||||||
@@ -508,7 +601,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
DateTime currentDateTime = currentHostDateTime.Add(systemDateTimeOffset);
|
DateTime currentDateTime = currentHostDateTime.Add(systemDateTimeOffset);
|
||||||
CurrentDate = currentDateTime.Date;
|
CurrentDate = currentDateTime.Date;
|
||||||
CurrentTime = currentDateTime.TimeOfDay;
|
CurrentTime = currentDateTime.TimeOfDay;
|
||||||
|
|
||||||
MatchSystemTime = config.System.MatchSystemTime;
|
MatchSystemTime = config.System.MatchSystemTime;
|
||||||
|
|
||||||
EnableCustomVSyncInterval = config.Graphics.EnableCustomVSyncInterval;
|
EnableCustomVSyncInterval = config.Graphics.EnableCustomVSyncInterval;
|
||||||
@@ -568,37 +660,41 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
LdnPassphrase = config.Multiplayer.LdnPassphrase;
|
LdnPassphrase = config.Multiplayer.LdnPassphrase;
|
||||||
LdnServer = config.Multiplayer.LdnServer;
|
LdnServer = config.Multiplayer.LdnServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveSettings()
|
public void SaveSettings()
|
||||||
{
|
{
|
||||||
ConfigurationState config = ConfigurationState.Instance;
|
ConfigurationState config = ConfigurationState.Instance;
|
||||||
|
bool userConfigFile = string.IsNullOrEmpty(GameId);
|
||||||
|
|
||||||
// User Interface
|
if (userConfigFile)
|
||||||
config.EnableDiscordIntegration.Value = EnableDiscordIntegration;
|
|
||||||
config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart;
|
|
||||||
config.ShowConfirmExit.Value = ShowConfirmExit;
|
|
||||||
config.RememberWindowState.Value = RememberWindowState;
|
|
||||||
config.ShowTitleBar.Value = ShowTitleBar;
|
|
||||||
config.HideCursor.Value = (HideCursorMode)HideCursor;
|
|
||||||
|
|
||||||
if (GameDirectoryChanged)
|
|
||||||
{
|
{
|
||||||
config.UI.GameDirs.Value = [..GameDirectories];
|
// User Interface
|
||||||
|
config.EnableDiscordIntegration.Value = EnableDiscordIntegration;
|
||||||
|
config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart;
|
||||||
|
config.ShowConfirmExit.Value = ShowConfirmExit;
|
||||||
|
config.RememberWindowState.Value = RememberWindowState;
|
||||||
|
config.ShowTitleBar.Value = ShowTitleBar;
|
||||||
|
config.HideCursor.Value = (HideCursorMode)HideCursor;
|
||||||
|
|
||||||
|
if (GameDirectoryChanged)
|
||||||
|
{
|
||||||
|
config.UI.GameDirs.Value = [.. GameDirectories];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AutoloadDirectoryChanged)
|
||||||
|
{
|
||||||
|
config.UI.AutoloadDirs.Value = [.. AutoloadDirectories];
|
||||||
|
}
|
||||||
|
|
||||||
|
config.UI.BaseStyle.Value = BaseStyleIndex switch
|
||||||
|
{
|
||||||
|
0 => "Auto",
|
||||||
|
1 => "Light",
|
||||||
|
2 => "Dark",
|
||||||
|
_ => "Auto"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AutoloadDirectoryChanged)
|
|
||||||
{
|
|
||||||
config.UI.AutoloadDirs.Value = [..AutoloadDirectories];
|
|
||||||
}
|
|
||||||
|
|
||||||
config.UI.BaseStyle.Value = BaseStyleIndex switch
|
|
||||||
{
|
|
||||||
0 => "Auto",
|
|
||||||
1 => "Light",
|
|
||||||
2 => "Dark",
|
|
||||||
_ => "Auto"
|
|
||||||
};
|
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
config.System.EnableDockedMode.Value = EnableDockedMode;
|
config.System.EnableDockedMode.Value = EnableDockedMode;
|
||||||
config.Hid.EnableKeyboard.Value = EnableKeyboard;
|
config.Hid.EnableKeyboard.Value = EnableKeyboard;
|
||||||
@@ -693,7 +789,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
config.Hacks.EnableShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelayEnabled;
|
config.Hacks.EnableShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelayEnabled;
|
||||||
config.Hacks.ShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelay;
|
config.Hacks.ShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelay;
|
||||||
|
|
||||||
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
|
||||||
|
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
|
|
||||||
|
|
||||||
MainWindow.UpdateGraphicsConfig();
|
MainWindow.UpdateGraphicsConfig();
|
||||||
RyujinxApp.MainWindow.ViewModel.VSyncModeSettingChanged();
|
RyujinxApp.MainWindow.ViewModel.VSyncModeSettingChanged();
|
||||||
@@ -706,7 +804,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
private static void RevertIfNotSaved()
|
private static void RevertIfNotSaved()
|
||||||
{
|
{
|
||||||
Program.ReloadConfig();
|
// maybe this is an unnecessary check(all options need to be tested)
|
||||||
|
if (string.IsNullOrEmpty(Program.GlobalConfigurationPath))
|
||||||
|
{
|
||||||
|
Program.ReloadConfig();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyButton()
|
public void ApplyButton()
|
||||||
@@ -714,6 +816,26 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
SaveSettings();
|
SaveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void DeleteConfigGame()
|
||||||
|
{
|
||||||
|
string gameDir = Program.GetDirGameUserConfig(GameId,false,false);
|
||||||
|
|
||||||
|
if (File.Exists(gameDir))
|
||||||
|
{
|
||||||
|
File.Delete(gameDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
RevertIfNotSaved();
|
||||||
|
CloseWindow?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveUserConfig()
|
||||||
|
{
|
||||||
|
SaveSettings();
|
||||||
|
RevertIfNotSaved(); // Revert global configuration after saving user configuration
|
||||||
|
CloseWindow?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
public void OkButton()
|
public void OkButton()
|
||||||
{
|
{
|
||||||
SaveSettings();
|
SaveSettings();
|
||||||
|
|||||||
@@ -134,9 +134,26 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager);
|
Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager);
|
||||||
|
|
||||||
Rainbow.Enable();
|
Rainbow.Enable();
|
||||||
|
|
||||||
await Window.SettingsWindow.ShowDialog(Window);
|
if (ViewModel.SelectedApplication is null) // Checks if game data exists
|
||||||
|
{
|
||||||
|
await Window.SettingsWindow.ShowDialog(Window);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool userConfigExist = Program.FindGameConfig(Program.GetDirGameUserConfig(ViewModel.SelectedApplication.IdString, false, false));
|
||||||
|
|
||||||
|
if (!ViewModel.IsGameRunning || !userConfigExist)
|
||||||
|
{
|
||||||
|
await Window.SettingsWindow.ShowDialog(Window); // The game is not running, or if the user configuration does not exist
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If there is a custom configuration in the folder
|
||||||
|
await new UserConfigWindows(ViewModel, userConfigExist).ShowDialog((Window)ViewModel.TopLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rainbow.Disable();
|
Rainbow.Disable();
|
||||||
Rainbow.Reset();
|
Rainbow.Reset();
|
||||||
|
|
||||||
|
|||||||
144
src/Ryujinx/UI/Windows/UserConfigWindows.axaml
Normal file
144
src/Ryujinx/UI/Windows/UserConfigWindows.axaml
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<window:StyleableAppWindow
|
||||||
|
x:Class="Ryujinx.Ava.UI.Windows.UserConfigWindows"
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
|
||||||
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
|
xmlns:settings="clr-namespace:Ryujinx.Ava.UI.Views.Settings"
|
||||||
|
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||||
|
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
|
||||||
|
Width="1100"
|
||||||
|
Height="768"
|
||||||
|
MinWidth="800"
|
||||||
|
MinHeight="480"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
x:DataType="viewModels:SettingsViewModel"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Focusable="True">
|
||||||
|
<Design.DataContext>
|
||||||
|
<viewModels:SettingsViewModel />
|
||||||
|
</Design.DataContext>
|
||||||
|
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinWidth="600" RowDefinitions="Auto,*,Auto">
|
||||||
|
<ContentPresenter
|
||||||
|
x:Name="ContentPresenter"
|
||||||
|
Grid.Row="1"
|
||||||
|
IsVisible="False"
|
||||||
|
KeyboardNavigation.IsTabStop="False"/>
|
||||||
|
<Grid Name="Pages" IsVisible="False" Grid.Row="2">
|
||||||
|
<settings:SettingsInputView Name="InputPage" />
|
||||||
|
<settings:SettingsSystemView Name="SystemPage" />
|
||||||
|
<settings:SettingsCPUView Name="CpuPage" />
|
||||||
|
<settings:SettingsGraphicsView Name="GraphicsPage" />
|
||||||
|
<settings:SettingsAudioView Name="AudioPage" />
|
||||||
|
<settings:SettingsNetworkView Name="NetworkPage" />
|
||||||
|
<settings:SettingsLoggingView Name="LoggingPage" />
|
||||||
|
<settings:SettingsHacksView Name="HacksPage" />
|
||||||
|
</Grid>
|
||||||
|
<ui:NavigationView
|
||||||
|
Grid.Row="1"
|
||||||
|
IsSettingsVisible="False"
|
||||||
|
Name="NavPanel"
|
||||||
|
IsBackEnabled="False"
|
||||||
|
PaneDisplayMode="Left"
|
||||||
|
Margin="2,10,10,0"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
OpenPaneLength="200"
|
||||||
|
IsPaneToggleButtonVisible="False">
|
||||||
|
|
||||||
|
<!-- For image -->
|
||||||
|
<ui:NavigationView.PaneHeader>
|
||||||
|
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="5"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<TextBlock Text="{Binding GameId}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="0,0,0,10"
|
||||||
|
TextAlignment="Center" Grid.Row="0" />
|
||||||
|
<Image Source="{Binding GameIcon}"
|
||||||
|
Width="160"
|
||||||
|
Height="160"
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="0,0,0,10"
|
||||||
|
HorizontalAlignment="Center" />
|
||||||
|
<TextBlock Text="{Binding GameTitle}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="0,0,0,10"
|
||||||
|
TextAlignment="Center" Grid.Row="2" />
|
||||||
|
<Separator Height="1" Grid.Row="3" Margin="0,0,0,10" HorizontalAlignment="Stretch"/>
|
||||||
|
</Grid>
|
||||||
|
</ui:NavigationView.PaneHeader>
|
||||||
|
|
||||||
|
<ui:NavigationView.MenuItems>
|
||||||
|
<ui:NavigationViewItem
|
||||||
|
Content="{ext:Locale SettingsTabInput}"
|
||||||
|
Tag="InputPage"
|
||||||
|
IconSource="Games" />
|
||||||
|
<ui:NavigationViewItem
|
||||||
|
Content="{ext:Locale SettingsTabSystem}"
|
||||||
|
Tag="SystemPage"
|
||||||
|
IconSource="Settings" />
|
||||||
|
<ui:NavigationViewItem
|
||||||
|
Content="{ext:Locale SettingsTabCpu}"
|
||||||
|
Tag="CpuPage">
|
||||||
|
<ui:NavigationViewItem.IconSource>
|
||||||
|
<ui:FontIconSource
|
||||||
|
FontFamily="avares://Ryujinx/Assets/Fonts#Segoe Fluent Icons"
|
||||||
|
Glyph="{helpers:GlyphValueConverter Chip}" />
|
||||||
|
</ui:NavigationViewItem.IconSource>
|
||||||
|
</ui:NavigationViewItem>
|
||||||
|
<ui:NavigationViewItem
|
||||||
|
Content="{ext:Locale SettingsTabGraphics}"
|
||||||
|
Tag="GraphicsPage"
|
||||||
|
IconSource="Image" />
|
||||||
|
<ui:NavigationViewItem
|
||||||
|
Content="{ext:Locale SettingsTabAudio}"
|
||||||
|
IconSource="Audio"
|
||||||
|
Tag="AudioPage" />
|
||||||
|
<ui:NavigationViewItem
|
||||||
|
Content="{ext:Locale SettingsTabNetwork}"
|
||||||
|
Tag="NetworkPage"
|
||||||
|
IconSource="Globe" />
|
||||||
|
<ui:NavigationViewItem
|
||||||
|
Content="{ext:Locale SettingsTabLogging}"
|
||||||
|
Tag="LoggingPage"
|
||||||
|
IconSource="Document" />
|
||||||
|
<ui:NavigationViewItem
|
||||||
|
IsVisible="{Binding ShowDirtyHacks}"
|
||||||
|
Content="Dirty Hacks"
|
||||||
|
Tag="HacksPage"
|
||||||
|
IconSource="Code" />
|
||||||
|
</ui:NavigationView.MenuItems>
|
||||||
|
</ui:NavigationView>
|
||||||
|
|
||||||
|
<ReversibleStackPanel
|
||||||
|
Grid.Row="2"
|
||||||
|
Margin="10"
|
||||||
|
Spacing="10"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
ReverseOrder="{x:Static helper:RunningPlatform.IsMacOS}">
|
||||||
|
<Button
|
||||||
|
Content="{ext:Locale SettingsButtonSave}"
|
||||||
|
Command="{Binding SaveUserConfig}" />
|
||||||
|
<Button
|
||||||
|
HotKey="Escape"
|
||||||
|
Content="{ext:Locale SettingsButtonClose}"
|
||||||
|
Command="{Binding CancelButton}" />
|
||||||
|
<Button
|
||||||
|
Content="{ext:Locale UserProfilesDelete}"
|
||||||
|
Command="{Binding DeleteConfigGame}"
|
||||||
|
Classes="red"/>
|
||||||
|
</ReversibleStackPanel>
|
||||||
|
</Grid>
|
||||||
|
</window:StyleableAppWindow>
|
||||||
115
src/Ryujinx/UI/Windows/UserConfigWindows.axaml.cs
Normal file
115
src/Ryujinx/UI/Windows/UserConfigWindows.axaml.cs
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Shapes;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Media.Imaging;
|
||||||
|
using FluentAvalonia.Core;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
|
using Projektanker.Icons.Avalonia;
|
||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.Ava.UI.Models;
|
||||||
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
|
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||||
|
using Ryujinx.Ava.Utilities;
|
||||||
|
using Ryujinx.Ava.Utilities.Configuration;
|
||||||
|
using Ryujinx.Common.Configuration;
|
||||||
|
using Ryujinx.HLE.FileSystem;
|
||||||
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
|
using Ryujinx.Input;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Key = Avalonia.Input.Key;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.UI.Windows
|
||||||
|
{
|
||||||
|
public partial class UserConfigWindows : StyleableAppWindow
|
||||||
|
{
|
||||||
|
internal readonly SettingsViewModel ViewModel;
|
||||||
|
|
||||||
|
public UserConfigWindows(MainWindowViewModel viewModel, bool findUserConfigDir = true)
|
||||||
|
{
|
||||||
|
Title = string.Format(LocaleManager.Instance[LocaleKeys.SettingsWithInfo], viewModel.SelectedApplication.Name, viewModel.SelectedApplication.IdString);
|
||||||
|
|
||||||
|
DataContext = ViewModel = new SettingsViewModel(
|
||||||
|
viewModel.VirtualFileSystem,
|
||||||
|
viewModel.ContentManager,
|
||||||
|
viewModel.SelectedApplication.Path,
|
||||||
|
viewModel.SelectedApplication.Name,
|
||||||
|
viewModel.SelectedApplication.IdString,
|
||||||
|
viewModel.SelectedApplication.Icon,
|
||||||
|
findUserConfigDir);
|
||||||
|
|
||||||
|
ViewModel.CloseWindow += Close;
|
||||||
|
ViewModel.SaveSettingsEvent += SaveSettings;
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
Load();
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
this.AttachDevTools(new KeyGesture(Key.F12, KeyModifiers.Alt));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveSettings()
|
||||||
|
{
|
||||||
|
InputPage.InputView?.SaveCurrentProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void Load()
|
||||||
|
{
|
||||||
|
Pages.Children.Clear();
|
||||||
|
NavPanel.SelectionChanged += NavPanelOnSelectionChanged;
|
||||||
|
NavPanel.SelectedItem = NavPanel.MenuItems.ElementAt(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NavPanelOnSelectionChanged(object sender, NavigationViewSelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (e.SelectedItem is NavigationViewItem navItem && navItem.Tag is not null)
|
||||||
|
{
|
||||||
|
switch (navItem.Tag.ToString())
|
||||||
|
{
|
||||||
|
case nameof(InputPage):
|
||||||
|
NavPanel.Content = InputPage;
|
||||||
|
break;
|
||||||
|
case nameof(SystemPage):
|
||||||
|
SystemPage.ViewModel = ViewModel;
|
||||||
|
NavPanel.Content = SystemPage;
|
||||||
|
break;
|
||||||
|
case nameof(CpuPage):
|
||||||
|
NavPanel.Content = CpuPage;
|
||||||
|
break;
|
||||||
|
case nameof(GraphicsPage):
|
||||||
|
NavPanel.Content = GraphicsPage;
|
||||||
|
break;
|
||||||
|
case nameof(AudioPage):
|
||||||
|
NavPanel.Content = AudioPage;
|
||||||
|
break;
|
||||||
|
case nameof(NetworkPage):
|
||||||
|
NetworkPage.ViewModel = ViewModel;
|
||||||
|
NavPanel.Content = NetworkPage;
|
||||||
|
break;
|
||||||
|
case nameof(LoggingPage):
|
||||||
|
NavPanel.Content = LoggingPage;
|
||||||
|
break;
|
||||||
|
case nameof(HacksPage):
|
||||||
|
HacksPage.DataContext = ViewModel;
|
||||||
|
NavPanel.Content = HacksPage;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnClosing(WindowClosingEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnClosing(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
public class ApplicationData
|
public class ApplicationData
|
||||||
{
|
{
|
||||||
public bool Favorite { get; set; }
|
public bool Favorite { get; set; }
|
||||||
|
public bool UserConfig { get; set; }
|
||||||
public byte[] Icon { get; set; }
|
public byte[] Icon { get; set; }
|
||||||
public string Name { get; set; } = "Unknown";
|
public string Name { get; set; } = "Unknown";
|
||||||
|
|
||||||
@@ -63,6 +64,9 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
public int GameCount { get; set; }
|
public int GameCount { get; set; }
|
||||||
|
|
||||||
public bool HasLdnGames => PlayerCount != 0 && GameCount != 0;
|
public bool HasLdnGames => PlayerCount != 0 && GameCount != 0;
|
||||||
|
|
||||||
|
public bool HasRichPresenceAsset => DiscordIntegrationModule.HasAssetImage(IdString);
|
||||||
|
public bool HasDynamicRichPresenceSupport => DiscordIntegrationModule.HasAnalyzer(IdString);
|
||||||
|
|
||||||
public TimeSpan TimePlayed { get; set; }
|
public TimeSpan TimePlayed { get; set; }
|
||||||
public DateTime? LastPlayed { get; set; }
|
public DateTime? LastPlayed { get; set; }
|
||||||
|
|||||||
@@ -505,7 +505,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
if (data.Id != 0)
|
if (data.Id != 0)
|
||||||
{
|
{
|
||||||
ApplicationMetadata appMetadata = LoadAndSaveMetaData(data.IdString, appMetadata =>
|
ApplicationMetadata appMetadata = LoadAndSaveMetaData(data.IdString, appMetadata =>
|
||||||
{
|
{
|
||||||
appMetadata.Title = data.Name;
|
appMetadata.Title = data.Name;
|
||||||
|
|
||||||
// Only do the migration if time_played has a value and timespan_played hasn't been updated yet.
|
// Only do the migration if time_played has a value and timespan_played hasn't been updated yet.
|
||||||
@@ -529,10 +529,11 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
data.Favorite = appMetadata.Favorite;
|
data.Favorite = appMetadata.Favorite;
|
||||||
data.TimePlayed = appMetadata.TimePlayed;
|
data.TimePlayed = appMetadata.TimePlayed;
|
||||||
data.LastPlayed = appMetadata.LastPlayed;
|
data.LastPlayed = appMetadata.LastPlayed;
|
||||||
|
data.UserConfig = File.Exists(Program.GetDirGameUserConfig(data.IdBaseString, false, false)); // Just check user config
|
||||||
}
|
}
|
||||||
|
|
||||||
data.FileExtension = Path.GetExtension(applicationPath).TrimStart('.').ToUpper();
|
data.FileExtension = Path.GetExtension(applicationPath).TrimStart('.').ToUpper();
|
||||||
|
|||||||
@@ -6,11 +6,15 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
public static class CommandLineState
|
public static class CommandLineState
|
||||||
{
|
{
|
||||||
public static string[] Arguments { get; private set; }
|
public static string[] Arguments { get; private set; }
|
||||||
|
public static int CountArguments { get; private set; }
|
||||||
public static bool? OverrideDockedMode { get; private set; }
|
public static bool? OverrideDockedMode { get; private set; }
|
||||||
public static bool? OverrideHardwareAcceleration { get; private set; }
|
public static bool? OverrideHardwareAcceleration { get; private set; }
|
||||||
public static string OverrideGraphicsBackend { get; private set; }
|
public static string OverrideGraphicsBackend { get; private set; }
|
||||||
public static string OverrideBackendThreading { get; private set; }
|
public static string OverrideBackendThreading { get; private set; }
|
||||||
|
public static string OverridePPTC { get; private set; }
|
||||||
|
public static string OverrideMemoryManagerMode { get; private set; }
|
||||||
|
public static string OverrideSystemRegion { get; private set; }
|
||||||
|
public static string OverrideSystemLanguage { get; private set; }
|
||||||
public static string OverrideHideCursor { get; private set; }
|
public static string OverrideHideCursor { get; private set; }
|
||||||
public static string BaseDirPathArg { get; private set; }
|
public static string BaseDirPathArg { get; private set; }
|
||||||
public static string Profile { get; private set; }
|
public static string Profile { get; private set; }
|
||||||
@@ -19,6 +23,11 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
public static bool StartFullscreenArg { get; private set; }
|
public static bool StartFullscreenArg { get; private set; }
|
||||||
public static bool HideAvailableUpdates { get; private set; }
|
public static bool HideAvailableUpdates { get; private set; }
|
||||||
|
|
||||||
|
public static void ArgumentsClean()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static void ParseArguments(string[] args)
|
public static void ParseArguments(string[] args)
|
||||||
{
|
{
|
||||||
List<string> arguments = [];
|
List<string> arguments = [];
|
||||||
@@ -28,6 +37,11 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
{
|
{
|
||||||
string arg = args[i];
|
string arg = args[i];
|
||||||
|
|
||||||
|
if (arg.Contains("-") || arg.Contains("--"))
|
||||||
|
{
|
||||||
|
CountArguments++;
|
||||||
|
}
|
||||||
|
|
||||||
switch (arg)
|
switch (arg)
|
||||||
{
|
{
|
||||||
case "-r":
|
case "-r":
|
||||||
@@ -85,6 +99,47 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
|
|
||||||
OverrideBackendThreading = args[++i];
|
OverrideBackendThreading = args[++i];
|
||||||
break;
|
break;
|
||||||
|
case "--pptc":
|
||||||
|
if (i + 1 >= args.Length)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
OverridePPTC = args[++i];
|
||||||
|
break;
|
||||||
|
case "-m":
|
||||||
|
case "--memory-manager-mode":
|
||||||
|
if (i + 1 >= args.Length)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
OverrideMemoryManagerMode = args[++i];
|
||||||
|
break;
|
||||||
|
case "--system-region":
|
||||||
|
if (i + 1 >= args.Length)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
OverrideSystemRegion = args[++i];
|
||||||
|
break;
|
||||||
|
case "--system-language":
|
||||||
|
if (i + 1 >= args.Length)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
OverrideSystemLanguage = args[++i];
|
||||||
|
break;
|
||||||
case "-i":
|
case "-i":
|
||||||
case "--application-id":
|
case "--application-id":
|
||||||
LaunchApplicationId = args[++i];
|
LaunchApplicationId = args[++i];
|
||||||
|
|||||||
@@ -18,9 +18,15 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
|||||||
{
|
{
|
||||||
public partial class ConfigurationState
|
public partial class ConfigurationState
|
||||||
{
|
{
|
||||||
public void Load(ConfigurationFileFormat cff, string configurationFilePath)
|
public void Load(ConfigurationFileFormat cff, string configurationFilePath, string gameId="")
|
||||||
{
|
{
|
||||||
bool configurationFileUpdated = false;
|
bool configurationFileUpdated = false;
|
||||||
|
bool LoadSetting = true;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(gameId))
|
||||||
|
{
|
||||||
|
LoadSetting = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (cff.Version is < 0 or > ConfigurationFileFormat.CurrentVersion)
|
if (cff.Version is < 0 or > ConfigurationFileFormat.CurrentVersion)
|
||||||
{
|
{
|
||||||
@@ -43,14 +49,14 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
|||||||
configurationFileUpdated = true;
|
configurationFileUpdated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
EnableDiscordIntegration.Value = cff.EnableDiscordIntegration;
|
EnableDiscordIntegration.Value = LoadSetting ? cff.EnableDiscordIntegration : EnableDiscordIntegration.Value; // Get from global config only
|
||||||
CheckUpdatesOnStart.Value = cff.CheckUpdatesOnStart;
|
CheckUpdatesOnStart.Value = LoadSetting ? cff.CheckUpdatesOnStart : CheckUpdatesOnStart.Value; // Get from global config only
|
||||||
ShowConfirmExit.Value = cff.ShowConfirmExit;
|
ShowConfirmExit.Value = LoadSetting ? cff.ShowConfirmExit : ShowConfirmExit.Value; // Get from global config only
|
||||||
RememberWindowState.Value = cff.RememberWindowState;
|
RememberWindowState.Value = LoadSetting ? cff.RememberWindowState : RememberWindowState.Value; // Get from global config only
|
||||||
ShowTitleBar.Value = cff.ShowTitleBar;
|
ShowTitleBar.Value = LoadSetting ? cff.ShowTitleBar : ShowTitleBar.Value; // Get from global config only
|
||||||
EnableHardwareAcceleration.Value = cff.EnableHardwareAcceleration;
|
EnableHardwareAcceleration.Value = LoadSetting ? cff.EnableHardwareAcceleration : EnableHardwareAcceleration.Value; // Get from global config only
|
||||||
HideCursor.Value = cff.HideCursor;
|
HideCursor.Value = LoadSetting ? cff.HideCursor : HideCursor.Value; // Get from global config only
|
||||||
|
|
||||||
Logger.EnableFileLog.Value = cff.EnableFileLog;
|
Logger.EnableFileLog.Value = cff.EnableFileLog;
|
||||||
Logger.EnableDebug.Value = cff.LoggingEnableDebug;
|
Logger.EnableDebug.Value = cff.LoggingEnableDebug;
|
||||||
Logger.EnableStub.Value = cff.LoggingEnableStub;
|
Logger.EnableStub.Value = cff.LoggingEnableStub;
|
||||||
@@ -85,7 +91,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
|||||||
System.Language.Value = cff.SystemLanguage;
|
System.Language.Value = cff.SystemLanguage;
|
||||||
System.Region.Value = cff.SystemRegion;
|
System.Region.Value = cff.SystemRegion;
|
||||||
System.TimeZone.Value = cff.SystemTimeZone;
|
System.TimeZone.Value = cff.SystemTimeZone;
|
||||||
System.SystemTimeOffset.Value = cff.SystemTimeOffset;
|
System.SystemTimeOffset.Value = LoadSetting ? cff.SystemTimeOffset : System.SystemTimeOffset.Value; // Get from global config only
|
||||||
System.EnableDockedMode.Value = cff.DockedMode;
|
System.EnableDockedMode.Value = cff.DockedMode;
|
||||||
System.EnablePtc.Value = cff.EnablePtc;
|
System.EnablePtc.Value = cff.EnablePtc;
|
||||||
System.EnableLowPowerPtc.Value = cff.EnableLowPowerPtc;
|
System.EnableLowPowerPtc.Value = cff.EnableLowPowerPtc;
|
||||||
@@ -99,46 +105,47 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
|||||||
System.IgnoreMissingServices.Value = cff.IgnoreMissingServices;
|
System.IgnoreMissingServices.Value = cff.IgnoreMissingServices;
|
||||||
System.IgnoreApplet.Value = cff.IgnoreApplet;
|
System.IgnoreApplet.Value = cff.IgnoreApplet;
|
||||||
System.UseHypervisor.Value = cff.UseHypervisor;
|
System.UseHypervisor.Value = cff.UseHypervisor;
|
||||||
|
|
||||||
UI.GuiColumns.FavColumn.Value = cff.GuiColumns.FavColumn;
|
UI.GuiColumns.FavColumn.Value = LoadSetting ? cff.GuiColumns.FavColumn : UI.GuiColumns.FavColumn.Value;
|
||||||
UI.GuiColumns.IconColumn.Value = cff.GuiColumns.IconColumn;
|
UI.GuiColumns.IconColumn.Value = LoadSetting ? cff.GuiColumns.IconColumn : UI.GuiColumns.IconColumn.Value;
|
||||||
UI.GuiColumns.AppColumn.Value = cff.GuiColumns.AppColumn;
|
UI.GuiColumns.AppColumn.Value = LoadSetting ? cff.GuiColumns.AppColumn : UI.GuiColumns.AppColumn.Value;
|
||||||
UI.GuiColumns.DevColumn.Value = cff.GuiColumns.DevColumn;
|
UI.GuiColumns.DevColumn.Value = LoadSetting ? cff.GuiColumns.DevColumn : UI.GuiColumns.DevColumn.Value;
|
||||||
UI.GuiColumns.VersionColumn.Value = cff.GuiColumns.VersionColumn;
|
UI.GuiColumns.VersionColumn.Value = LoadSetting ? cff.GuiColumns.VersionColumn : UI.GuiColumns.VersionColumn.Value;
|
||||||
UI.GuiColumns.TimePlayedColumn.Value = cff.GuiColumns.TimePlayedColumn;
|
UI.GuiColumns.TimePlayedColumn.Value = LoadSetting ? cff.GuiColumns.TimePlayedColumn : UI.GuiColumns.TimePlayedColumn.Value;
|
||||||
UI.GuiColumns.LastPlayedColumn.Value = cff.GuiColumns.LastPlayedColumn;
|
UI.GuiColumns.LastPlayedColumn.Value = LoadSetting ? cff.GuiColumns.LastPlayedColumn : UI.GuiColumns.LastPlayedColumn.Value;
|
||||||
UI.GuiColumns.FileExtColumn.Value = cff.GuiColumns.FileExtColumn;
|
UI.GuiColumns.FileExtColumn.Value = LoadSetting ? cff.GuiColumns.FileExtColumn : UI.GuiColumns.FileExtColumn.Value;
|
||||||
UI.GuiColumns.FileSizeColumn.Value = cff.GuiColumns.FileSizeColumn;
|
UI.GuiColumns.FileSizeColumn.Value = LoadSetting ? cff.GuiColumns.FileSizeColumn : UI.GuiColumns.FileSizeColumn.Value;
|
||||||
UI.GuiColumns.PathColumn.Value = cff.GuiColumns.PathColumn;
|
UI.GuiColumns.PathColumn.Value = LoadSetting ? cff.GuiColumns.PathColumn : UI.GuiColumns.PathColumn.Value;
|
||||||
UI.ColumnSort.SortColumnId.Value = cff.ColumnSort.SortColumnId;
|
UI.ColumnSort.SortColumnId.Value = LoadSetting ? cff.ColumnSort.SortColumnId : UI.ColumnSort.SortColumnId.Value;
|
||||||
UI.ColumnSort.SortAscending.Value = cff.ColumnSort.SortAscending;
|
UI.ColumnSort.SortAscending.Value = LoadSetting ? cff.ColumnSort.SortAscending : UI.ColumnSort.SortAscending.Value;
|
||||||
UI.GameDirs.Value = cff.GameDirs;
|
UI.GameDirs.Value = LoadSetting ? cff.GameDirs : UI.GameDirs.Value;
|
||||||
UI.AutoloadDirs.Value = cff.AutoloadDirs ?? [];
|
UI.AutoloadDirs.Value = LoadSetting ? (cff.AutoloadDirs ?? []) : UI.AutoloadDirs.Value;
|
||||||
UI.ShownFileTypes.NSP.Value = cff.ShownFileTypes.NSP;
|
UI.ShownFileTypes.NSP.Value = LoadSetting ? cff.ShownFileTypes.NSP : UI.ShownFileTypes.NSP.Value;
|
||||||
UI.ShownFileTypes.PFS0.Value = cff.ShownFileTypes.PFS0;
|
UI.ShownFileTypes.PFS0.Value = LoadSetting ? cff.ShownFileTypes.PFS0 : UI.ShownFileTypes.PFS0.Value;
|
||||||
UI.ShownFileTypes.XCI.Value = cff.ShownFileTypes.XCI;
|
UI.ShownFileTypes.XCI.Value = LoadSetting ? cff.ShownFileTypes.XCI : UI.ShownFileTypes.XCI.Value;
|
||||||
UI.ShownFileTypes.NCA.Value = cff.ShownFileTypes.NCA;
|
UI.ShownFileTypes.NCA.Value = LoadSetting ? cff.ShownFileTypes.NCA : UI.ShownFileTypes.NCA.Value;
|
||||||
UI.ShownFileTypes.NRO.Value = cff.ShownFileTypes.NRO;
|
UI.ShownFileTypes.NRO.Value = LoadSetting ? cff.ShownFileTypes.NRO : UI.ShownFileTypes.NRO.Value;
|
||||||
UI.ShownFileTypes.NSO.Value = cff.ShownFileTypes.NSO;
|
UI.ShownFileTypes.NSO.Value = LoadSetting ? cff.ShownFileTypes.NSO : UI.ShownFileTypes.NSO.Value;
|
||||||
UI.LanguageCode.Value = cff.LanguageCode;
|
UI.LanguageCode.Value = LoadSetting ? cff.LanguageCode : UI.LanguageCode.Value;
|
||||||
UI.BaseStyle.Value = cff.BaseStyle;
|
UI.BaseStyle.Value = LoadSetting ? cff.BaseStyle : UI.BaseStyle.Value;
|
||||||
UI.GameListViewMode.Value = cff.GameListViewMode;
|
UI.GameListViewMode.Value = LoadSetting ? cff.GameListViewMode : UI.GameListViewMode.Value;
|
||||||
UI.ShowNames.Value = cff.ShowNames;
|
UI.ShowNames.Value = LoadSetting ? cff.ShowNames : UI.ShowNames.Value;
|
||||||
UI.IsAscendingOrder.Value = cff.IsAscendingOrder;
|
UI.IsAscendingOrder.Value = LoadSetting ? cff.IsAscendingOrder : UI.IsAscendingOrder.Value;
|
||||||
UI.GridSize.Value = cff.GridSize;
|
UI.GridSize.Value = LoadSetting ? cff.GridSize : UI.GridSize.Value;
|
||||||
UI.ApplicationSort.Value = cff.ApplicationSort;
|
UI.ApplicationSort.Value = LoadSetting ? cff.ApplicationSort : UI.ApplicationSort.Value;
|
||||||
UI.StartFullscreen.Value = cff.StartFullscreen;
|
UI.StartFullscreen.Value = LoadSetting ? cff.StartFullscreen : UI.StartFullscreen.Value;
|
||||||
UI.StartNoUI.Value = cff.StartNoUI;
|
UI.StartNoUI.Value = LoadSetting ? cff.StartNoUI : UI.StartNoUI.Value;
|
||||||
UI.ShowConsole.Value = cff.ShowConsole;
|
UI.ShowConsole.Value = LoadSetting ? cff.ShowConsole : UI.ShowConsole.Value;
|
||||||
UI.WindowStartup.WindowSizeWidth.Value = cff.WindowStartup.WindowSizeWidth;
|
UI.WindowStartup.WindowSizeWidth.Value = LoadSetting ? cff.WindowStartup.WindowSizeWidth : UI.WindowStartup.WindowSizeWidth.Value;
|
||||||
UI.WindowStartup.WindowSizeHeight.Value = cff.WindowStartup.WindowSizeHeight;
|
UI.WindowStartup.WindowSizeHeight.Value = LoadSetting ? cff.WindowStartup.WindowSizeHeight : UI.WindowStartup.WindowSizeHeight.Value;
|
||||||
UI.WindowStartup.WindowPositionX.Value = cff.WindowStartup.WindowPositionX;
|
UI.WindowStartup.WindowPositionX.Value = LoadSetting ? cff.WindowStartup.WindowPositionX : UI.WindowStartup.WindowPositionX.Value;
|
||||||
UI.WindowStartup.WindowPositionY.Value = cff.WindowStartup.WindowPositionY;
|
UI.WindowStartup.WindowPositionY.Value = LoadSetting ? cff.WindowStartup.WindowPositionY : UI.WindowStartup.WindowPositionY.Value;
|
||||||
UI.WindowStartup.WindowMaximized.Value = cff.WindowStartup.WindowMaximized;
|
UI.WindowStartup.WindowMaximized.Value = LoadSetting ? cff.WindowStartup.WindowMaximized : UI.WindowStartup.WindowMaximized.Value;
|
||||||
|
|
||||||
|
|
||||||
Hid.EnableKeyboard.Value = cff.EnableKeyboard;
|
Hid.EnableKeyboard.Value = cff.EnableKeyboard;
|
||||||
Hid.EnableMouse.Value = cff.EnableMouse;
|
Hid.EnableMouse.Value = cff.EnableMouse;
|
||||||
Hid.Hotkeys.Value = cff.Hotkeys;
|
Hid.Hotkeys.Value = LoadSetting ? cff.Hotkeys : Hid.Hotkeys.Value; // Get from global config only
|
||||||
Hid.InputConfig.Value = cff.InputConfig ?? [];
|
Hid.InputConfig.Value = cff.InputConfig ?? [];
|
||||||
Hid.RainbowSpeed.Value = cff.RainbowSpeed;
|
Hid.RainbowSpeed.Value = cff.RainbowSpeed;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using MsgPack;
|
|||||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@@ -15,6 +16,10 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
|||||||
{
|
{
|
||||||
private readonly List<GameSpec> _specs = [];
|
private readonly List<GameSpec> _specs = [];
|
||||||
|
|
||||||
|
public string[] TitleIds => Specs.SelectMany(x => x.TitleIds).ToArray();
|
||||||
|
|
||||||
|
public IReadOnlyList<GameSpec> Specs => new ReadOnlyCollection<GameSpec>(_specs);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add an analysis spec matching a specific game by title ID, with the provided spec configuration.
|
/// Add an analysis spec matching a specific game by title ID, with the provided spec configuration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -80,7 +85,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
|||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="runningGameId">The game currently running.</param>
|
/// <param name="runningGameId">The game currently running.</param>
|
||||||
/// <param name="appMeta">The Application metadata information, including localized game name and play time information.</param>
|
/// <param name="appMeta">The Application metadata information, including localized game name and play time information.</param>
|
||||||
@@ -89,54 +94,21 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
|||||||
public FormattedValue Format(
|
public FormattedValue Format(
|
||||||
string runningGameId,
|
string runningGameId,
|
||||||
ApplicationMetadata appMeta,
|
ApplicationMetadata appMeta,
|
||||||
MessagePackObject playReport
|
Horizon.Prepo.Types.PlayReport playReport
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (!playReport.IsDictionary)
|
if (!playReport.ReportData.IsDictionary)
|
||||||
return FormattedValue.Unhandled;
|
return FormattedValue.Unhandled;
|
||||||
|
|
||||||
if (!_specs.TryGetFirst(s => runningGameId.EqualsAnyIgnoreCase(s.TitleIds), out GameSpec spec))
|
if (!_specs.TryGetFirst(s => runningGameId.EqualsAnyIgnoreCase(s.TitleIds), out GameSpec spec))
|
||||||
return FormattedValue.Unhandled;
|
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;
|
continue;
|
||||||
|
|
||||||
return formatSpec.Formatter(new Value { Application = appMeta, PackedValue = valuePackObject });
|
return value;
|
||||||
}
|
|
||||||
|
|
||||||
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 FormattedValue.Unhandled;
|
return FormattedValue.Unhandled;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
namespace Ryujinx.Ava.Utilities.PlayReport
|
||||||
|
|
||||||
namespace Ryujinx.Ava.Utilities.PlayReport
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The delegate type that powers single value formatters.<br/>
|
/// The delegate type that powers single value formatters.<br/>
|
||||||
@@ -12,7 +10,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
|||||||
/// <br/>
|
/// <br/>
|
||||||
/// OR a signal to reset the value that the caller is using the <see cref="Analyzer"/> for.
|
/// OR a signal to reset the value that the caller is using the <see cref="Analyzer"/> for.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public delegate FormattedValue ValueFormatter(Value value);
|
public delegate FormattedValue SingleValueFormatter(SingleValue value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The delegate type that powers multiple value formatters.<br/>
|
/// The delegate type that powers multiple value formatters.<br/>
|
||||||
@@ -24,7 +22,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
|||||||
/// <br/>
|
/// <br/>
|
||||||
/// OR a signal to reset the value that the caller is using the <see cref="Analyzer"/> for.
|
/// OR a signal to reset the value that the caller is using the <see cref="Analyzer"/> for.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public delegate FormattedValue MultiValueFormatter(Value[] value);
|
public delegate FormattedValue MultiValueFormatter(MultiValue value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The delegate type that powers multiple value formatters.
|
/// The delegate type that powers multiple value formatters.
|
||||||
@@ -38,5 +36,5 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
|||||||
/// <br/>
|
/// <br/>
|
||||||
/// OR a signal to reset the value that the caller is using the <see cref="Analyzer"/> for.
|
/// OR a signal to reset the value that the caller is using the <see cref="Analyzer"/> for.
|
||||||
/// </summary>
|
/// </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)
|
.AddValueFormatter("team_circle", PokemonSVUnionCircle)
|
||||||
);
|
);
|
||||||
|
|
||||||
private static FormattedValue BreathOfTheWild_MasterMode(Value value)
|
private static FormattedValue BreathOfTheWild_MasterMode(SingleValue value)
|
||||||
=> value.BoxedValue is 1 ? "Playing Master Mode" : FormattedValue.ForceReset;
|
=> value.Matched.BoxedValue is 1 ? "Playing Master Mode" : FormattedValue.ForceReset;
|
||||||
|
|
||||||
private static FormattedValue TearsOfTheKingdom_CurrentField(Value value) =>
|
private static FormattedValue TearsOfTheKingdom_CurrentField(SingleValue value) =>
|
||||||
value.DoubleValue switch
|
value.Matched.DoubleValue switch
|
||||||
{
|
{
|
||||||
> 800d => "Exploring the Sky Islands",
|
> 800d => "Exploring the Sky Islands",
|
||||||
< -201d => "Exploring the Depths",
|
< -201d => "Exploring the Depths",
|
||||||
_ => "Roaming Hyrule"
|
_ => "Roaming Hyrule"
|
||||||
};
|
};
|
||||||
|
|
||||||
private static FormattedValue SuperMarioOdyssey_AssistMode(Value value)
|
private static FormattedValue SuperMarioOdyssey_AssistMode(SingleValue value)
|
||||||
=> value.BoxedValue is 1 ? "Playing in Assist Mode" : "Playing in Regular Mode";
|
=> value.Matched.BoxedValue is 1 ? "Playing in Assist Mode" : "Playing in Regular Mode";
|
||||||
|
|
||||||
private static FormattedValue SuperMarioOdysseyChina_AssistMode(Value value)
|
private static FormattedValue SuperMarioOdysseyChina_AssistMode(SingleValue value)
|
||||||
=> value.BoxedValue is 1 ? "Playing in 帮助模式" : "Playing in 普通模式";
|
=> value.Matched.BoxedValue is 1 ? "Playing in 帮助模式" : "Playing in 普通模式";
|
||||||
|
|
||||||
private static FormattedValue SuperMario3DWorldOrBowsersFury(Value value)
|
private static FormattedValue SuperMario3DWorldOrBowsersFury(SingleValue value)
|
||||||
=> value.BoxedValue is 0 ? "Playing Super Mario 3D World" : "Playing Bowser's Fury";
|
=> value.Matched.BoxedValue is 0 ? "Playing Super Mario 3D World" : "Playing Bowser's Fury";
|
||||||
|
|
||||||
private static FormattedValue MarioKart8Deluxe_Mode(Value value)
|
private static FormattedValue MarioKart8Deluxe_Mode(SingleValue value)
|
||||||
=> value.StringValue switch
|
=> value.Matched.StringValue switch
|
||||||
{
|
{
|
||||||
// Single Player
|
// Single Player
|
||||||
"Single" => "Single Player",
|
"Single" => "Single Player",
|
||||||
@@ -87,11 +87,11 @@
|
|||||||
_ => FormattedValue.ForceReset
|
_ => FormattedValue.ForceReset
|
||||||
};
|
};
|
||||||
|
|
||||||
private static FormattedValue PokemonSVUnionCircle(Value value)
|
private static FormattedValue PokemonSVUnionCircle(SingleValue value)
|
||||||
=> value.BoxedValue is 0 ? "Playing Alone" : "Playing in a group";
|
=> value.Matched.BoxedValue is 0 ? "Playing Alone" : "Playing in a group";
|
||||||
|
|
||||||
private static FormattedValue PokemonSVArea(Value value)
|
private static FormattedValue PokemonSVArea(SingleValue value)
|
||||||
=> value.StringValue switch
|
=> value.Matched.StringValue switch
|
||||||
{
|
{
|
||||||
// Base Game Locations
|
// Base Game Locations
|
||||||
"a_w01" => "South Area One",
|
"a_w01" => "South Area One",
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
using FluentAvalonia.Core;
|
using FluentAvalonia.Core;
|
||||||
|
using MsgPack;
|
||||||
|
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@@ -11,10 +14,11 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class GameSpec
|
public class GameSpec
|
||||||
{
|
{
|
||||||
|
private int _lastPriority;
|
||||||
|
|
||||||
public required string[] TitleIds { get; init; }
|
public required string[] TitleIds { get; init; }
|
||||||
public List<FormatterSpec> SimpleValueFormatters { get; } = [];
|
|
||||||
public List<MultiFormatterSpec> MultiValueFormatters { get; } = [];
|
public List<FormatterSpecBase> ValueFormatters { get; } = [];
|
||||||
public List<SparseMultiFormatterSpec> SparseMultiValueFormatters { get; } = [];
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -24,8 +28,8 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
|||||||
/// <param name="reportKey">The key name to match.</param>
|
/// <param name="reportKey">The key name to match.</param>
|
||||||
/// <param name="valueFormatter">The function which can return a potential formatted value.</param>
|
/// <param name="valueFormatter">The function which can return a potential formatted value.</param>
|
||||||
/// <returns>The current <see cref="GameSpec"/>, for chaining convenience.</returns>
|
/// <returns>The current <see cref="GameSpec"/>, for chaining convenience.</returns>
|
||||||
public GameSpec AddValueFormatter(string reportKey, ValueFormatter valueFormatter)
|
public GameSpec AddValueFormatter(string reportKey, SingleValueFormatter valueFormatter)
|
||||||
=> AddValueFormatter(SimpleValueFormatters.Count, reportKey, valueFormatter);
|
=> AddValueFormatter(_lastPriority++, reportKey, valueFormatter);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a value formatter at a specific priority to the current <see cref="GameSpec"/>
|
/// 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>
|
/// <param name="valueFormatter">The function which can return a potential formatted value.</param>
|
||||||
/// <returns>The current <see cref="GameSpec"/>, for chaining convenience.</returns>
|
/// <returns>The current <see cref="GameSpec"/>, for chaining convenience.</returns>
|
||||||
public GameSpec AddValueFormatter(int priority, string reportKey,
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -53,7 +57,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
|||||||
/// <param name="valueFormatter">The function which can format the values.</param>
|
/// <param name="valueFormatter">The function which can format the values.</param>
|
||||||
/// <returns>The current <see cref="GameSpec"/>, for chaining convenience.</returns>
|
/// <returns>The current <see cref="GameSpec"/>, for chaining convenience.</returns>
|
||||||
public GameSpec AddMultiValueFormatter(string[] reportKeys, MultiValueFormatter valueFormatter)
|
public GameSpec AddMultiValueFormatter(string[] reportKeys, MultiValueFormatter valueFormatter)
|
||||||
=> AddMultiValueFormatter(MultiValueFormatters.Count, reportKeys, valueFormatter);
|
=> AddMultiValueFormatter(_lastPriority++, reportKeys, valueFormatter);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a multi-value formatter at a specific priority to the current <see cref="GameSpec"/>
|
/// 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,
|
public GameSpec AddMultiValueFormatter(int priority, string[] reportKeys,
|
||||||
MultiValueFormatter valueFormatter)
|
MultiValueFormatter valueFormatter)
|
||||||
{
|
{
|
||||||
MultiValueFormatters.Add(new MultiFormatterSpec
|
ValueFormatters.Add(new MultiFormatterSpec
|
||||||
{
|
{
|
||||||
Priority = priority, ReportKeys = reportKeys, Formatter = valueFormatter
|
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>
|
/// <param name="valueFormatter">The function which can format the values.</param>
|
||||||
/// <returns>The current <see cref="GameSpec"/>, for chaining convenience.</returns>
|
/// <returns>The current <see cref="GameSpec"/>, for chaining convenience.</returns>
|
||||||
public GameSpec AddSparseMultiValueFormatter(string[] reportKeys, SparseMultiValueFormatter valueFormatter)
|
public GameSpec AddSparseMultiValueFormatter(string[] reportKeys, SparseMultiValueFormatter valueFormatter)
|
||||||
=> AddSparseMultiValueFormatter(SparseMultiValueFormatters.Count, reportKeys, valueFormatter);
|
=> AddSparseMultiValueFormatter(_lastPriority++, reportKeys, valueFormatter);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a multi-value formatter at a specific priority to the current <see cref="GameSpec"/>
|
/// 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,
|
public GameSpec AddSparseMultiValueFormatter(int priority, string[] reportKeys,
|
||||||
SparseMultiValueFormatter valueFormatter)
|
SparseMultiValueFormatter valueFormatter)
|
||||||
{
|
{
|
||||||
SparseMultiValueFormatters.Add(new SparseMultiFormatterSpec
|
ValueFormatters.Add(new SparseMultiFormatterSpec
|
||||||
{
|
{
|
||||||
Priority = priority, ReportKeys = reportKeys, Formatter = valueFormatter
|
Priority = priority, ReportKeys = reportKeys, Formatter = valueFormatter
|
||||||
});
|
});
|
||||||
@@ -111,30 +115,101 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A struct containing the data for a mapping of a key in a Play Report to a formatter for its potential value.
|
/// A struct containing the data for a mapping of a key in a Play Report to a formatter for its potential value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct FormatterSpec
|
public class FormatterSpec : FormatterSpecBase
|
||||||
{
|
{
|
||||||
public required int Priority { get; init; }
|
public override bool GetData(Horizon.Prepo.Types.PlayReport playReport, out object result)
|
||||||
public required string ReportKey { get; init; }
|
{
|
||||||
public ValueFormatter Formatter { get; init; }
|
if (!playReport.ReportData.AsDictionary().TryGetValue(ReportKeys[0], out MessagePackObject valuePackObject))
|
||||||
|
{
|
||||||
|
result = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = valuePackObject;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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.
|
/// 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>
|
/// </summary>
|
||||||
public struct MultiFormatterSpec
|
public class MultiFormatterSpec : FormatterSpecBase
|
||||||
{
|
{
|
||||||
public required int Priority { get; init; }
|
public override bool GetData(Horizon.Prepo.Types.PlayReport playReport, out object result)
|
||||||
public required string[] ReportKeys { get; init; }
|
{
|
||||||
public MultiValueFormatter Formatter { get; init; }
|
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>
|
/// <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.
|
/// 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>
|
/// </summary>
|
||||||
public struct SparseMultiFormatterSpec
|
public class SparseMultiFormatterSpec : FormatterSpecBase
|
||||||
{
|
{
|
||||||
public required int Priority { get; init; }
|
public override bool GetData(Horizon.Prepo.Types.PlayReport playReport, out object result)
|
||||||
public required string[] ReportKeys { get; init; }
|
{
|
||||||
public SparseMultiValueFormatter Formatter { get; init; }
|
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 MsgPack;
|
||||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.Utilities.PlayReport
|
namespace Ryujinx.Ava.Utilities.PlayReport
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The input data to a <see cref="ValueFormatter"/>,
|
/// The base input data to a ValueFormatter delegate,
|
||||||
/// containing the currently running application's <see cref="ApplicationMetadata"/>,
|
|
||||||
/// and the matched <see cref="MessagePackObject"/> from the Play Report.
|
/// and the matched <see cref="MessagePackObject"/> from the Play Report.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Value
|
public readonly struct Value
|
||||||
{
|
{
|
||||||
/// <summary>
|
public Value(MessagePackObject packedValue)
|
||||||
/// The currently running application's <see cref="ApplicationMetadata"/>.
|
{
|
||||||
/// </summary>
|
PackedValue = packedValue;
|
||||||
public ApplicationMetadata Application { get; init; }
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The matched value from the Play Report.
|
/// The matched value from the Play Report.
|
||||||
@@ -37,6 +37,17 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
|||||||
: boxed.ToString();
|
: 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
|
#region AsX accessors
|
||||||
|
|
||||||
public bool BooleanValue => PackedValue.AsBoolean();
|
public bool BooleanValue => PackedValue.AsBoolean();
|
||||||
@@ -57,7 +68,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A potential formatted value returned by a <see cref="ValueFormatter"/>.
|
/// A potential formatted value returned by a ValueFormatter delegate.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly struct FormattedValue
|
public readonly struct FormattedValue
|
||||||
{
|
{
|
||||||
@@ -103,28 +114,47 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Return this to tell the caller there is no value to return.
|
/// Return this to tell the caller there is no value to return.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static FormattedValue Unhandled => default;
|
public static readonly FormattedValue Unhandled = default;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Return this to suggest the caller reset the value it's using the <see cref="Analyzer"/> for.
|
/// Return this to suggest the caller reset the value it's using the <see cref="Analyzer"/> for.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static FormattedValue ForceReset => new() { Handled = true, Reset = true };
|
public static readonly FormattedValue ForceReset = new() { Handled = true, Reset = true };
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public static readonly ValueFormatter SingleAlwaysResets = _ => ForceReset;
|
public static readonly SingleValueFormatter SingleAlwaysResets = _ => ForceReset;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A delegate singleton you can use to always return <see cref="ForceReset"/> in a <see cref="MultiValueFormatter"/>.
|
/// A delegate singleton you can use to always return <see cref="ForceReset"/> in a <see cref="MultiValueFormatter"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly MultiValueFormatter MultiAlwaysResets = _ => ForceReset;
|
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>
|
/// <summary>
|
||||||
/// A delegate factory you can use to always return the specified
|
/// 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>
|
/// </summary>
|
||||||
/// <param name="formattedValue">The string to always return for this delegate instance.</param>
|
/// <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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
public static class ShortcutHelper
|
public static class ShortcutHelper
|
||||||
{
|
{
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
private static void CreateShortcutWindows(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string cleanedAppName, string desktopPath)
|
private static void CreateShortcutWindows(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string cleanedAppName, string desktopPath, string args = "")
|
||||||
{
|
{
|
||||||
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName + ".exe");
|
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName + ".exe");
|
||||||
iconPath += ".ico";
|
iconPath += ".ico";
|
||||||
@@ -22,13 +22,13 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
image.Resize(new SKImageInfo(128, 128), SKFilterQuality.High);
|
image.Resize(new SKImageInfo(128, 128), SKFilterQuality.High);
|
||||||
SaveBitmapAsIcon(image, iconPath);
|
SaveBitmapAsIcon(image, iconPath);
|
||||||
|
|
||||||
Shortcut shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath, applicationId), iconPath, 0);
|
Shortcut shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath, applicationId, args), iconPath, 0);
|
||||||
shortcut.StringData.NameString = cleanedAppName;
|
shortcut.StringData.NameString = cleanedAppName;
|
||||||
shortcut.WriteToFile(Path.Combine(desktopPath, cleanedAppName + ".lnk"));
|
shortcut.WriteToFile(Path.Combine(desktopPath, cleanedAppName + ".lnk"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("linux")]
|
[SupportedOSPlatform("linux")]
|
||||||
private static void CreateShortcutLinux(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string desktopPath, string cleanedAppName)
|
private static void CreateShortcutLinux(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string desktopPath, string cleanedAppName, string args = "")
|
||||||
{
|
{
|
||||||
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.sh");
|
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.sh");
|
||||||
string desktopFile = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-template.desktop");
|
string desktopFile = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-template.desktop");
|
||||||
@@ -40,11 +40,11 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
data.SaveTo(file);
|
data.SaveTo(file);
|
||||||
|
|
||||||
using StreamWriter outputFile = new(Path.Combine(desktopPath, cleanedAppName + ".desktop"));
|
using StreamWriter outputFile = new(Path.Combine(desktopPath, cleanedAppName + ".desktop"));
|
||||||
outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath, applicationId)}");
|
outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath, applicationId, args)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
private static void CreateShortcutMacos(string appFilePath, string applicationId, byte[] iconData, string desktopPath, string cleanedAppName)
|
private static void CreateShortcutMacos(string appFilePath, string applicationId, byte[] iconData, string desktopPath, string cleanedAppName, string args = "")
|
||||||
{
|
{
|
||||||
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx");
|
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx");
|
||||||
string plistFile = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-template.plist");
|
string plistFile = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-template.plist");
|
||||||
@@ -63,7 +63,7 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
string scriptPath = Path.Combine(scriptFolderPath, ScriptName);
|
string scriptPath = Path.Combine(scriptFolderPath, ScriptName);
|
||||||
using StreamWriter scriptFile = new(scriptPath);
|
using StreamWriter scriptFile = new(scriptPath);
|
||||||
|
|
||||||
scriptFile.Write(shortcutScript, basePath, GetArgsString(appFilePath, applicationId));
|
scriptFile.Write(shortcutScript, basePath, GetArgsString(appFilePath, applicationId, args));
|
||||||
|
|
||||||
// Set execute permission
|
// Set execute permission
|
||||||
FileInfo fileInfo = new(scriptPath);
|
FileInfo fileInfo = new(scriptPath);
|
||||||
@@ -87,7 +87,7 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
outputFile.Write(plistFile, ScriptName, cleanedAppName, IconName);
|
outputFile.Write(plistFile, ScriptName, cleanedAppName, IconName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void CreateAppShortcut(string applicationFilePath, string applicationName, string applicationId, byte[] iconData)
|
public static void CreateAppShortcut(string applicationFilePath, string applicationName, string applicationId, byte[] iconData, string args = "")
|
||||||
{
|
{
|
||||||
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
|
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
|
||||||
string cleanedAppName = string.Join("_", applicationName.Split(Path.GetInvalidFileNameChars()));
|
string cleanedAppName = string.Join("_", applicationName.Split(Path.GetInvalidFileNameChars()));
|
||||||
@@ -96,7 +96,7 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
{
|
{
|
||||||
string iconPath = Path.Combine(AppDataManager.BaseDirPath, "games", applicationId, "app");
|
string iconPath = Path.Combine(AppDataManager.BaseDirPath, "games", applicationId, "app");
|
||||||
|
|
||||||
CreateShortcutWindows(applicationFilePath, applicationId, iconData, iconPath, cleanedAppName, desktopPath);
|
CreateShortcutWindows(applicationFilePath, applicationId, iconData, iconPath, cleanedAppName, desktopPath, args);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -106,14 +106,14 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
string iconPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "icons", "Ryujinx");
|
string iconPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "icons", "Ryujinx");
|
||||||
|
|
||||||
Directory.CreateDirectory(iconPath);
|
Directory.CreateDirectory(iconPath);
|
||||||
CreateShortcutLinux(applicationFilePath, applicationId, iconData, Path.Combine(iconPath, applicationId), desktopPath, cleanedAppName);
|
CreateShortcutLinux(applicationFilePath, applicationId, iconData, Path.Combine(iconPath, applicationId), desktopPath, cleanedAppName, args);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS())
|
if (OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
CreateShortcutMacos(applicationFilePath, applicationId, iconData, desktopPath, cleanedAppName);
|
CreateShortcutMacos(applicationFilePath, applicationId, iconData, desktopPath, cleanedAppName, args);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -121,7 +121,7 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
throw new NotImplementedException("Shortcut support has not been implemented yet for this OS.");
|
throw new NotImplementedException("Shortcut support has not been implemented yet for this OS.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetArgsString(string appFilePath, string applicationId)
|
private static string GetArgsString(string appFilePath, string applicationId, string config = "")
|
||||||
{
|
{
|
||||||
// args are first defined as a list, for easier adjustments in the future
|
// args are first defined as a list, for easier adjustments in the future
|
||||||
List<string> argsList = [];
|
List<string> argsList = [];
|
||||||
@@ -132,6 +132,11 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
argsList.Add($"\"{CommandLineState.BaseDirPathArg}\"");
|
argsList.Add($"\"{CommandLineState.BaseDirPathArg}\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(config))
|
||||||
|
{
|
||||||
|
argsList.Add(config);
|
||||||
|
}
|
||||||
|
|
||||||
if (appFilePath.ToLower().EndsWith(".xci"))
|
if (appFilePath.ToLower().EndsWith(".xci"))
|
||||||
{
|
{
|
||||||
argsList.Add("--application-id");
|
argsList.Add("--application-id");
|
||||||
|
|||||||
Reference in New Issue
Block a user