Compare commits
23 Commits
139e86915b
...
Canary-1.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52b0b45d34 | ||
|
|
12f0dbcc70 | ||
|
|
719be560ec | ||
|
|
18238736be | ||
|
|
5d9e4ad7a4 | ||
|
|
adba775f0c | ||
|
|
2ffaeb2803 | ||
|
|
b16b844760 | ||
|
|
bc07bc482d | ||
|
|
61975ca44d | ||
|
|
66054dd225 | ||
|
|
b1f61e5143 | ||
|
|
0d7d0e8092 | ||
|
|
aa2178dbe5 | ||
|
|
f92d09711b | ||
|
|
45ee8cd0e8 | ||
|
|
395bbd144a | ||
|
|
744d813b87 | ||
|
|
7d59ada798 | ||
|
|
a4b5304935 | ||
|
|
0965ee905d | ||
|
|
855161b23b | ||
|
|
6b55d158b7 |
@@ -969,6 +969,7 @@
|
||||
0100751007ADA000,"Don't Starve: Nintendo Switch Edition",nvdec,playable,2022-02-05 20:43:34
|
||||
010088B010DD2000,"Dongo Adventure",,playable,2022-10-04 16:22:26
|
||||
0100C1F0051B6000,"Donkey Kong Country™: Tropical Freeze",,playable,2024-08-05 16:46:10
|
||||
01009D901BC56000,"Donkey Kong Country™: Returns HD",gpu,ingame,2025-02-16 13:44:12
|
||||
0100F2C00F060000,"Doodle Derby",,boots,2020-12-04 22:51:48
|
||||
0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07
|
||||
010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19
|
||||
@@ -1158,7 +1159,7 @@
|
||||
010095600AA36000,"Fill-a-Pix: Phil's Epic Adventure",,playable,2020-12-22 13:48:22
|
||||
0100C3A00BB76000,"Fimbul",nvdec,playable,2022-07-26 13:31:47
|
||||
0100C8200E942000,"Fin and the Ancient Mystery",nvdec,playable,2020-12-17 16:40:39
|
||||
01000EA014150000,"FINAL FANTASY",crash,nothing,2024-09-05 20:55:30
|
||||
01000EA014150000,"FINAL FANTASY",,playable,2025-02-16 21:27:30
|
||||
01006B7014156000,"FINAL FANTASY II",crash,nothing,2024-04-13 19:18:04
|
||||
01006F000B056000,"FINAL FANTASY IX",audout;nvdec,playable,2021-06-05 11:35:00
|
||||
0100AA201415C000,"FINAL FANTASY V",,playable,2023-04-26 01:11:55
|
||||
@@ -2838,6 +2839,7 @@
|
||||
01009B90006DC000,"Super Mario Maker™ 2",online-broken;ldn-broken,playable,2024-08-25 11:05:19
|
||||
0100000000010000,"Super Mario Odyssey™",nvdec;intel-vendor-bug;mac-bug,playable,2024-08-25 01:32:34
|
||||
010036B0034E4000,"Super Mario Party™",gpu;Needs Update;ldn-works,ingame,2024-06-21 05:10:16
|
||||
0100965017338000,"Super Mario Party Jamboree",mac-bug;gpu,ingame,2025-02-17 02:09:20
|
||||
0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42
|
||||
,"Super Mario World",homebrew,boots,2024-06-13 01:40:31
|
||||
010049900F546000,"Super Mario™ 3D All-Stars",services-horizon;slow;vulkan;amd-vendor-bug,ingame,2024-05-07 02:38:16
|
||||
@@ -2988,8 +2990,8 @@
|
||||
010015D003EE4000,"The Jackbox Party Pack 2",online-working,playable,2022-08-22 18:23:40
|
||||
0100CC80013D6000,"The Jackbox Party Pack 3",slow;online-working,playable,2022-08-22 18:41:06
|
||||
0100E1F003EE8000,"The Jackbox Party Pack 4",online-working,playable,2022-08-22 18:56:34
|
||||
01006fe0096ac000,"The Jackbox Party Pack 5",ldn-untested,boots,2025-02-03 22:32:00
|
||||
01005a400db52000,"The Jackbox Party Pack 6",ldn-untested,boots,2025-02-03 22:32:00
|
||||
01006fe0096ac000,"The Jackbox Party Pack 5",slow;online-working,ingame,2025-02-14 05:32:00
|
||||
01005a400db52000,"The Jackbox Party Pack 6",slow;online-working,ingame,2025-02-14 05:26:00
|
||||
010052C00B184000,"The Journey Down: Chapter One",nvdec,playable,2021-02-24 13:32:41
|
||||
01006BC00B188000,"The Journey Down: Chapter Three",nvdec,playable,2021-02-24 13:45:27
|
||||
01009AB00B186000,"The Journey Down: Chapter Two",nvdec,playable,2021-02-24 13:32:13
|
||||
|
||||
|
@@ -186,7 +186,7 @@ namespace ARMeilleure.Translation.Cache
|
||||
|
||||
int newRegionNumber = _activeRegionIndex;
|
||||
|
||||
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
|
||||
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
|
||||
|
||||
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
||||
|
||||
|
||||
@@ -53,10 +53,9 @@ namespace Ryujinx.Common
|
||||
"0100000000010000", // Super Mario Odyssey
|
||||
|
||||
// Further testing is appreciated, I did not test the entire game:
|
||||
"01007300020fa000", // Astral Chain
|
||||
"010076f0049a2000", // Bayonetta
|
||||
"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
|
||||
"0100f4300bf2c000", // New Pokemon Snap
|
||||
//"010076f0049a2000", // Bayonetta
|
||||
//"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
|
||||
//"0100f4300bf2c000", // New Pokemon Snap
|
||||
];
|
||||
|
||||
public static string GetDiscordGameAsset(string titleId)
|
||||
@@ -230,6 +229,7 @@ namespace Ryujinx.Common
|
||||
"01008c8012920000", // Dying Light Platinum Edition
|
||||
"01001cc01b2d4000", // Goat Simulator 3
|
||||
"01003620068ea000", // Hand of Fate 2
|
||||
"0100f7e00c70e000", // Hogwarts Legacy
|
||||
"010085500130a000", // Lego City: Undercover
|
||||
"010073c01af34000", // LEGO Horizon Adventures
|
||||
"0100d71004694000", // Minecraft
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
||||
|
||||
int newRegionNumber = _activeRegionIndex;
|
||||
|
||||
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
|
||||
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
|
||||
|
||||
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
|
||||
public async Task<ushort> NatPunch()
|
||||
{
|
||||
NatDiscoverer discoverer = new();
|
||||
CancellationTokenSource cts = new(5000);
|
||||
CancellationTokenSource cts = new(2500);
|
||||
|
||||
NatDevice device;
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
if (errorCode != LinuxError.SUCCESS)
|
||||
{
|
||||
if (errorCode != LinuxError.EWOULDBLOCK)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Operation failed with error {errorCode}.");
|
||||
}
|
||||
result = -1;
|
||||
}
|
||||
|
||||
@@ -66,6 +70,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
BsdSocketType type = (BsdSocketType)context.RequestData.ReadInt32();
|
||||
ProtocolType protocol = (ProtocolType)context.RequestData.ReadInt32();
|
||||
|
||||
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Creating socket with domain={domain}, type={type}, protocol={protocol}");
|
||||
|
||||
BsdSocketCreationFlags creationFlags = (BsdSocketCreationFlags)((int)type >> (int)BsdSocketCreationFlags.FlagsShift);
|
||||
type &= BsdSocketType.TypeMask;
|
||||
|
||||
@@ -95,12 +101,21 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
}
|
||||
}
|
||||
|
||||
ISocket newBsdSocket = new ManagedSocket(netDomain, (SocketType)type, protocol, context.Device.Configuration.MultiplayerLanInterfaceId)
|
||||
{
|
||||
Blocking = !creationFlags.HasFlag(BsdSocketCreationFlags.NonBlocking),
|
||||
};
|
||||
|
||||
LinuxError errno = LinuxError.SUCCESS;
|
||||
ISocket newBsdSocket;
|
||||
|
||||
try
|
||||
{
|
||||
newBsdSocket = new ManagedSocket(netDomain, (SocketType)type, protocol, context.Device.Configuration.MultiplayerLanInterfaceId)
|
||||
{
|
||||
Blocking = !creationFlags.HasFlag(BsdSocketCreationFlags.NonBlocking),
|
||||
};
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
LinuxError errNo = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
return WriteBsdResult(context, 0, errNo);
|
||||
}
|
||||
|
||||
int newSockFd = _context.RegisterFileDescriptor(newBsdSocket);
|
||||
|
||||
@@ -111,6 +126,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
|
||||
if (exempt)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.ServiceBsd, "Disconnecting exempt socket.");
|
||||
newBsdSocket.Disconnect();
|
||||
}
|
||||
|
||||
@@ -797,7 +813,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
errno = socket.Listen(backlog);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, $"Invalid socket fd '{socketFd}'.");
|
||||
}
|
||||
|
||||
return WriteBsdResult(context, 0, errno);
|
||||
}
|
||||
|
||||
|
||||
@@ -92,18 +92,30 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
{
|
||||
newSocket = new ManagedSocket(Socket.Accept());
|
||||
|
||||
IPEndPoint remoteEndPoint = newSocket.RemoteEndPoint;
|
||||
bool isPrivateIp = remoteEndPoint.Address.ToString().StartsWith("192.168.");
|
||||
Logger.Info?.PrintMsg(LogClass.ServiceBsd,
|
||||
isPrivateIp
|
||||
? $"Accepted connection from {ProtocolType}/{remoteEndPoint.Address}:{remoteEndPoint.Port}"
|
||||
: $"Accepted connection from {ProtocolType}/***:{remoteEndPoint.Port}");
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
newSocket = null;
|
||||
|
||||
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||
}
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
public LinuxError Bind(IPEndPoint localEndPoint)
|
||||
{
|
||||
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket binding to: {ProtocolType}/{localEndPoint.Port}");
|
||||
try
|
||||
{
|
||||
Socket.Bind(localEndPoint);
|
||||
@@ -112,6 +124,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||
}
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
@@ -123,6 +139,15 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
|
||||
public LinuxError Connect(IPEndPoint remoteEndPoint)
|
||||
{
|
||||
bool isLDNPrivateIP = remoteEndPoint.Address.ToString().StartsWith("192.168.");
|
||||
if (isLDNPrivateIP)
|
||||
{
|
||||
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Connecting to: {ProtocolType}/{remoteEndPoint.Address}:{remoteEndPoint.Port}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Connecting to: {ProtocolType}/***:{remoteEndPoint.Port}");
|
||||
}
|
||||
try
|
||||
{
|
||||
Socket.Connect(remoteEndPoint);
|
||||
@@ -137,6 +162,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
}
|
||||
else
|
||||
{
|
||||
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||
}
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
@@ -144,11 +173,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
Logger.Info?.Print(LogClass.ServiceBsd, "Socket disconnecting");
|
||||
Socket.Disconnect(true);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Logger.Info?.Print(LogClass.ServiceBsd, "Socket closed");
|
||||
Socket.Close();
|
||||
Socket.Dispose();
|
||||
}
|
||||
@@ -159,10 +190,16 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
{
|
||||
Socket.Listen(backlog);
|
||||
|
||||
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket listening: {ProtocolType}/{(Socket.LocalEndPoint as IPEndPoint).Port}");
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||
}
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
@@ -182,11 +219,15 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||
}
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
bool hasEmittedBlockingWarning = false;
|
||||
private bool _hasEmittedBlockingWarning;
|
||||
|
||||
public LinuxError Receive(out int receiveSize, Span<byte> buffer, BsdSocketFlags flags)
|
||||
{
|
||||
@@ -202,10 +243,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
shouldBlockAfterOperation = true;
|
||||
}
|
||||
|
||||
if (Blocking && !hasEmittedBlockingWarning)
|
||||
if (Blocking && !_hasEmittedBlockingWarning)
|
||||
{
|
||||
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors.");
|
||||
hasEmittedBlockingWarning = true;
|
||||
_hasEmittedBlockingWarning = true;
|
||||
}
|
||||
|
||||
receiveSize = Socket.Receive(buffer, ConvertBsdSocketFlags(flags));
|
||||
@@ -214,6 +255,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||
}
|
||||
receiveSize = -1;
|
||||
|
||||
result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
@@ -245,10 +290,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
shouldBlockAfterOperation = true;
|
||||
}
|
||||
|
||||
if (Blocking && !hasEmittedBlockingWarning)
|
||||
if (Blocking && !_hasEmittedBlockingWarning)
|
||||
{
|
||||
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors.");
|
||||
hasEmittedBlockingWarning = true;
|
||||
_hasEmittedBlockingWarning = true;
|
||||
}
|
||||
|
||||
if (!Socket.IsBound)
|
||||
@@ -265,6 +310,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||
}
|
||||
receiveSize = -1;
|
||||
|
||||
result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
@@ -288,6 +337,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||
}
|
||||
sendSize = -1;
|
||||
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
@@ -304,6 +357,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||
}
|
||||
sendSize = -1;
|
||||
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
@@ -341,6 +398,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||
}
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
@@ -387,6 +448,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||
}
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
@@ -519,6 +584,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||
}
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
@@ -557,6 +626,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||
}
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy;
|
||||
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -64,10 +66,18 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy
|
||||
{
|
||||
if (_proxy.Supported(domain, type, protocol))
|
||||
{
|
||||
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket is using LDN proxy");
|
||||
return new LdnProxySocket(domain, type, protocol, _proxy);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, $"LDN proxy does not support socket {domain}, {type}, {protocol}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Opening socket using host networking stack");
|
||||
}
|
||||
|
||||
return new DefaultSocket(domain, type, protocol, lanInterfaceId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -582,13 +582,6 @@ namespace Ryujinx.Ava
|
||||
Rainbow.Disable();
|
||||
Rainbow.Reset();
|
||||
|
||||
// Reload settings when the game is turned off
|
||||
// (resets custom settings if there were any)
|
||||
Program.ReloadConfig();
|
||||
|
||||
//Updates the gameList (changes the status of the user setting if it was added or removed during the game)
|
||||
RyujinxApp.MainWindow.GameListUpdate();
|
||||
|
||||
_isStopped = true;
|
||||
Stop();
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<Styles
|
||||
<Styles
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
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">
|
||||
<Design.PreviewWith>
|
||||
<Border Height="2000"
|
||||
@@ -31,8 +30,7 @@
|
||||
<Button
|
||||
Name="btnRem"
|
||||
HorizontalAlignment="Right"
|
||||
Content="Add"
|
||||
Classes="red"/>
|
||||
Content="Add" />
|
||||
<TextBox
|
||||
Width="100"
|
||||
VerticalAlignment="Center"
|
||||
@@ -43,11 +41,7 @@
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<ui:NumberBox Value="1" />
|
||||
<MenuItem
|
||||
Header="123 0000"
|
||||
ToolTip.Tip="What this"/>
|
||||
</StackPanel>
|
||||
|
||||
</Border>
|
||||
</Design.PreviewWith>
|
||||
<Style Selector="DropDownButton">
|
||||
@@ -379,16 +373,6 @@
|
||||
<Setter Property="Background"
|
||||
Value="{DynamicResource AppListHoverBackgroundColor}" />
|
||||
</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>
|
||||
<SolidColorBrush x:Key="ThemeAccentColorBrush"
|
||||
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">
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Default">
|
||||
@@ -12,13 +12,11 @@
|
||||
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
|
||||
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
|
||||
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
|
||||
<Color x:Key="WarningBackgroundColor">#FF6347</Color>
|
||||
<Color x:Key="SecondaryTextColor">#A0000000</Color>
|
||||
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
|
||||
<Color x:Key="Switch">#FF2EEAC9</Color>
|
||||
<Color x:Key="Unbounded">#FFFF4554</Color>
|
||||
<Color x:Key="Custom">#6483F5</Color>
|
||||
<Color x:Key="Warning">#800080</Color>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
|
||||
@@ -31,13 +29,11 @@
|
||||
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
|
||||
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
|
||||
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
|
||||
<Color x:Key="WarningBackgroundColor">#FF6347</Color>
|
||||
<Color x:Key="SecondaryTextColor">#A0000000</Color>
|
||||
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
|
||||
<Color x:Key="Switch">#13c3a4</Color>
|
||||
<Color x:Key="Unbounded">#FFFF4554</Color>
|
||||
<Color x:Key="Custom">#6483F5</Color>
|
||||
<Color x:Key="Warning">#800080</Color>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
|
||||
@@ -50,13 +46,11 @@
|
||||
<Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color>
|
||||
<Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color>
|
||||
<Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color>
|
||||
<Color x:Key="WarningBackgroundColor">#FF6347</Color>
|
||||
<Color x:Key="SecondaryTextColor">#A0FFFFFF</Color>
|
||||
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
|
||||
<Color x:Key="Switch">#FF2EEAC9</Color>
|
||||
<Color x:Key="Unbounded">#FFFF4554</Color>
|
||||
<Color x:Key="Custom">#6483F5</Color>
|
||||
<Color x:Key="Warning">#FFA500</Color>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -54,7 +54,6 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
SetDynamicValues(LocaleKeys.RyujinxInfo, RyujinxApp.FullAppName);
|
||||
SetDynamicValues(LocaleKeys.RyujinxConfirm, RyujinxApp.FullAppName);
|
||||
SetDynamicValues(LocaleKeys.RyujinxUpdater, RyujinxApp.FullAppName);
|
||||
SetDynamicValues(LocaleKeys.RyujinxRebooter, RyujinxApp.FullAppName);
|
||||
}
|
||||
|
||||
public string this[LocaleKeys key]
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
private static readonly string _description =
|
||||
ReleaseInformation.IsValid
|
||||
? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}@{ReleaseInformation.BuildGitHash}"
|
||||
? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}"
|
||||
: "dev build";
|
||||
|
||||
private const string ApplicationId = "1293250299716173864";
|
||||
@@ -56,6 +56,7 @@ namespace Ryujinx.Ava
|
||||
ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
|
||||
TitleIDs.CurrentApplication.Event += (_, e) => Use(e.NewValue);
|
||||
HorizonStatic.PlayReport += HandlePlayReport;
|
||||
PlayReports.Initialize();
|
||||
}
|
||||
|
||||
private static void Update(object sender, ReactiveEventArgs<bool> evnt)
|
||||
|
||||
@@ -289,7 +289,8 @@ namespace Ryujinx.Headless
|
||||
|
||||
DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off);
|
||||
|
||||
if (_inputConfiguration.OfType<StandardControllerInputConfig>().Any(ic => ic.Led.UseRainbow))
|
||||
if (_inputConfiguration.OfType<StandardControllerInputConfig>()
|
||||
.Any(ic => ic?.Led?.UseRainbow ?? false))
|
||||
Rainbow.Enable();
|
||||
|
||||
while (true)
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace Ryujinx.Headless
|
||||
IgnoreMissingServices = configurationState.System.IgnoreMissingServices;
|
||||
|
||||
if (NeedsOverride(nameof(IgnoreControllerApplet)))
|
||||
IgnoreControllerApplet = configurationState.System.IgnoreApplet;
|
||||
IgnoreControllerApplet = configurationState.System.IgnoreControllerApplet;
|
||||
|
||||
return;
|
||||
|
||||
|
||||
@@ -32,10 +32,8 @@ namespace Ryujinx.Ava
|
||||
public static double DesktopScaleFactor { get; set; } = 1.0;
|
||||
public static string Version { 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 UseHardwareAcceleration { get; private set; }
|
||||
public static string BackendThreadingArg { get; private set; }
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true)]
|
||||
public static partial int MessageBoxA(nint hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
|
||||
@@ -49,6 +47,7 @@ namespace Ryujinx.Ava
|
||||
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 19041))
|
||||
{
|
||||
_ = MessageBoxA(nint.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 20H1 and newer.\n", $"Ryujinx {Version}", MbIconwarning);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PreviewerDetached = true;
|
||||
@@ -157,43 +156,11 @@ 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()
|
||||
{
|
||||
//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 appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
|
||||
|
||||
|
||||
// Now load the configuration as the other subsystems are now registered
|
||||
if (File.Exists(localConfigurationPath))
|
||||
{
|
||||
@@ -251,11 +218,6 @@ namespace Ryujinx.Ava
|
||||
_ => ConfigurationState.Instance.Graphics.BackendThreading
|
||||
};
|
||||
|
||||
if (CommandLineState.OverrideBackendThreadingAfterReboot is not null)
|
||||
{
|
||||
BackendThreadingArg = CommandLineState.OverrideBackendThreadingAfterReboot;
|
||||
}
|
||||
|
||||
// Check if docked mode was overriden.
|
||||
if (CommandLineState.OverrideDockedMode.HasValue)
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value;
|
||||
@@ -271,33 +233,6 @@ namespace Ryujinx.Ava
|
||||
_ => 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 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.
|
||||
if (CommandLineState.OverrideHardwareAcceleration != null)
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.Utilities;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava
|
||||
{
|
||||
internal static class Rebooter
|
||||
{
|
||||
|
||||
private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
|
||||
|
||||
|
||||
public static void RebootAppWithGame(string gamePath, List<string> args)
|
||||
{
|
||||
_ = Reboot(gamePath, args);
|
||||
|
||||
}
|
||||
|
||||
private static async Task Reboot(string gamePath, List<string> args)
|
||||
{
|
||||
|
||||
bool shouldRestart = true;
|
||||
|
||||
TaskDialog taskDialog = new()
|
||||
{
|
||||
Header = LocaleManager.Instance[LocaleKeys.RyujinxRebooter],
|
||||
SubHeader = LocaleManager.Instance[LocaleKeys.DialogRebooterMessage],
|
||||
IconSource = new SymbolIconSource { Symbol = Symbol.Games },
|
||||
XamlRoot = RyujinxApp.MainWindow,
|
||||
};
|
||||
|
||||
if (shouldRestart)
|
||||
{
|
||||
List<string> arguments = CommandLineState.Arguments.ToList();
|
||||
string executableDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
// On macOS we perform the update at relaunch.
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", ".."));
|
||||
string newBundlePath = Path.Combine(_updateDir, "Ryujinx.app");
|
||||
string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh");
|
||||
string currentPid = Environment.ProcessId.ToString();
|
||||
|
||||
arguments.InsertRange(0, new List<string> { updaterScriptPath, baseBundlePath, newBundlePath, currentPid });
|
||||
Process.Start("/bin/bash", arguments);
|
||||
}
|
||||
else
|
||||
{
|
||||
var dialogTask = taskDialog.ShowAsync(true);
|
||||
await Task.Delay(500);
|
||||
|
||||
// Find the process name.
|
||||
string ryuName = Path.GetFileName(Environment.ProcessPath) ?? string.Empty;
|
||||
|
||||
// Some operating systems can see the renamed executable, so strip off the .ryuold if found.
|
||||
if (ryuName.EndsWith(".ryuold"))
|
||||
{
|
||||
ryuName = ryuName[..^7];
|
||||
}
|
||||
|
||||
// Fallback if the executable could not be found.
|
||||
if (ryuName.Length == 0 || !Path.Exists(Path.Combine(executableDirectory, ryuName)))
|
||||
{
|
||||
ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx";
|
||||
}
|
||||
|
||||
ProcessStartInfo processStart = new(ryuName)
|
||||
{
|
||||
UseShellExecute = true,
|
||||
WorkingDirectory = executableDirectory,
|
||||
};
|
||||
|
||||
foreach (var arg in args)
|
||||
{
|
||||
processStart.ArgumentList.Add(arg);
|
||||
}
|
||||
|
||||
processStart.ArgumentList.Add(gamePath);
|
||||
|
||||
Process.Start(processStart);
|
||||
}
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
|
||||
bool okPressed = false;
|
||||
|
||||
if (ConfigurationState.Instance.System.IgnoreApplet)
|
||||
if (ConfigurationState.Instance.System.IgnoreControllerApplet)
|
||||
return false;
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
|
||||
@@ -19,11 +19,6 @@
|
||||
Header="{ext:Locale GameListContextMenuCreateShortcut}"
|
||||
Icon="{ext:Icon fa-solid fa-bookmark}"
|
||||
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
|
||||
IsVisible="{Binding HasCompatibilityEntry}"
|
||||
Click="OpenApplicationCompatibility_Click"
|
||||
|
||||
@@ -386,26 +386,13 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
viewModel.SelectedApplication.Icon
|
||||
);
|
||||
}
|
||||
|
||||
public async void EditGameConfiguration_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||
{
|
||||
await new GameSpecificSettingsWindow(viewModel).ShowDialog((Window)viewModel.TopLevel);
|
||||
|
||||
//just checking for file presence
|
||||
viewModel.SelectedApplication.HasIndependentConfiguration = File.Exists(Program.GetDirGameUserConfig(viewModel.SelectedApplication.IdString,false,false));
|
||||
|
||||
viewModel.RefreshView();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async void OpenApplicationCompatibility_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||
await CompatibilityList.Show(viewModel.SelectedApplication.IdString);
|
||||
}
|
||||
|
||||
|
||||
public async void OpenApplicationData_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||
|
||||
@@ -119,17 +119,23 @@
|
||||
TextWrapping="Wrap" >
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<ui:SymbolIcon Foreground="ForestGreen" Symbol="Checkmark" IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"/>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5" ToolTip.Tip="{Binding DynamicRichPresenceDescription}">
|
||||
<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" >
|
||||
TextWrapping="Wrap">
|
||||
</TextBlock>
|
||||
<ui:SymbolIcon Foreground="Red" Symbol="Cancel" IsVisible="{Binding !AppData.HasDynamicRichPresenceSupport}"/>
|
||||
<ui:SymbolIcon
|
||||
Foreground="Red"
|
||||
Symbol="Cancel"
|
||||
IsVisible="{Binding !AppData.HasDynamicRichPresenceSupport}"/>
|
||||
<TextBlock
|
||||
Foreground="Red"
|
||||
HorizontalAlignment="Stretch"
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
Focusable="True"
|
||||
@@ -74,18 +73,12 @@
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
IsVisible="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).ShowNames}">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<TextBlock
|
||||
Text="{Binding Name}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
IsVisible="{Binding HasIndependentConfiguration}"
|
||||
Text="{ext:Locale UserConfigurationHeader}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="{DynamicResource Warning}" />
|
||||
</StackPanel>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Name}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</Panel>
|
||||
</Grid>
|
||||
</Border>
|
||||
@@ -93,28 +86,10 @@
|
||||
Margin="5,5,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
FontSize="18"
|
||||
FontSize="16"
|
||||
Foreground="{DynamicResource FavoriteApplicationIconColor}"
|
||||
IsVisible="{Binding Favorite}"
|
||||
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 HasIndependentConfiguration}"
|
||||
Background="{DynamicResource ThemeContentBackgroundColor}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{ext:Locale UserConfigurationHeader}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
Focusable="True"
|
||||
@@ -157,13 +156,6 @@
|
||||
Text="{Binding Converter={x:Static helpers:MultiplayerInfoConverter.Instance}}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap"/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
IsVisible="{Binding HasIndependentConfiguration}"
|
||||
Text="{ext:Locale UserConfigurationHeader}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="{DynamicResource Warning}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="4"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using Ryujinx.Ava.Utilities.PlayReport;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
@@ -10,6 +11,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public ApplicationDataViewModel(ApplicationData appData) => AppData = appData;
|
||||
|
||||
public string DynamicRichPresenceDescription =>
|
||||
AppData.HasDynamicRichPresenceSupport
|
||||
? AppData.RichPresenceSpec.Value.Description
|
||||
: GameSpec.DefaultDescription;
|
||||
|
||||
public string FormattedVersion => LocaleManager.Instance[LocaleKeys.GameListHeaderVersion].Format(AppData.Version);
|
||||
public string FormattedDeveloper => LocaleManager.Instance[LocaleKeys.GameListHeaderDeveloper].Format(AppData.Developer);
|
||||
public string FormattedFileExtension => LocaleManager.Instance[LocaleKeys.GameListHeaderFileExtension].Format(AppData.FileExtension);
|
||||
|
||||
@@ -355,11 +355,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
set
|
||||
{
|
||||
ListSelectedApplication = value;
|
||||
GridSelectedApplication = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasCompatibilityEntry => SelectedApplication.HasPlayabilityInfo;
|
||||
@@ -1089,7 +1084,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
_rendererWaitEvent.WaitOne();
|
||||
|
||||
AppHost?.Start();
|
||||
|
||||
|
||||
AppHost?.DisposeContext();
|
||||
}
|
||||
|
||||
@@ -1352,6 +1347,25 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
OpenHelper.OpenFolder(AppDataManager.BaseDirPath);
|
||||
}
|
||||
|
||||
public void OpenScreenshotsFolder()
|
||||
{
|
||||
string screenshotsDir = Path.Combine(AppDataManager.BaseDirPath, "screenshots");
|
||||
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(screenshotsDir))
|
||||
Directory.CreateDirectory(screenshotsDir);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Failed to create directory at path {screenshotsDir}. Error : {ex.GetType().Name}", "Screenshot");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
OpenHelper.OpenFolder(screenshotsDir);
|
||||
}
|
||||
|
||||
public void OpenLogsFolder()
|
||||
{
|
||||
string logPath = AppDataManager.GetOrCreateLogsDir();
|
||||
@@ -1536,50 +1550,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public bool InitializeUserConfig(ApplicationData application)
|
||||
{
|
||||
// Code where conditions will be met before loading the user configuration (Global Config)
|
||||
BackendThreading backendThreadingValue = ConfigurationState.Instance.Graphics.BackendThreading.Value;
|
||||
string BackendThreadingInit = Program.BackendThreadingArg;
|
||||
|
||||
if (BackendThreadingInit is null)
|
||||
{
|
||||
BackendThreadingInit = ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString();
|
||||
}
|
||||
|
||||
// If a configuration is found in the "/games/xxxxxxxxxxxxxx" folder, the program will load the user setting.
|
||||
string idGame = application.IdBaseString;
|
||||
if (ConfigurationFileFormat.TryLoad(Program.GetDirGameUserConfig(idGame), out ConfigurationFileFormat configurationFileFormat))
|
||||
{
|
||||
// Loads the user configuration, having previously changed the global configuration to the user configuration
|
||||
ConfigurationState.Instance.Load(configurationFileFormat, Program.GetDirGameUserConfig(idGame, true, true), idGame);
|
||||
}
|
||||
|
||||
// Code where conditions will be executed after loading user configuration
|
||||
if (ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() != BackendThreadingInit)
|
||||
{
|
||||
|
||||
List<string> Arguments = new List<string>
|
||||
{
|
||||
"--bt", ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() // BackendThreading
|
||||
};
|
||||
|
||||
Rebooter.RebootAppWithGame(application.Path, Arguments);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task LoadApplication(ApplicationData application, bool startFullscreen = false, BlitStruct<ApplicationControlProperty>? customNacpData = null)
|
||||
{
|
||||
|
||||
if (InitializeUserConfig(application))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AppHost != null)
|
||||
{
|
||||
await ContentDialogHelper.CreateInfoDialog(
|
||||
@@ -1595,7 +1567,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
#if RELEASE
|
||||
await PerformanceCheck();
|
||||
#endif
|
||||
|
||||
|
||||
Logger.RestartTime();
|
||||
|
||||
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path, ConfigurationState.Instance.System.Language, application.Id);
|
||||
@@ -1640,7 +1612,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
|
||||
gameThread.Start();
|
||||
|
||||
}
|
||||
|
||||
public void SwitchToRenderer(bool startFullscreen) =>
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Humanizer;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.OpenAL;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
@@ -29,7 +27,6 @@ using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading.Tasks;
|
||||
@@ -52,8 +49,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
private int _graphicsBackendMultithreadingIndex;
|
||||
private float _volume;
|
||||
[ObservableProperty] private bool _isVulkanAvailable = true;
|
||||
[ObservableProperty] private bool _gameDirectoryChanged;
|
||||
[ObservableProperty] private bool _autoloadDirectoryChanged;
|
||||
[ObservableProperty] private bool _gameListNeedsRefresh;
|
||||
private readonly List<string> _gpuIds = [];
|
||||
private int _graphicsBackendIndex;
|
||||
private int _scalingFilter;
|
||||
@@ -72,17 +68,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public SettingsHacksViewModel DirtyHacks { get; }
|
||||
|
||||
private readonly bool _isGameRunning;
|
||||
private Bitmap _gameIcon;
|
||||
private string _gameTitle;
|
||||
private string _gamePath;
|
||||
private string _gameId;
|
||||
public bool IsGameRunning => _isGameRunning;
|
||||
public Bitmap GameIcon => _gameIcon;
|
||||
public string GamePath => _gamePath;
|
||||
public string GameTitle => _gameTitle;
|
||||
public string GameId => _gameId;
|
||||
|
||||
public int ResolutionScale
|
||||
{
|
||||
get => _resolutionScale;
|
||||
@@ -350,7 +335,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public bool IsInvalidLdnPassphraseVisible { get; set; }
|
||||
|
||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this(false)
|
||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
|
||||
{
|
||||
_virtualFileSystem = virtualFileSystem;
|
||||
_contentManager = contentManager;
|
||||
@@ -363,51 +348,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public SettingsViewModel(
|
||||
VirtualFileSystem virtualFileSystem,
|
||||
ContentManager contentManager,
|
||||
bool gameRunning,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
_isGameRunning = gameRunning;
|
||||
_gamePath = gamePath;
|
||||
_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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public SettingsViewModel(bool noLoadGlobalConfig = false)
|
||||
public SettingsViewModel()
|
||||
{
|
||||
GameDirectories = [];
|
||||
AutoloadDirectories = [];
|
||||
@@ -422,11 +363,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
Task.Run(LoadAvailableGpus);
|
||||
|
||||
if (!noLoadGlobalConfig)// Default is false, but loading custom config avoids double call
|
||||
{
|
||||
LoadCurrentConfiguration();
|
||||
}
|
||||
LoadCurrentConfiguration();
|
||||
|
||||
DirtyHacks = new SettingsHacksViewModel(this);
|
||||
}
|
||||
@@ -537,35 +474,29 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
ConfigurationState config = ConfigurationState.Instance;
|
||||
|
||||
// User Interface
|
||||
EnableDiscordIntegration = config.EnableDiscordIntegration;
|
||||
CheckUpdatesOnStart = config.CheckUpdatesOnStart;
|
||||
ShowConfirmExit = config.ShowConfirmExit;
|
||||
RememberWindowState = config.RememberWindowState;
|
||||
ShowTitleBar = config.ShowTitleBar;
|
||||
HideCursor = (int)config.HideCursor.Value;
|
||||
UpdateCheckerType = (int)config.UpdateCheckerType.Value;
|
||||
FocusLostActionType = (int)config.FocusLostActionType.Value;
|
||||
|
||||
//It is necessary that the data is used from the global configuration file
|
||||
if (string.IsNullOrEmpty(GameId))
|
||||
GameDirectories.Clear();
|
||||
GameDirectories.AddRange(config.UI.GameDirs.Value);
|
||||
|
||||
AutoloadDirectories.Clear();
|
||||
AutoloadDirectories.AddRange(config.UI.AutoloadDirs.Value);
|
||||
|
||||
BaseStyleIndex = config.UI.BaseStyle.Value switch
|
||||
{
|
||||
// User Interface
|
||||
EnableDiscordIntegration = config.EnableDiscordIntegration;
|
||||
CheckUpdatesOnStart = config.CheckUpdatesOnStart;
|
||||
ShowConfirmExit = config.ShowConfirmExit;
|
||||
RememberWindowState = config.RememberWindowState;
|
||||
ShowTitleBar = config.ShowTitleBar;
|
||||
HideCursor = (int)config.HideCursor.Value;
|
||||
UpdateCheckerType = (int)config.UpdateCheckerType.Value;
|
||||
FocusLostActionType = (int)config.FocusLostActionType.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
|
||||
};
|
||||
|
||||
}
|
||||
"Auto" => 0,
|
||||
"Light" => 1,
|
||||
"Dark" => 2,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
// Input
|
||||
EnableDockedMode = config.System.EnableDockedMode;
|
||||
@@ -586,6 +517,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
DateTime currentDateTime = currentHostDateTime.Add(systemDateTimeOffset);
|
||||
CurrentDate = currentDateTime.Date;
|
||||
CurrentTime = currentDateTime.TimeOfDay;
|
||||
|
||||
MatchSystemTime = config.System.MatchSystemTime;
|
||||
|
||||
EnableCustomVSyncInterval = config.Graphics.EnableCustomVSyncInterval;
|
||||
@@ -594,7 +526,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
|
||||
DramSize = config.System.DramSize;
|
||||
IgnoreMissingServices = config.System.IgnoreMissingServices;
|
||||
IgnoreApplet = config.System.IgnoreApplet;
|
||||
IgnoreApplet = config.System.IgnoreControllerApplet;
|
||||
|
||||
// CPU
|
||||
EnablePptc = config.System.EnablePtc;
|
||||
@@ -646,43 +578,30 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
LdnPassphrase = config.Multiplayer.LdnPassphrase;
|
||||
LdnServer = config.Multiplayer.LdnServer;
|
||||
}
|
||||
|
||||
|
||||
public void SaveSettings()
|
||||
{
|
||||
ConfigurationState config = ConfigurationState.Instance;
|
||||
bool userConfigFile = string.IsNullOrEmpty(GameId);
|
||||
|
||||
// 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;
|
||||
config.UpdateCheckerType.Value = (UpdaterType)UpdateCheckerType;
|
||||
config.FocusLostActionType.Value = (FocusLostType)FocusLostActionType;
|
||||
config.UI.GameDirs.Value = [..GameDirectories];
|
||||
config.UI.AutoloadDirs.Value = [..AutoloadDirectories];
|
||||
|
||||
if (userConfigFile)
|
||||
config.UI.BaseStyle.Value = BaseStyleIndex switch
|
||||
{
|
||||
// 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;
|
||||
config.UpdateCheckerType.Value = (UpdaterType)UpdateCheckerType;
|
||||
config.FocusLostActionType.Value = (FocusLostType)FocusLostActionType;
|
||||
|
||||
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"
|
||||
};
|
||||
}
|
||||
0 => "Auto",
|
||||
1 => "Light",
|
||||
2 => "Dark",
|
||||
_ => "Auto"
|
||||
};
|
||||
|
||||
// Input
|
||||
config.System.EnableDockedMode.Value = EnableDockedMode;
|
||||
@@ -695,8 +614,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
// System
|
||||
config.System.Region.Value = (Region)Region;
|
||||
|
||||
if (config.System.Language.Value != (Language)Language)
|
||||
GameListNeedsRefresh = true;
|
||||
|
||||
config.System.Language.Value = (Language)Language;
|
||||
|
||||
if (_validTzRegions.Contains(TimeZone))
|
||||
{
|
||||
config.System.TimeZone.Value = TimeZone;
|
||||
@@ -707,7 +629,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
|
||||
config.System.DramSize.Value = DramSize;
|
||||
config.System.IgnoreMissingServices.Value = IgnoreMissingServices;
|
||||
config.System.IgnoreApplet.Value = IgnoreApplet;
|
||||
config.System.IgnoreControllerApplet.Value = IgnoreApplet;
|
||||
|
||||
// CPU
|
||||
config.System.EnablePtc.Value = EnablePptc;
|
||||
@@ -780,26 +702,19 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
config.Hacks.EnableShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelayEnabled;
|
||||
config.Hacks.ShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelay;
|
||||
|
||||
|
||||
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
|
||||
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
|
||||
MainWindow.UpdateGraphicsConfig();
|
||||
RyujinxApp.MainWindow.ViewModel.VSyncModeSettingChanged();
|
||||
|
||||
SaveSettingsEvent?.Invoke();
|
||||
|
||||
GameDirectoryChanged = false;
|
||||
AutoloadDirectoryChanged = false;
|
||||
GameListNeedsRefresh = false;
|
||||
}
|
||||
|
||||
private static void RevertIfNotSaved()
|
||||
{
|
||||
// maybe this is an unnecessary check(all options need to be tested)
|
||||
if (string.IsNullOrEmpty(Program.GlobalConfigurationPath))
|
||||
{
|
||||
Program.ReloadConfig();
|
||||
}
|
||||
Program.ReloadConfig();
|
||||
}
|
||||
|
||||
public void ApplyButton()
|
||||
@@ -807,26 +722,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
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()
|
||||
{
|
||||
SaveSettings();
|
||||
|
||||
@@ -66,6 +66,10 @@
|
||||
Command="{Binding OpenRyujinxFolder}"
|
||||
Header="{ext:Locale MenuBarFileOpenEmuFolder}"
|
||||
ToolTip.Tip="{ext:Locale OpenRyujinxFolderTooltip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenScreenshotsFolder}"
|
||||
Header="{ext:Locale MenuBarFileOpenScreenshotsFolder}"
|
||||
ToolTip.Tip="{ext:Locale OpenScreenshotFolderTooltip}"/>
|
||||
<MenuItem
|
||||
Command="{Binding OpenLogsFolder}"
|
||||
Header="{ext:Locale MenuBarFileOpenLogsFolder}"
|
||||
|
||||
@@ -130,26 +130,9 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager);
|
||||
|
||||
Rainbow.Enable();
|
||||
|
||||
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 GameSpecificSettingsWindow(ViewModel, userConfigExist).ShowDialog((Window)ViewModel.TopLevel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
await Window.SettingsWindow.ShowDialog(Window);
|
||||
|
||||
Rainbow.Disable();
|
||||
Rainbow.Reset();
|
||||
|
||||
|
||||
@@ -316,8 +316,8 @@
|
||||
</CheckBox>
|
||||
<CheckBox
|
||||
IsChecked="{Binding IgnoreApplet}"
|
||||
ToolTip.Tip="{ext:Locale IgnoreAppletTooltip}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemIgnoreApplet}" />
|
||||
ToolTip.Tip="{ext:Locale IgnoreControllerAppletTooltip}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemIgnoreControllerApplet}" />
|
||||
</CheckBox>
|
||||
<CheckBox
|
||||
IsChecked="{Binding EnableCustomVSyncInterval}"
|
||||
|
||||
@@ -36,11 +36,8 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
directories.Add(path);
|
||||
|
||||
addDirBox.Clear();
|
||||
|
||||
if (isGameList)
|
||||
ViewModel.GameDirectoryChanged = true;
|
||||
else
|
||||
ViewModel.AutoloadDirectoryChanged = true;
|
||||
|
||||
ViewModel.GameListNeedsRefresh = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -50,10 +47,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
directories.Add(folder.Value.Path.LocalPath);
|
||||
|
||||
if (isGameList)
|
||||
ViewModel.GameDirectoryChanged = true;
|
||||
else
|
||||
ViewModel.AutoloadDirectoryChanged = true;
|
||||
ViewModel.GameListNeedsRefresh = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,7 +59,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
foreach (string path in new List<string>(GameDirsList.SelectedItems.Cast<string>()))
|
||||
{
|
||||
ViewModel.GameDirectories.Remove(path);
|
||||
ViewModel.GameDirectoryChanged = true;
|
||||
ViewModel.GameListNeedsRefresh = true;
|
||||
}
|
||||
|
||||
if (GameDirsList.ItemCount > 0)
|
||||
@@ -81,7 +75,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
foreach (string path in new List<string>(AutoloadDirsList.SelectedItems.Cast<string>()))
|
||||
{
|
||||
ViewModel.AutoloadDirectories.Remove(path);
|
||||
ViewModel.AutoloadDirectoryChanged = true;
|
||||
ViewModel.GameListNeedsRefresh = true;
|
||||
}
|
||||
|
||||
if (AutoloadDirsList.ItemCount > 0)
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
<window:StyleableAppWindow
|
||||
x:Class="Ryujinx.Ava.UI.Windows.GameSpecificSettingsWindow"
|
||||
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
|
||||
IsVisible="{Binding IsGameRunning}"
|
||||
Content="{ext:Locale SettingsButtonApply}"
|
||||
Command="{Binding ApplyButton}" />
|
||||
<Button
|
||||
IsVisible="{Binding !IsGameRunning}"
|
||||
Content="{ext:Locale UserProfilesDelete}"
|
||||
Command="{Binding DeleteConfigGame}"
|
||||
Classes="red"/>
|
||||
</ReversibleStackPanel>
|
||||
</Grid>
|
||||
</window:StyleableAppWindow>
|
||||
@@ -1,117 +0,0 @@
|
||||
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 GameSpecificSettingsWindow : StyleableAppWindow
|
||||
{
|
||||
internal readonly SettingsViewModel ViewModel;
|
||||
|
||||
public GameSpecificSettingsWindow(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.IsGameRunning,
|
||||
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)
|
||||
{
|
||||
InputPage.Dispose(); // You need to unload the gamepad settings, otherwise the controls will be blocked
|
||||
base.OnClosing(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,7 +223,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
_deferLoad = true;
|
||||
_launchPath = launchPathArg;
|
||||
_launchApplicationId = launchApplicationId;
|
||||
_startFullscreen = startFullscreenArg;
|
||||
_startFullscreen = startFullscreenArg;
|
||||
}
|
||||
|
||||
public void SwitchToGameControl(bool startFullscreen = false)
|
||||
@@ -374,7 +374,6 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
if (applicationData != null)
|
||||
{
|
||||
ViewModel.SelectedApplication = applicationData;
|
||||
await ViewModel.LoadApplication(applicationData, _startFullscreen);
|
||||
}
|
||||
else
|
||||
@@ -386,7 +385,6 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
else
|
||||
{
|
||||
applicationData = applications[0];
|
||||
ViewModel.SelectedApplication = applicationData;
|
||||
await ViewModel.LoadApplication(applicationData, _startFullscreen);
|
||||
}
|
||||
}
|
||||
@@ -660,11 +658,6 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
ReloadGameList();
|
||||
}
|
||||
|
||||
public void GameListUpdate()
|
||||
{
|
||||
ReloadGameList();
|
||||
}
|
||||
|
||||
public void ToggleFileType(string fileType)
|
||||
{
|
||||
switch (fileType)
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
InputPage.InputView?.SaveCurrentProfile();
|
||||
|
||||
if (Owner is MainWindow window && (ViewModel.GameDirectoryChanged || ViewModel.AutoloadDirectoryChanged))
|
||||
if (Owner is MainWindow window && ViewModel.GameListNeedsRefresh)
|
||||
{
|
||||
window.LoadApplications();
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using LibHac.Tools.FsSystem;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Utilities.Compat;
|
||||
using Ryujinx.Ava.Utilities.PlayReport;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
||||
@@ -23,7 +24,6 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
||||
public class ApplicationData
|
||||
{
|
||||
public bool Favorite { get; set; }
|
||||
public bool HasIndependentConfiguration { get; set; }
|
||||
public byte[] Icon { get; set; }
|
||||
public string Name { get; set; } = "Unknown";
|
||||
|
||||
@@ -36,9 +36,14 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
||||
{
|
||||
_id = value;
|
||||
|
||||
Compatibility = CompatibilityCsv.Find(Id);
|
||||
Compatibility = CompatibilityCsv.Find(value);
|
||||
RichPresenceSpec = PlayReports.Analyzer.TryGetSpec(IdString, out GameSpec gameSpec)
|
||||
? gameSpec
|
||||
: default(Optional<GameSpec>);
|
||||
}
|
||||
}
|
||||
public Optional<GameSpec> RichPresenceSpec { get; set; }
|
||||
|
||||
public string Developer { get; set; } = "Unknown";
|
||||
public string Version { get; set; } = "0";
|
||||
public int PlayerCount { get; set; }
|
||||
@@ -47,7 +52,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
||||
public bool HasLdnGames => PlayerCount != 0 && GameCount != 0;
|
||||
|
||||
public bool HasRichPresenceAsset => DiscordIntegrationModule.HasAssetImage(IdString);
|
||||
public bool HasDynamicRichPresenceSupport => DiscordIntegrationModule.HasAnalyzer(IdString);
|
||||
public bool HasDynamicRichPresenceSupport => RichPresenceSpec.HasValue;
|
||||
|
||||
public TimeSpan TimePlayed { get; set; }
|
||||
public DateTime? LastPlayed { get; set; }
|
||||
|
||||
@@ -505,7 +505,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
||||
if (data.Id != 0)
|
||||
{
|
||||
ApplicationMetadata appMetadata = LoadAndSaveMetaData(data.IdString, appMetadata =>
|
||||
{
|
||||
{
|
||||
appMetadata.Title = data.Name;
|
||||
|
||||
// Only do the migration if time_played has a value and timespan_played hasn't been updated yet.
|
||||
@@ -529,11 +529,10 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
data.Favorite = appMetadata.Favorite;
|
||||
data.TimePlayed = appMetadata.TimePlayed;
|
||||
data.LastPlayed = appMetadata.LastPlayed;
|
||||
data.HasIndependentConfiguration = File.Exists(Program.GetDirGameUserConfig(data.IdBaseString, false, false)); // Just check user config
|
||||
}
|
||||
|
||||
data.FileExtension = Path.GetExtension(applicationPath).TrimStart('.').ToUpper();
|
||||
|
||||
@@ -6,16 +6,11 @@ namespace Ryujinx.Ava.Utilities
|
||||
public static class CommandLineState
|
||||
{
|
||||
public static string[] Arguments { get; private set; }
|
||||
public static int CountArguments { get; private set; }
|
||||
|
||||
public static bool? OverrideDockedMode { get; private set; }
|
||||
public static bool? OverrideHardwareAcceleration { get; private set; }
|
||||
public static string OverrideGraphicsBackend { get; private set; }
|
||||
public static string OverrideBackendThreading { get; private set; }
|
||||
public static string OverrideBackendThreadingAfterReboot { 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 BaseDirPathArg { get; private set; }
|
||||
public static string Profile { get; private set; }
|
||||
@@ -24,7 +19,6 @@ namespace Ryujinx.Ava.Utilities
|
||||
public static bool StartFullscreenArg { get; private set; }
|
||||
public static bool HideAvailableUpdates { get; private set; }
|
||||
|
||||
|
||||
public static void ParseArguments(string[] args)
|
||||
{
|
||||
List<string> arguments = [];
|
||||
@@ -34,11 +28,6 @@ namespace Ryujinx.Ava.Utilities
|
||||
{
|
||||
string arg = args[i];
|
||||
|
||||
if (arg.Contains("-") || arg.Contains("--"))
|
||||
{
|
||||
CountArguments++;
|
||||
}
|
||||
|
||||
switch (arg)
|
||||
{
|
||||
case "-r":
|
||||
@@ -96,57 +85,6 @@ namespace Ryujinx.Ava.Utilities
|
||||
|
||||
OverrideBackendThreading = args[++i];
|
||||
break;
|
||||
case "--bt":
|
||||
if (i + 1 >= args.Length)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
OverrideBackendThreadingAfterReboot = args[++i];
|
||||
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 "--application-id":
|
||||
LaunchApplicationId = args[++i];
|
||||
|
||||
@@ -183,7 +183,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
public bool ShowConfirmExit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ignore "Applet" dialog
|
||||
/// Ignore Controller Applet dialog
|
||||
/// </summary>
|
||||
public bool IgnoreApplet { get; set; }
|
||||
|
||||
|
||||
@@ -18,15 +18,9 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
{
|
||||
public partial class ConfigurationState
|
||||
{
|
||||
public void Load(ConfigurationFileFormat cff, string configurationFilePath, string titleId = "")
|
||||
public void Load(ConfigurationFileFormat cff, string configurationFilePath)
|
||||
{
|
||||
bool configurationFileUpdated = false;
|
||||
bool shouldLoadFromFile = true;
|
||||
|
||||
if (!string.IsNullOrEmpty(titleId))
|
||||
{
|
||||
shouldLoadFromFile = false;
|
||||
}
|
||||
|
||||
if (cff.Version is < 0 or > ConfigurationFileFormat.CurrentVersion)
|
||||
{
|
||||
@@ -49,17 +43,16 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
configurationFileUpdated = true;
|
||||
}
|
||||
|
||||
|
||||
EnableDiscordIntegration.Value = shouldLoadFromFile ? cff.EnableDiscordIntegration : EnableDiscordIntegration.Value; // Get from global config only
|
||||
CheckUpdatesOnStart.Value = shouldLoadFromFile ? cff.CheckUpdatesOnStart : CheckUpdatesOnStart.Value; // Get from global config only
|
||||
UpdateCheckerType.Value = shouldLoadFromFile ? cff.UpdateCheckerType : UpdateCheckerType.Value; // Get from global config only
|
||||
FocusLostActionType.Value = shouldLoadFromFile ? cff.FocusLostActionType : FocusLostActionType.Value; // Get from global config only
|
||||
ShowConfirmExit.Value = shouldLoadFromFile ? cff.ShowConfirmExit : ShowConfirmExit.Value; // Get from global config only
|
||||
RememberWindowState.Value = shouldLoadFromFile ? cff.RememberWindowState : RememberWindowState.Value; // Get from global config only
|
||||
ShowTitleBar.Value = shouldLoadFromFile ? cff.ShowTitleBar : ShowTitleBar.Value; // Get from global config only
|
||||
EnableHardwareAcceleration.Value = shouldLoadFromFile ? cff.EnableHardwareAcceleration : EnableHardwareAcceleration.Value; // Get from global config only
|
||||
HideCursor.Value = shouldLoadFromFile ? cff.HideCursor : HideCursor.Value; // Get from global config only
|
||||
|
||||
EnableDiscordIntegration.Value = cff.EnableDiscordIntegration;
|
||||
CheckUpdatesOnStart.Value = cff.CheckUpdatesOnStart;
|
||||
UpdateCheckerType.Value = cff.UpdateCheckerType;
|
||||
FocusLostActionType.Value = cff.FocusLostActionType;
|
||||
ShowConfirmExit.Value = cff.ShowConfirmExit;
|
||||
RememberWindowState.Value = cff.RememberWindowState;
|
||||
ShowTitleBar.Value = cff.ShowTitleBar;
|
||||
EnableHardwareAcceleration.Value = cff.EnableHardwareAcceleration;
|
||||
HideCursor.Value = cff.HideCursor;
|
||||
|
||||
Logger.EnableFileLog.Value = cff.EnableFileLog;
|
||||
Logger.EnableDebug.Value = cff.LoggingEnableDebug;
|
||||
Logger.EnableStub.Value = cff.LoggingEnableStub;
|
||||
@@ -94,7 +87,8 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
System.Language.Value = cff.SystemLanguage;
|
||||
System.Region.Value = cff.SystemRegion;
|
||||
System.TimeZone.Value = cff.SystemTimeZone;
|
||||
System.SystemTimeOffset.Value = shouldLoadFromFile ? cff.SystemTimeOffset : System.SystemTimeOffset.Value; // Get from global config only
|
||||
System.SystemTimeOffset.Value = cff.SystemTimeOffset;
|
||||
System.MatchSystemTime.Value = cff.MatchSystemTime;
|
||||
System.EnableDockedMode.Value = cff.DockedMode;
|
||||
System.EnablePtc.Value = cff.EnablePtc;
|
||||
System.EnableLowPowerPtc.Value = cff.EnableLowPowerPtc;
|
||||
@@ -106,50 +100,49 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
System.MemoryManagerMode.Value = cff.MemoryManagerMode;
|
||||
System.DramSize.Value = cff.DramSize;
|
||||
System.IgnoreMissingServices.Value = cff.IgnoreMissingServices;
|
||||
System.IgnoreApplet.Value = cff.IgnoreApplet;
|
||||
System.IgnoreControllerApplet.Value = cff.IgnoreApplet;
|
||||
System.UseHypervisor.Value = cff.UseHypervisor;
|
||||
|
||||
UI.GuiColumns.FavColumn.Value = shouldLoadFromFile ? cff.GuiColumns.FavColumn : UI.GuiColumns.FavColumn.Value;
|
||||
UI.GuiColumns.IconColumn.Value = shouldLoadFromFile ? cff.GuiColumns.IconColumn : UI.GuiColumns.IconColumn.Value;
|
||||
UI.GuiColumns.AppColumn.Value = shouldLoadFromFile ? cff.GuiColumns.AppColumn : UI.GuiColumns.AppColumn.Value;
|
||||
UI.GuiColumns.DevColumn.Value = shouldLoadFromFile ? cff.GuiColumns.DevColumn : UI.GuiColumns.DevColumn.Value;
|
||||
UI.GuiColumns.VersionColumn.Value = shouldLoadFromFile ? cff.GuiColumns.VersionColumn : UI.GuiColumns.VersionColumn.Value;
|
||||
UI.GuiColumns.TimePlayedColumn.Value = shouldLoadFromFile ? cff.GuiColumns.TimePlayedColumn : UI.GuiColumns.TimePlayedColumn.Value;
|
||||
UI.GuiColumns.LastPlayedColumn.Value = shouldLoadFromFile ? cff.GuiColumns.LastPlayedColumn : UI.GuiColumns.LastPlayedColumn.Value;
|
||||
UI.GuiColumns.FileExtColumn.Value = shouldLoadFromFile ? cff.GuiColumns.FileExtColumn : UI.GuiColumns.FileExtColumn.Value;
|
||||
UI.GuiColumns.FileSizeColumn.Value = shouldLoadFromFile ? cff.GuiColumns.FileSizeColumn : UI.GuiColumns.FileSizeColumn.Value;
|
||||
UI.GuiColumns.PathColumn.Value = shouldLoadFromFile ? cff.GuiColumns.PathColumn : UI.GuiColumns.PathColumn.Value;
|
||||
UI.ColumnSort.SortColumnId.Value = shouldLoadFromFile ? cff.ColumnSort.SortColumnId : UI.ColumnSort.SortColumnId.Value;
|
||||
UI.ColumnSort.SortAscending.Value = shouldLoadFromFile ? cff.ColumnSort.SortAscending : UI.ColumnSort.SortAscending.Value;
|
||||
UI.GameDirs.Value = shouldLoadFromFile ? cff.GameDirs : UI.GameDirs.Value;
|
||||
UI.AutoloadDirs.Value = shouldLoadFromFile ? (cff.AutoloadDirs ?? []) : UI.AutoloadDirs.Value;
|
||||
UI.ShownFileTypes.NSP.Value = shouldLoadFromFile ? cff.ShownFileTypes.NSP : UI.ShownFileTypes.NSP.Value;
|
||||
UI.ShownFileTypes.PFS0.Value = shouldLoadFromFile ? cff.ShownFileTypes.PFS0 : UI.ShownFileTypes.PFS0.Value;
|
||||
UI.ShownFileTypes.XCI.Value = shouldLoadFromFile ? cff.ShownFileTypes.XCI : UI.ShownFileTypes.XCI.Value;
|
||||
UI.ShownFileTypes.NCA.Value = shouldLoadFromFile ? cff.ShownFileTypes.NCA : UI.ShownFileTypes.NCA.Value;
|
||||
UI.ShownFileTypes.NRO.Value = shouldLoadFromFile ? cff.ShownFileTypes.NRO : UI.ShownFileTypes.NRO.Value;
|
||||
UI.ShownFileTypes.NSO.Value = shouldLoadFromFile ? cff.ShownFileTypes.NSO : UI.ShownFileTypes.NSO.Value;
|
||||
UI.LanguageCode.Value = shouldLoadFromFile ? cff.LanguageCode : UI.LanguageCode.Value;
|
||||
UI.BaseStyle.Value = shouldLoadFromFile ? cff.BaseStyle : UI.BaseStyle.Value;
|
||||
UI.GameListViewMode.Value = shouldLoadFromFile ? cff.GameListViewMode : UI.GameListViewMode.Value;
|
||||
UI.ShowNames.Value = shouldLoadFromFile ? cff.ShowNames : UI.ShowNames.Value;
|
||||
UI.IsAscendingOrder.Value = shouldLoadFromFile ? cff.IsAscendingOrder : UI.IsAscendingOrder.Value;
|
||||
UI.GridSize.Value = shouldLoadFromFile ? cff.GridSize : UI.GridSize.Value;
|
||||
UI.ApplicationSort.Value = shouldLoadFromFile ? cff.ApplicationSort : UI.ApplicationSort.Value;
|
||||
UI.StartFullscreen.Value = shouldLoadFromFile ? cff.StartFullscreen : UI.StartFullscreen.Value;
|
||||
UI.StartNoUI.Value = shouldLoadFromFile ? cff.StartNoUI : UI.StartNoUI.Value;
|
||||
UI.ShowConsole.Value = shouldLoadFromFile ? cff.ShowConsole : UI.ShowConsole.Value;
|
||||
UI.WindowStartup.WindowSizeWidth.Value = shouldLoadFromFile ? cff.WindowStartup.WindowSizeWidth : UI.WindowStartup.WindowSizeWidth.Value;
|
||||
UI.WindowStartup.WindowSizeHeight.Value = shouldLoadFromFile ? cff.WindowStartup.WindowSizeHeight : UI.WindowStartup.WindowSizeHeight.Value;
|
||||
UI.WindowStartup.WindowPositionX.Value = shouldLoadFromFile ? cff.WindowStartup.WindowPositionX : UI.WindowStartup.WindowPositionX.Value;
|
||||
UI.WindowStartup.WindowPositionY.Value = shouldLoadFromFile ? cff.WindowStartup.WindowPositionY : UI.WindowStartup.WindowPositionY.Value;
|
||||
UI.WindowStartup.WindowMaximized.Value = shouldLoadFromFile ? cff.WindowStartup.WindowMaximized : UI.WindowStartup.WindowMaximized.Value;
|
||||
|
||||
|
||||
|
||||
UI.GuiColumns.FavColumn.Value = cff.GuiColumns.FavColumn;
|
||||
UI.GuiColumns.IconColumn.Value = cff.GuiColumns.IconColumn;
|
||||
UI.GuiColumns.AppColumn.Value = cff.GuiColumns.AppColumn;
|
||||
UI.GuiColumns.DevColumn.Value = cff.GuiColumns.DevColumn;
|
||||
UI.GuiColumns.VersionColumn.Value = cff.GuiColumns.VersionColumn;
|
||||
UI.GuiColumns.TimePlayedColumn.Value = cff.GuiColumns.TimePlayedColumn;
|
||||
UI.GuiColumns.LastPlayedColumn.Value = cff.GuiColumns.LastPlayedColumn;
|
||||
UI.GuiColumns.FileExtColumn.Value = cff.GuiColumns.FileExtColumn;
|
||||
UI.GuiColumns.FileSizeColumn.Value = cff.GuiColumns.FileSizeColumn;
|
||||
UI.GuiColumns.PathColumn.Value = cff.GuiColumns.PathColumn;
|
||||
UI.ColumnSort.SortColumnId.Value = cff.ColumnSort.SortColumnId;
|
||||
UI.ColumnSort.SortAscending.Value = cff.ColumnSort.SortAscending;
|
||||
UI.GameDirs.Value = cff.GameDirs;
|
||||
UI.AutoloadDirs.Value = cff.AutoloadDirs ?? [];
|
||||
UI.ShownFileTypes.NSP.Value = cff.ShownFileTypes.NSP;
|
||||
UI.ShownFileTypes.PFS0.Value = cff.ShownFileTypes.PFS0;
|
||||
UI.ShownFileTypes.XCI.Value = cff.ShownFileTypes.XCI;
|
||||
UI.ShownFileTypes.NCA.Value = cff.ShownFileTypes.NCA;
|
||||
UI.ShownFileTypes.NRO.Value = cff.ShownFileTypes.NRO;
|
||||
UI.ShownFileTypes.NSO.Value = cff.ShownFileTypes.NSO;
|
||||
UI.LanguageCode.Value = cff.LanguageCode;
|
||||
UI.BaseStyle.Value = cff.BaseStyle;
|
||||
UI.GameListViewMode.Value = cff.GameListViewMode;
|
||||
UI.ShowNames.Value = cff.ShowNames;
|
||||
UI.IsAscendingOrder.Value = cff.IsAscendingOrder;
|
||||
UI.GridSize.Value = cff.GridSize;
|
||||
UI.ApplicationSort.Value = cff.ApplicationSort;
|
||||
UI.StartFullscreen.Value = cff.StartFullscreen;
|
||||
UI.StartNoUI.Value = cff.StartNoUI;
|
||||
UI.ShowConsole.Value = cff.ShowConsole;
|
||||
UI.WindowStartup.WindowSizeWidth.Value = cff.WindowStartup.WindowSizeWidth;
|
||||
UI.WindowStartup.WindowSizeHeight.Value = cff.WindowStartup.WindowSizeHeight;
|
||||
UI.WindowStartup.WindowPositionX.Value = cff.WindowStartup.WindowPositionX;
|
||||
UI.WindowStartup.WindowPositionY.Value = cff.WindowStartup.WindowPositionY;
|
||||
UI.WindowStartup.WindowMaximized.Value = cff.WindowStartup.WindowMaximized;
|
||||
|
||||
Hid.EnableKeyboard.Value = cff.EnableKeyboard;
|
||||
Hid.EnableMouse.Value = cff.EnableMouse;
|
||||
Hid.DisableInputWhenOutOfFocus.Value = shouldLoadFromFile ? cff.DisableInputWhenOutOfFocus: Hid.DisableInputWhenOutOfFocus.Value; // Get from global config only
|
||||
Hid.Hotkeys.Value = shouldLoadFromFile ? cff.Hotkeys : Hid.Hotkeys.Value; // Get from global config only
|
||||
Hid.DisableInputWhenOutOfFocus.Value = cff.DisableInputWhenOutOfFocus;
|
||||
Hid.Hotkeys.Value = cff.Hotkeys;
|
||||
Hid.InputConfig.Value = cff.InputConfig ?? [];
|
||||
Hid.RainbowSpeed.Value = cff.RainbowSpeed;
|
||||
|
||||
|
||||
@@ -383,7 +383,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
/// <summary>
|
||||
/// Ignore Controller Applet
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> IgnoreApplet { get; private set; }
|
||||
public ReactiveObject<bool> IgnoreControllerApplet { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Uses Hypervisor over JIT if available
|
||||
@@ -424,8 +424,8 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
DramSize.LogChangesToValue(nameof(DramSize));
|
||||
IgnoreMissingServices = new ReactiveObject<bool>();
|
||||
IgnoreMissingServices.LogChangesToValue(nameof(IgnoreMissingServices));
|
||||
IgnoreApplet = new ReactiveObject<bool>();
|
||||
IgnoreApplet.LogChangesToValue(nameof(IgnoreApplet));
|
||||
IgnoreControllerApplet = new ReactiveObject<bool>();
|
||||
IgnoreControllerApplet.LogChangesToValue(nameof(IgnoreControllerApplet));
|
||||
AudioVolume = new ReactiveObject<float>();
|
||||
AudioVolume.LogChangesToValue(nameof(AudioVolume));
|
||||
UseHypervisor = new ReactiveObject<bool>();
|
||||
|
||||
@@ -53,6 +53,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
SystemRegion = System.Region,
|
||||
SystemTimeZone = System.TimeZone,
|
||||
SystemTimeOffset = System.SystemTimeOffset,
|
||||
MatchSystemTime = System.MatchSystemTime,
|
||||
DockedMode = System.EnableDockedMode,
|
||||
EnableDiscordIntegration = EnableDiscordIntegration,
|
||||
CheckUpdatesOnStart = CheckUpdatesOnStart,
|
||||
@@ -80,7 +81,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
MemoryManagerMode = System.MemoryManagerMode,
|
||||
DramSize = System.DramSize,
|
||||
IgnoreMissingServices = System.IgnoreMissingServices,
|
||||
IgnoreApplet = System.IgnoreApplet,
|
||||
IgnoreApplet = System.IgnoreControllerApplet,
|
||||
UseHypervisor = System.UseHypervisor,
|
||||
GuiColumns = new GuiColumns
|
||||
{
|
||||
@@ -204,7 +205,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
System.MemoryManagerMode.Value = MemoryManagerMode.HostMappedUnsafe;
|
||||
System.DramSize.Value = MemoryConfiguration.MemoryConfiguration4GiB;
|
||||
System.IgnoreMissingServices.Value = false;
|
||||
System.IgnoreApplet.Value = false;
|
||||
System.IgnoreControllerApplet.Value = false;
|
||||
System.UseHypervisor.Value = true;
|
||||
Multiplayer.LanInterfaceId.Value = "0";
|
||||
Multiplayer.Mode.Value = MultiplayerMode.Disabled;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -19,6 +20,11 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
|
||||
public IReadOnlyList<GameSpec> Specs => new ReadOnlyCollection<GameSpec>(_specs);
|
||||
|
||||
public GameSpec GetSpec(string titleId) => _specs.First(x => x.TitleIds.ContainsIgnoreCase(titleId));
|
||||
|
||||
public bool TryGetSpec(string titleId, out GameSpec gameSpec)
|
||||
=> (gameSpec = _specs.FirstOrDefault(x => x.TitleIds.ContainsIgnoreCase(titleId))) != null;
|
||||
|
||||
/// <summary>
|
||||
/// Add an analysis spec matching a specific game by title ID, with the provided spec configuration.
|
||||
/// </summary>
|
||||
@@ -27,10 +33,12 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
/// <returns>The current <see cref="Analyzer"/>, for chaining convenience.</returns>
|
||||
public Analyzer AddSpec(string titleId, Func<GameSpec, GameSpec> transform)
|
||||
{
|
||||
Guard.Ensure(ulong.TryParse(titleId, NumberStyles.HexNumber, null, out _),
|
||||
$"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
|
||||
if (ulong.TryParse(titleId, NumberStyles.HexNumber, null, out _))
|
||||
return AddSpec(transform(GameSpec.Create(titleId)));
|
||||
|
||||
return AddSpec(transform(GameSpec.Create(titleId)));
|
||||
Logger.Notice.PrintMsg(LogClass.Application,
|
||||
$"Tried to add a {nameof(GameSpec)} with a non-hexadecimal title ID value. Input: '{titleId}'");
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -41,10 +49,12 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
/// <returns>The current <see cref="Analyzer"/>, for chaining convenience.</returns>
|
||||
public Analyzer AddSpec(string titleId, Action<GameSpec> transform)
|
||||
{
|
||||
Guard.Ensure(ulong.TryParse(titleId, NumberStyles.HexNumber, null, out _),
|
||||
$"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
|
||||
if (ulong.TryParse(titleId, NumberStyles.HexNumber, null, out _))
|
||||
return AddSpec(GameSpec.Create(titleId).Apply(transform));
|
||||
|
||||
return AddSpec(GameSpec.Create(titleId).Apply(transform));
|
||||
Logger.Notice.PrintMsg(LogClass.Application,
|
||||
$"Tried to add a {nameof(GameSpec)} with a non-hexadecimal title ID value. Input: '{titleId}'");
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -57,10 +67,19 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
Func<GameSpec, GameSpec> transform)
|
||||
{
|
||||
string[] tids = titleIds.ToArray();
|
||||
Guard.Ensure(tids.All(x => ulong.TryParse(x, NumberStyles.HexNumber, null, out _)),
|
||||
$"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
|
||||
if (tids.All(x => ulong.TryParse(x, NumberStyles.HexNumber, null, out _) && !string.IsNullOrEmpty(x)))
|
||||
return AddSpec(transform(GameSpec.Create(tids)));
|
||||
|
||||
return AddSpec(transform(GameSpec.Create(tids)));
|
||||
Logger.Notice.PrintMsg(LogClass.Application,
|
||||
$"Tried to add a {nameof(GameSpec)} with a non-hexadecimal title ID value. Input: '{
|
||||
tids.FormatCollection(
|
||||
x => x,
|
||||
separator: ", ",
|
||||
prefix: "[",
|
||||
suffix: "]"
|
||||
)
|
||||
}'");
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -72,12 +91,21 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
public Analyzer AddSpec(IEnumerable<string> titleIds, Action<GameSpec> transform)
|
||||
{
|
||||
string[] tids = titleIds.ToArray();
|
||||
Guard.Ensure(tids.All(x => ulong.TryParse(x, NumberStyles.HexNumber, null, out _)),
|
||||
$"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
|
||||
if (tids.All(x => ulong.TryParse(x, NumberStyles.HexNumber, null, out _) && !string.IsNullOrEmpty(x)))
|
||||
return AddSpec(GameSpec.Create(tids).Apply(transform));
|
||||
|
||||
return AddSpec(GameSpec.Create(tids).Apply(transform));
|
||||
Logger.Notice.PrintMsg(LogClass.Application,
|
||||
$"Tried to add a {nameof(GameSpec)} with a non-hexadecimal title ID value. Input: '{
|
||||
tids.FormatCollection(
|
||||
x => x,
|
||||
separator: ", ",
|
||||
prefix: "[",
|
||||
suffix: "]"
|
||||
)
|
||||
}'");
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Add an analysis spec matching a specific game by title ID, with the provided pre-configured spec.
|
||||
/// </summary>
|
||||
@@ -105,13 +133,13 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
{
|
||||
if (!playReport.ReportData.IsDictionary)
|
||||
return FormattedValue.Unhandled;
|
||||
|
||||
if (!_specs.TryGetFirst(s => runningGameId.EqualsAnyIgnoreCase(s.TitleIds), out GameSpec spec))
|
||||
|
||||
if (!TryGetSpec(runningGameId, out GameSpec spec))
|
||||
return FormattedValue.Unhandled;
|
||||
|
||||
foreach (FormatterSpecBase formatSpec in spec.ValueFormatters.OrderBy(x => x.Priority))
|
||||
{
|
||||
if (!formatSpec.Format(appMeta, playReport, out FormattedValue value))
|
||||
if (!formatSpec.TryFormat(appMeta, playReport, out FormattedValue value))
|
||||
continue;
|
||||
|
||||
return value;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Gommon;
|
||||
using Humanizer;
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
@@ -18,6 +19,9 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
< -201d => "Exploring the Depths",
|
||||
_ => "Roaming Hyrule"
|
||||
};
|
||||
|
||||
private static FormattedValue SkywardSwordHD_Rupees(SingleValue value)
|
||||
=> "rupee".ToQuantity(value.Matched.IntValue);
|
||||
|
||||
private static FormattedValue SuperMarioOdyssey_AssistMode(SingleValue value)
|
||||
=> value.Matched.BoxedValue is 1 ? "Playing in Assist Mode" : "Playing in Regular Mode";
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
{
|
||||
public static partial class PlayReports
|
||||
{
|
||||
public static Analyzer Analyzer { get; } = new Analyzer()
|
||||
public static void Initialize()
|
||||
{
|
||||
// init lazy value
|
||||
_ = Analyzer;
|
||||
}
|
||||
|
||||
public static Analyzer Analyzer => _analyzerLazy.Value;
|
||||
|
||||
private static readonly Lazy<Analyzer> _analyzerLazy = new(() => new Analyzer()
|
||||
.AddSpec(
|
||||
"01007ef00011e000",
|
||||
spec => spec
|
||||
.WithDescription("based on being in Master Mode.")
|
||||
.AddValueFormatter("IsHardMode", BreathOfTheWild_MasterMode)
|
||||
// reset to normal status when switching between normal & master mode in title screen
|
||||
.AddValueFormatter("AoCVer", FormattedValue.SingleAlwaysResets)
|
||||
@@ -13,34 +24,49 @@
|
||||
.AddSpec(
|
||||
"0100f2c0115b6000",
|
||||
spec => spec
|
||||
.WithDescription("based on where you are in Hyrule (Depths, Surface, Sky).")
|
||||
.AddValueFormatter("PlayerPosY", TearsOfTheKingdom_CurrentField))
|
||||
.AddSpec(
|
||||
"01002da013484000",
|
||||
spec => spec
|
||||
.WithDescription("based on how many Rupees you have.")
|
||||
.AddValueFormatter("rupees", SkywardSwordHD_Rupees))
|
||||
.AddSpec(
|
||||
"0100000000010000",
|
||||
spec =>
|
||||
spec.AddValueFormatter("is_kids_mode", SuperMarioOdyssey_AssistMode)
|
||||
spec => spec
|
||||
.WithDescription("based on if you're playing with Assist Mode.")
|
||||
.AddValueFormatter("is_kids_mode", SuperMarioOdyssey_AssistMode)
|
||||
)
|
||||
.AddSpec(
|
||||
"010075000ecbe000",
|
||||
spec =>
|
||||
spec.AddValueFormatter("is_kids_mode", SuperMarioOdysseyChina_AssistMode)
|
||||
spec => spec
|
||||
.WithDescription("based on if you're playing with Assist Mode.")
|
||||
.AddValueFormatter("is_kids_mode", SuperMarioOdysseyChina_AssistMode)
|
||||
)
|
||||
.AddSpec(
|
||||
"010028600ebda000",
|
||||
spec => spec.AddValueFormatter("mode", SuperMario3DWorldOrBowsersFury)
|
||||
spec => spec
|
||||
.WithDescription("based on being in either Super Mario 3D World or Bowser's Fury.")
|
||||
.AddValueFormatter("mode", SuperMario3DWorldOrBowsersFury)
|
||||
)
|
||||
.AddSpec( // Global & China IDs
|
||||
["0100152000022000", "010075100e8ec000"],
|
||||
spec => spec.AddValueFormatter("To", MarioKart8Deluxe_Mode)
|
||||
spec => spec
|
||||
.WithDescription(
|
||||
"based on what modes you're selecting in the menu & whether or not you're in a race.")
|
||||
.AddValueFormatter("To", MarioKart8Deluxe_Mode)
|
||||
)
|
||||
.AddSpec(
|
||||
["0100a3d008c5c000", "01008f6008c5e000"],
|
||||
spec => spec
|
||||
.WithDescription("based on what area of Paldea you're exploring.")
|
||||
.AddValueFormatter("area_no", PokemonSVArea)
|
||||
.AddValueFormatter("team_circle", PokemonSVUnionCircle)
|
||||
)
|
||||
.AddSpec(
|
||||
"01006a800016e000",
|
||||
spec => spec
|
||||
.WithDescription("based on what mode you're playing, who won, and what characters were present.")
|
||||
.AddSparseMultiValueFormatter(
|
||||
[
|
||||
// Metadata to figure out what PlayReport we have.
|
||||
@@ -58,10 +84,15 @@
|
||||
)
|
||||
.AddSpec(
|
||||
[
|
||||
"0100c9a00ece6000", "01008d300c50c000", "0100d870045b6000",
|
||||
"010012f017576000", "0100c62011050000", "0100b3c014bda000"],
|
||||
spec => spec.AddValueFormatter("launch_title_id", NsoEmulator_LaunchedGame)
|
||||
);
|
||||
"0100c9a00ece6000", "01008d300c50c000", "0100d870045b6000",
|
||||
"010012f017576000", "0100c62011050000", "0100b3c014bda000"
|
||||
],
|
||||
spec => spec
|
||||
.WithDescription(
|
||||
"based on what game you first launch.\n\nNSO emulators do not print any Play Report information past the first game launch so it's all we got.")
|
||||
.AddValueFormatter("launch_title_id", NsoEmulator_LaunchedGame)
|
||||
)
|
||||
);
|
||||
|
||||
private static string Playing(string game) => $"Playing {game}";
|
||||
}
|
||||
|
||||
@@ -23,6 +23,20 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
|
||||
public required string[] TitleIds { get; init; }
|
||||
|
||||
public const string DefaultDescription = "Formats the details on your Discord presence based on logged data from the game.";
|
||||
|
||||
private string _valueDescription;
|
||||
|
||||
public string Description => _valueDescription ?? DefaultDescription;
|
||||
|
||||
public GameSpec WithDescription(string description)
|
||||
{
|
||||
_valueDescription = description != null
|
||||
? $"Formats the details on your Discord presence {description}"
|
||||
: null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<FormatterSpecBase> ValueFormatters { get; } = [];
|
||||
|
||||
|
||||
@@ -197,7 +211,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
||||
public string[] ReportKeys { get; init; }
|
||||
public Delegate Formatter { get; init; }
|
||||
|
||||
public bool Format(ApplicationMetadata appMeta, Horizon.Prepo.Types.PlayReport playReport,
|
||||
public bool TryFormat(ApplicationMetadata appMeta, Horizon.Prepo.Types.PlayReport playReport,
|
||||
out FormattedValue formattedValue)
|
||||
{
|
||||
formattedValue = default;
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Ryujinx.Ava.Utilities
|
||||
public static class ShortcutHelper
|
||||
{
|
||||
[SupportedOSPlatform("windows")]
|
||||
private static void CreateShortcutWindows(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string cleanedAppName, string desktopPath, string args = "")
|
||||
private static void CreateShortcutWindows(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string cleanedAppName, string desktopPath)
|
||||
{
|
||||
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName + ".exe");
|
||||
iconPath += ".ico";
|
||||
@@ -22,13 +22,13 @@ namespace Ryujinx.Ava.Utilities
|
||||
image.Resize(new SKImageInfo(128, 128), SKFilterQuality.High);
|
||||
SaveBitmapAsIcon(image, iconPath);
|
||||
|
||||
Shortcut shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath, applicationId, args), iconPath, 0);
|
||||
Shortcut shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath, applicationId), iconPath, 0);
|
||||
shortcut.StringData.NameString = cleanedAppName;
|
||||
shortcut.WriteToFile(Path.Combine(desktopPath, cleanedAppName + ".lnk"));
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("linux")]
|
||||
private static void CreateShortcutLinux(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string desktopPath, string cleanedAppName, string args = "")
|
||||
private static void CreateShortcutLinux(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string desktopPath, string cleanedAppName)
|
||||
{
|
||||
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.sh");
|
||||
string desktopFile = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-template.desktop");
|
||||
@@ -40,11 +40,11 @@ namespace Ryujinx.Ava.Utilities
|
||||
data.SaveTo(file);
|
||||
|
||||
using StreamWriter outputFile = new(Path.Combine(desktopPath, cleanedAppName + ".desktop"));
|
||||
outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath, applicationId, args)}");
|
||||
outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath, applicationId)}");
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("macos")]
|
||||
private static void CreateShortcutMacos(string appFilePath, string applicationId, byte[] iconData, string desktopPath, string cleanedAppName, string args = "")
|
||||
private static void CreateShortcutMacos(string appFilePath, string applicationId, byte[] iconData, string desktopPath, string cleanedAppName)
|
||||
{
|
||||
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx");
|
||||
string plistFile = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-template.plist");
|
||||
@@ -63,7 +63,7 @@ namespace Ryujinx.Ava.Utilities
|
||||
string scriptPath = Path.Combine(scriptFolderPath, ScriptName);
|
||||
using StreamWriter scriptFile = new(scriptPath);
|
||||
|
||||
scriptFile.Write(shortcutScript, basePath, GetArgsString(appFilePath, applicationId, args));
|
||||
scriptFile.Write(shortcutScript, basePath, GetArgsString(appFilePath, applicationId));
|
||||
|
||||
// Set execute permission
|
||||
FileInfo fileInfo = new(scriptPath);
|
||||
@@ -87,7 +87,7 @@ namespace Ryujinx.Ava.Utilities
|
||||
outputFile.Write(plistFile, ScriptName, cleanedAppName, IconName);
|
||||
}
|
||||
|
||||
public static void CreateAppShortcut(string applicationFilePath, string applicationName, string applicationId, byte[] iconData, string args = "")
|
||||
public static void CreateAppShortcut(string applicationFilePath, string applicationName, string applicationId, byte[] iconData)
|
||||
{
|
||||
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
|
||||
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");
|
||||
|
||||
CreateShortcutWindows(applicationFilePath, applicationId, iconData, iconPath, cleanedAppName, desktopPath, args);
|
||||
CreateShortcutWindows(applicationFilePath, applicationId, iconData, iconPath, cleanedAppName, desktopPath);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -106,14 +106,14 @@ namespace Ryujinx.Ava.Utilities
|
||||
string iconPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "icons", "Ryujinx");
|
||||
|
||||
Directory.CreateDirectory(iconPath);
|
||||
CreateShortcutLinux(applicationFilePath, applicationId, iconData, Path.Combine(iconPath, applicationId), desktopPath, cleanedAppName, args);
|
||||
CreateShortcutLinux(applicationFilePath, applicationId, iconData, Path.Combine(iconPath, applicationId), desktopPath, cleanedAppName);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
CreateShortcutMacos(applicationFilePath, applicationId, iconData, desktopPath, cleanedAppName, args);
|
||||
CreateShortcutMacos(applicationFilePath, applicationId, iconData, desktopPath, cleanedAppName);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ namespace Ryujinx.Ava.Utilities
|
||||
throw new NotImplementedException("Shortcut support has not been implemented yet for this OS.");
|
||||
}
|
||||
|
||||
private static string GetArgsString(string appFilePath, string applicationId, string config = "")
|
||||
private static string GetArgsString(string appFilePath, string applicationId)
|
||||
{
|
||||
// args are first defined as a list, for easier adjustments in the future
|
||||
List<string> argsList = [];
|
||||
@@ -132,11 +132,6 @@ namespace Ryujinx.Ava.Utilities
|
||||
argsList.Add($"\"{CommandLineState.BaseDirPathArg}\"");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(config))
|
||||
{
|
||||
argsList.Add(config);
|
||||
}
|
||||
|
||||
if (appFilePath.ToLower().EndsWith(".xci"))
|
||||
{
|
||||
argsList.Add("--application-id");
|
||||
|
||||
Reference in New Issue
Block a user