Compare commits

..

2 Commits

Author SHA1 Message Date
Frog Business
98d798d0cb Merge f73ffd1fd0 into ba0cd13cff 2025-02-12 07:06:04 -06:00
Barış Hamil
f73ffd1fd0 Ability to assign hotkeys to cycle controllers for players 2025-02-01 12:30:51 +03:00
44 changed files with 993 additions and 1344 deletions

View File

@@ -29,7 +29,7 @@ env:
jobs: jobs:
tag: tag:
name: Create tag name: Create tag
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
steps: steps:
- name: Get version info - name: Get version info
id: version_info id: version_info
@@ -202,7 +202,7 @@ jobs:
macos_release: macos_release:
name: Release MacOS universal name: Release MacOS universal
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

View File

@@ -18,7 +18,7 @@ env:
jobs: jobs:
tag: tag:
name: Create tag name: Create tag
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
steps: steps:
- name: Get version info - name: Get version info
id: version_info id: version_info
@@ -183,7 +183,7 @@ jobs:
macos_release: macos_release:
name: Release MacOS universal name: Release MacOS universal
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

View File

@@ -39,12 +39,12 @@
<p align="center"> <p align="center">
Click below to join the Discord: Click below to join the Discord:
<br> <br>
<a href="https://discord.gg/PEuzjrFXUA"> <a href="https://discord.gg/dHPrkBkkyA">
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord"> <img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord">
</a> </a>
<br> <br>
<br> <br>
<img src="https://raw.githubusercontent.com/Ryubing/Ryujinx/refs/heads/master/docs/shell.png"> <img src="https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/docs/shell.png">
</p> </p>
## Usage ## Usage

View File

@@ -969,7 +969,6 @@
0100751007ADA000,"Don't Starve: Nintendo Switch Edition",nvdec,playable,2022-02-05 20:43:34 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 010088B010DD2000,"Dongo Adventure",,playable,2022-10-04 16:22:26
0100C1F0051B6000,"Donkey Kong Country™: Tropical Freeze",,playable,2024-08-05 16:46:10 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 0100F2C00F060000,"Doodle Derby",,boots,2020-12-04 22:51:48
0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07 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 010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19
@@ -1250,7 +1249,7 @@
0100A6B00D4EC000,"Furwind",,playable,2021-02-19 19:44:08 0100A6B00D4EC000,"Furwind",,playable,2021-02-19 19:44:08
0100ECE00C0C4000,"Fury Unleashed",crash;services,ingame,2020-10-18 11:52:40 0100ECE00C0C4000,"Fury Unleashed",crash;services,ingame,2020-10-18 11:52:40
010070000ED9E000,"Fury Unleashed Demo",,playable,2020-10-08 20:09:21 010070000ED9E000,"Fury Unleashed Demo",,playable,2020-10-08 20:09:21
0100E1F013674000,"FUSER™",nvdec;UE4;slow;gpu,ingame,2025-02-12 16:03:00 0100E1F013674000,"FUSER™",nvdec;UE4,playable,2022-10-17 20:58:32
0100A7A015E4C000,"Fushigi no Gensokyo Lotus Labyrinth",Needs Update;audio;gpu;nvdec,ingame,2021-01-20 15:30:02 0100A7A015E4C000,"Fushigi no Gensokyo Lotus Labyrinth",Needs Update;audio;gpu;nvdec,ingame,2021-01-20 15:30:02
01003C300B274000,"Futari de! Nyanko Daisensou",,playable,2024-01-05 22:26:52 01003C300B274000,"Futari de! Nyanko Daisensou",,playable,2024-01-05 22:26:52
010055801134E000,"FUZE Player",online-broken;vulkan-backend-bug,ingame,2022-10-18 12:23:53 010055801134E000,"FUZE Player",online-broken;vulkan-backend-bug,ingame,2022-10-18 12:23:53
@@ -2989,8 +2988,8 @@
010015D003EE4000,"The Jackbox Party Pack 2",online-working,playable,2022-08-22 18:23:40 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 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 0100E1F003EE8000,"The Jackbox Party Pack 4",online-working,playable,2022-08-22 18:56:34
01006fe0096ac000,"The Jackbox Party Pack 5",slow;online-working,ingame,2025-02-14 05:32:00 01006fe0096ac000,"The Jackbox Party Pack 5",ldn-untested,boots,2025-02-03 22:32:00
01005a400db52000,"The Jackbox Party Pack 6",slow;online-working,ingame,2025-02-14 05:26:00 01005a400db52000,"The Jackbox Party Pack 6",ldn-untested,boots,2025-02-03 22:32:00
010052C00B184000,"The Journey Down: Chapter One",nvdec,playable,2021-02-24 13:32:41 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 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 01009AB00B186000,"The Journey Down: Chapter Two",nvdec,playable,2021-02-24 13:32:13
1 title_id game_name labels status last_updated
969 0100751007ADA000 Don't Starve: Nintendo Switch Edition nvdec playable 2022-02-05 20:43:34
970 010088B010DD2000 Dongo Adventure playable 2022-10-04 16:22:26
971 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
972 0100F2C00F060000 Doodle Derby boots 2020-12-04 22:51:48
973 0100416004C00000 DOOM gpu;slow;nvdec;online-broken ingame 2024-09-23 15:40:07
974 010018900DD00000 DOOM (1993) nvdec;online-broken menus 2022-09-06 13:32:19
1249 0100A6B00D4EC000 Furwind playable 2021-02-19 19:44:08
1250 0100ECE00C0C4000 Fury Unleashed crash;services ingame 2020-10-18 11:52:40
1251 010070000ED9E000 Fury Unleashed Demo playable 2020-10-08 20:09:21
1252 0100E1F013674000 FUSER™ nvdec;UE4;slow;gpu nvdec;UE4 ingame playable 2025-02-12 16:03:00 2022-10-17 20:58:32
1253 0100A7A015E4C000 Fushigi no Gensokyo Lotus Labyrinth Needs Update;audio;gpu;nvdec ingame 2021-01-20 15:30:02
1254 01003C300B274000 Futari de! Nyanko Daisensou playable 2024-01-05 22:26:52
1255 010055801134E000 FUZE Player online-broken;vulkan-backend-bug ingame 2022-10-18 12:23:53
2988 010015D003EE4000 The Jackbox Party Pack 2 online-working playable 2022-08-22 18:23:40
2989 0100CC80013D6000 The Jackbox Party Pack 3 slow;online-working playable 2022-08-22 18:41:06
2990 0100E1F003EE8000 The Jackbox Party Pack 4 online-working playable 2022-08-22 18:56:34
2991 01006fe0096ac000 The Jackbox Party Pack 5 slow;online-working ldn-untested ingame boots 2025-02-14 05:32:00 2025-02-03 22:32:00
2992 01005a400db52000 The Jackbox Party Pack 6 slow;online-working ldn-untested ingame boots 2025-02-14 05:26:00 2025-02-03 22:32:00
2993 010052C00B184000 The Journey Down: Chapter One nvdec playable 2021-02-24 13:32:41
2994 01006BC00B188000 The Journey Down: Chapter Three nvdec playable 2021-02-24 13:45:27
2995 01009AB00B186000 The Journey Down: Chapter Two nvdec playable 2021-02-24 13:32:13

View File

@@ -186,7 +186,7 @@ namespace ARMeilleure.Translation.Cache
int newRegionNumber = _activeRegionIndex; int newRegionNumber = _activeRegionIndex;
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation)."); Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
_cacheAllocator = new CacheMemoryAllocator(CacheSize); _cacheAllocator = new CacheMemoryAllocator(CacheSize);

View File

@@ -1,3 +1,5 @@
using System.Collections.Generic;
namespace Ryujinx.Common.Configuration.Hid namespace Ryujinx.Common.Configuration.Hid
{ {
public class KeyboardHotkeys public class KeyboardHotkeys
@@ -13,5 +15,6 @@ namespace Ryujinx.Common.Configuration.Hid
public Key VolumeDown { get; set; } public Key VolumeDown { get; set; }
public Key CustomVSyncIntervalIncrement { get; set; } public Key CustomVSyncIntervalIncrement { get; set; }
public Key CustomVSyncIntervalDecrement { get; set; } public Key CustomVSyncIntervalDecrement { get; set; }
public List<Key> CycleControllers { get; set; }
} }
} }

View File

@@ -166,7 +166,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
int newRegionNumber = _activeRegionIndex; int newRegionNumber = _activeRegionIndex;
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation)."); Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
_cacheAllocator = new CacheMemoryAllocator(CacheSize); _cacheAllocator = new CacheMemoryAllocator(CacheSize);

View File

@@ -34,10 +34,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{ {
if (errorCode != LinuxError.SUCCESS) if (errorCode != LinuxError.SUCCESS)
{ {
if (errorCode != LinuxError.EWOULDBLOCK)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Operation failed with error {errorCode}.");
}
result = -1; result = -1;
} }
@@ -70,8 +66,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
BsdSocketType type = (BsdSocketType)context.RequestData.ReadInt32(); BsdSocketType type = (BsdSocketType)context.RequestData.ReadInt32();
ProtocolType protocol = (ProtocolType)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); BsdSocketCreationFlags creationFlags = (BsdSocketCreationFlags)((int)type >> (int)BsdSocketCreationFlags.FlagsShift);
type &= BsdSocketType.TypeMask; type &= BsdSocketType.TypeMask;
@@ -101,21 +95,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
} }
} }
LinuxError errno = LinuxError.SUCCESS; ISocket newBsdSocket = new ManagedSocket(netDomain, (SocketType)type, protocol, context.Device.Configuration.MultiplayerLanInterfaceId)
ISocket newBsdSocket; {
Blocking = !creationFlags.HasFlag(BsdSocketCreationFlags.NonBlocking),
};
try LinuxError errno = LinuxError.SUCCESS;
{
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); int newSockFd = _context.RegisterFileDescriptor(newBsdSocket);
@@ -126,7 +111,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
if (exempt) if (exempt)
{ {
Logger.Info?.Print(LogClass.ServiceBsd, "Disconnecting exempt socket.");
newBsdSocket.Disconnect(); newBsdSocket.Disconnect();
} }
@@ -813,10 +797,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{ {
errno = socket.Listen(backlog); errno = socket.Listen(backlog);
} }
else
{
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, $"Invalid socket fd '{socketFd}'.");
}
return WriteBsdResult(context, 0, errno); return WriteBsdResult(context, 0, errno);
} }

View File

@@ -92,30 +92,18 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
{ {
newSocket = new ManagedSocket(Socket.Accept()); 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; return LinuxError.SUCCESS;
} }
catch (SocketException exception) catch (SocketException exception)
{ {
newSocket = null; newSocket = null;
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
public LinuxError Bind(IPEndPoint localEndPoint) public LinuxError Bind(IPEndPoint localEndPoint)
{ {
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket binding to: {ProtocolType}/{localEndPoint.Port}");
try try
{ {
Socket.Bind(localEndPoint); Socket.Bind(localEndPoint);
@@ -124,10 +112,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
@@ -139,15 +123,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
public LinuxError Connect(IPEndPoint remoteEndPoint) 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 try
{ {
Socket.Connect(remoteEndPoint); Socket.Connect(remoteEndPoint);
@@ -162,10 +137,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
else else
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
@@ -173,13 +144,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
public void Disconnect() public void Disconnect()
{ {
Logger.Info?.Print(LogClass.ServiceBsd, "Socket disconnecting");
Socket.Disconnect(true); Socket.Disconnect(true);
} }
public void Dispose() public void Dispose()
{ {
Logger.Info?.Print(LogClass.ServiceBsd, "Socket closed");
Socket.Close(); Socket.Close();
Socket.Dispose(); Socket.Dispose();
} }
@@ -190,16 +159,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
{ {
Socket.Listen(backlog); Socket.Listen(backlog);
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket listening: {ProtocolType}/{(Socket.LocalEndPoint as IPEndPoint).Port}");
return LinuxError.SUCCESS; return LinuxError.SUCCESS;
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
@@ -219,15 +182,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
private bool _hasEmittedBlockingWarning; bool hasEmittedBlockingWarning = false;
public LinuxError Receive(out int receiveSize, Span<byte> buffer, BsdSocketFlags flags) public LinuxError Receive(out int receiveSize, Span<byte> buffer, BsdSocketFlags flags)
{ {
@@ -243,10 +202,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
shouldBlockAfterOperation = true; shouldBlockAfterOperation = true;
} }
if (Blocking && !_hasEmittedBlockingWarning) if (Blocking && !hasEmittedBlockingWarning)
{ {
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors."); 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)); receiveSize = Socket.Receive(buffer, ConvertBsdSocketFlags(flags));
@@ -255,10 +214,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
receiveSize = -1; receiveSize = -1;
result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode); result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
@@ -290,10 +245,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
shouldBlockAfterOperation = true; shouldBlockAfterOperation = true;
} }
if (Blocking && !_hasEmittedBlockingWarning) if (Blocking && !hasEmittedBlockingWarning)
{ {
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors."); Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors.");
_hasEmittedBlockingWarning = true; hasEmittedBlockingWarning = true;
} }
if (!Socket.IsBound) if (!Socket.IsBound)
@@ -310,10 +265,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
receiveSize = -1; receiveSize = -1;
result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode); result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
@@ -337,10 +288,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
sendSize = -1; sendSize = -1;
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
@@ -357,10 +304,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
sendSize = -1; sendSize = -1;
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
@@ -398,10 +341,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
@@ -448,10 +387,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
@@ -584,10 +519,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
@@ -626,10 +557,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }

View File

@@ -1,6 +1,4 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy; using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -66,18 +64,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy
{ {
if (_proxy.Supported(domain, type, protocol)) if (_proxy.Supported(domain, type, protocol))
{ {
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket is using LDN proxy");
return new LdnProxySocket(domain, type, protocol, _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); return new DefaultSocket(domain, type, protocol, lanInterfaceId);
} }
} }

View File

@@ -40,6 +40,7 @@ using Ryujinx.HLE;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.Input; using Ryujinx.Input;
using Ryujinx.Input.HLE; using Ryujinx.Input.HLE;
@@ -49,6 +50,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -517,7 +519,7 @@ namespace Ryujinx.Ava
Device?.System.ChangeDockedModeState(e.NewValue); Device?.System.ChangeDockedModeState(e.NewValue);
} }
public void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e) private void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e)
{ {
Device?.SetVolume(e.NewValue); Device?.SetVolume(e.NewValue);
@@ -1309,6 +1311,18 @@ namespace Ryujinx.Ava
_viewModel.Volume = Device.GetVolume(); _viewModel.Volume = Device.GetVolume();
break; break;
case KeyboardHotkeyState.CycleControllersPlayer1:
case KeyboardHotkeyState.CycleControllersPlayer2:
case KeyboardHotkeyState.CycleControllersPlayer3:
case KeyboardHotkeyState.CycleControllersPlayer4:
case KeyboardHotkeyState.CycleControllersPlayer5:
case KeyboardHotkeyState.CycleControllersPlayer6:
case KeyboardHotkeyState.CycleControllersPlayer7:
case KeyboardHotkeyState.CycleControllersPlayer8:
var player = currentHotkeyState - KeyboardHotkeyState.CycleControllersPlayer1;
var ivm = new UI.ViewModels.Input.InputViewModel();
Dispatcher.UIThread.Invoke(() => ivm.CyclePlayerDevice(player));
break;
case KeyboardHotkeyState.None: case KeyboardHotkeyState.None:
(_keyboardInterface as AvaloniaKeyboard).Clear(); (_keyboardInterface as AvaloniaKeyboard).Clear();
break; break;
@@ -1391,6 +1405,15 @@ namespace Ryujinx.Ava
state = KeyboardHotkeyState.CustomVSyncIntervalDecrement; state = KeyboardHotkeyState.CustomVSyncIntervalDecrement;
} }
foreach (var cycle in ConfigurationState.Instance.Hid.Hotkeys.Value.CycleControllers?.Select((value, index) => (value, index)) ?? [])
{
if (_keyboardInterface.IsPressed((Key)cycle.value))
{
state = KeyboardHotkeyState.CycleControllersPlayer1 + cycle.index;
break;
}
}
return state; return state;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -14,5 +14,13 @@ namespace Ryujinx.Ava.Common
VolumeDown, VolumeDown,
CustomVSyncIntervalIncrement, CustomVSyncIntervalIncrement,
CustomVSyncIntervalDecrement, CustomVSyncIntervalDecrement,
CycleControllersPlayer1,
CycleControllersPlayer2,
CycleControllersPlayer3,
CycleControllersPlayer4,
CycleControllersPlayer5,
CycleControllersPlayer6,
CycleControllersPlayer7,
CycleControllersPlayer8
} }
} }

View File

@@ -0,0 +1,14 @@
using System;
namespace Ryujinx.Ava.Common
{
public static class ThemeManager
{
public static event Action ThemeChanged;
public static void OnThemeChanged()
{
ThemeChanged?.Invoke();
}
}
}

View File

@@ -24,7 +24,7 @@ namespace Ryujinx.Ava
private static readonly string _description = private static readonly string _description =
ReleaseInformation.IsValid ReleaseInformation.IsValid
? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}" ? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}@{ReleaseInformation.BuildGitHash}"
: "dev build"; : "dev build";
private const string ApplicationId = "1293250299716173864"; private const string ApplicationId = "1293250299716173864";
@@ -56,7 +56,6 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.EnableDiscordIntegration.Event += Update; ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
TitleIDs.CurrentApplication.Event += (_, e) => Use(e.NewValue); TitleIDs.CurrentApplication.Event += (_, e) => Use(e.NewValue);
HorizonStatic.PlayReport += HandlePlayReport; HorizonStatic.PlayReport += HandlePlayReport;
PlayReports.Initialize();
} }
private static void Update(object sender, ReactiveEventArgs<bool> evnt) private static void Update(object sender, ReactiveEventArgs<bool> evnt)

View File

@@ -148,7 +148,7 @@ namespace Ryujinx.Headless
IgnoreMissingServices = configurationState.System.IgnoreMissingServices; IgnoreMissingServices = configurationState.System.IgnoreMissingServices;
if (NeedsOverride(nameof(IgnoreControllerApplet))) if (NeedsOverride(nameof(IgnoreControllerApplet)))
IgnoreControllerApplet = configurationState.System.IgnoreControllerApplet; IgnoreControllerApplet = configurationState.System.IgnoreApplet;
return; return;

View File

@@ -22,8 +22,6 @@ namespace Ryujinx.Ava
{ {
public class RyujinxApp : Application public class RyujinxApp : Application
{ {
public static event Action ThemeChanged;
internal static string FormatTitle(LocaleKeys? windowTitleKey = null, bool includeVersion = true) internal static string FormatTitle(LocaleKeys? windowTitleKey = null, bool includeVersion = true)
=> windowTitleKey is null => windowTitleKey is null
? $"{FullAppName}{(includeVersion ? $" {Program.Version}" : string.Empty)}" ? $"{FullAppName}{(includeVersion ? $" {Program.Version}" : string.Empty)}"
@@ -114,7 +112,7 @@ namespace Ryujinx.Ava
baseStyle = ConfigurationState.Instance.UI.BaseStyle; baseStyle = ConfigurationState.Instance.UI.BaseStyle;
} }
ThemeChanged?.Invoke(); ThemeManager.OnThemeChanged();
RequestedThemeVariant = baseStyle switch RequestedThemeVariant = baseStyle switch
{ {

View File

@@ -41,7 +41,7 @@ namespace Ryujinx.Ava.UI.Applet
bool okPressed = false; bool okPressed = false;
if (ConfigurationState.Instance.System.IgnoreControllerApplet) if (ConfigurationState.Instance.System.IgnoreApplet)
return false; return false;
Dispatcher.UIThread.InvokeAsync(async () => Dispatcher.UIThread.InvokeAsync(async () =>

View File

@@ -119,23 +119,17 @@
TextWrapping="Wrap" > TextWrapping="Wrap" >
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal" Spacing="5" ToolTip.Tip="{Binding DynamicRichPresenceDescription}"> <StackPanel Orientation="Horizontal" Spacing="5">
<ui:SymbolIcon <ui:SymbolIcon Foreground="ForestGreen" Symbol="Checkmark" IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"/>
Foreground="ForestGreen"
Symbol="Checkmark"
IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"/>
<TextBlock <TextBlock
Foreground="ForestGreen" Foreground="ForestGreen"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}" IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"
Text="{ext:Locale GameInfoRpcDynamic}" Text="{ext:Locale GameInfoRpcDynamic}"
TextAlignment="Start" TextAlignment="Start"
TextWrapping="Wrap"> TextWrapping="Wrap" >
</TextBlock> </TextBlock>
<ui:SymbolIcon <ui:SymbolIcon Foreground="Red" Symbol="Cancel" IsVisible="{Binding !AppData.HasDynamicRichPresenceSupport}"/>
Foreground="Red"
Symbol="Cancel"
IsVisible="{Binding !AppData.HasDynamicRichPresenceSupport}"/>
<TextBlock <TextBlock
Foreground="Red" Foreground="Red"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"

View File

@@ -1,6 +1,11 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using DynamicData;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;
namespace Ryujinx.Ava.UI.Models.Input namespace Ryujinx.Ava.UI.Models.Input
{ {
@@ -28,8 +33,15 @@ namespace Ryujinx.Ava.UI.Models.Input
[ObservableProperty] private Key _customVSyncIntervalDecrement; [ObservableProperty] private Key _customVSyncIntervalDecrement;
public ObservableCollection<CycleController> CycleControllers { get; set; } = new ObservableCollection<CycleController>();
public ICommand AddCycleController { get; set; }
public ICommand RemoveCycleController { get; set; }
public bool CanRemoveCycleController => CycleControllers.Count > 0 && CycleControllers.Count < 8;
public HotkeyConfig(KeyboardHotkeys config) public HotkeyConfig(KeyboardHotkeys config)
{ {
AddCycleController = MiniCommand.Create(() => CycleControllers.Add(new CycleController(CycleControllers.Count + 1, Key.Unbound)));
RemoveCycleController = MiniCommand.Create(() => CycleControllers.Remove(CycleControllers.Last()));
if (config == null) if (config == null)
return; return;
@@ -44,6 +56,7 @@ namespace Ryujinx.Ava.UI.Models.Input
VolumeDown = config.VolumeDown; VolumeDown = config.VolumeDown;
CustomVSyncIntervalIncrement = config.CustomVSyncIntervalIncrement; CustomVSyncIntervalIncrement = config.CustomVSyncIntervalIncrement;
CustomVSyncIntervalDecrement = config.CustomVSyncIntervalDecrement; CustomVSyncIntervalDecrement = config.CustomVSyncIntervalDecrement;
CycleControllers.AddRange((config.CycleControllers ?? []).Select((x, i) => new CycleController(i + 1, x)));
} }
public KeyboardHotkeys GetConfig() => public KeyboardHotkeys GetConfig() =>
@@ -60,6 +73,7 @@ namespace Ryujinx.Ava.UI.Models.Input
VolumeDown = VolumeDown, VolumeDown = VolumeDown,
CustomVSyncIntervalIncrement = CustomVSyncIntervalIncrement, CustomVSyncIntervalIncrement = CustomVSyncIntervalIncrement,
CustomVSyncIntervalDecrement = CustomVSyncIntervalDecrement, CustomVSyncIntervalDecrement = CustomVSyncIntervalDecrement,
CycleControllers = CycleControllers.Select(x => x.Hotkey).ToList()
}; };
} }
} }

View File

@@ -25,10 +25,10 @@ namespace Ryujinx.Ava.UI.ViewModels
Version = RyujinxApp.FullAppName + "\n" + Program.Version; Version = RyujinxApp.FullAppName + "\n" + Program.Version;
UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value); UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value);
RyujinxApp.ThemeChanged += Ryujinx_ThemeChanged; ThemeManager.ThemeChanged += ThemeManager_ThemeChanged;
} }
private void Ryujinx_ThemeChanged() private void ThemeManager_ThemeChanged()
{ {
Dispatcher.UIThread.Post(() => UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value)); Dispatcher.UIThread.Post(() => UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value));
} }
@@ -49,7 +49,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public void Dispose() public void Dispose()
{ {
RyujinxApp.ThemeChanged -= Ryujinx_ThemeChanged; ThemeManager.ThemeChanged -= ThemeManager_ThemeChanged;
GithubLogo.Dispose(); GithubLogo.Dispose();
DiscordLogo.Dispose(); DiscordLogo.Dispose();

View File

@@ -1,7 +1,6 @@
using Gommon; using Gommon;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Utilities.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.PlayReport;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
@@ -11,11 +10,6 @@ namespace Ryujinx.Ava.UI.ViewModels
public ApplicationDataViewModel(ApplicationData appData) => AppData = appData; 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 FormattedVersion => LocaleManager.Instance[LocaleKeys.GameListHeaderVersion].Format(AppData.Version);
public string FormattedDeveloper => LocaleManager.Instance[LocaleKeys.GameListHeaderDeveloper].Format(AppData.Developer); public string FormattedDeveloper => LocaleManager.Instance[LocaleKeys.GameListHeaderDeveloper].Format(AppData.Developer);
public string FormattedFileExtension => LocaleManager.Instance[LocaleKeys.GameListHeaderFileExtension].Format(AppData.FileExtension); public string FormattedFileExtension => LocaleManager.Instance[LocaleKeys.GameListHeaderFileExtension].Format(AppData.FileExtension);

View File

@@ -0,0 +1,48 @@
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Common.Configuration.Hid;
namespace Ryujinx.Ava.UI.ViewModels
{
public class CycleController : BaseModel
{
private string _player;
private Key _hotkey;
public string Player
{
get => _player;
set
{
_player = value;
OnPropertyChanged(nameof(Player));
}
}
public Key Hotkey
{
get => _hotkey;
set
{
_hotkey = value;
OnPropertyChanged(nameof(Hotkey));
}
}
public CycleController(int v, Key x)
{
Player = v switch
{
1 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer1],
2 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer2],
3 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer3],
4 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer4],
5 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer5],
6 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer6],
7 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer7],
8 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer8],
_ => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer] + " " + v
};
Hotkey = x;
}
}
}

View File

@@ -897,5 +897,13 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
AvaloniaKeyboardDriver.Dispose(); AvaloniaKeyboardDriver.Dispose();
} }
public void CyclePlayerDevice(int player)
{
LoadDevices();
PlayerId = (PlayerIndex)player;
Device = (Device + 1) % Devices.Count;
Save();
}
} }
} }

View File

@@ -1347,25 +1347,6 @@ namespace Ryujinx.Ava.UI.ViewModels
OpenHelper.OpenFolder(AppDataManager.BaseDirPath); 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() public void OpenLogsFolder()
{ {
string logPath = AppDataManager.GetOrCreateLogsDir(); string logPath = AppDataManager.GetOrCreateLogsDir();

View File

@@ -49,7 +49,8 @@ namespace Ryujinx.Ava.UI.ViewModels
private int _graphicsBackendMultithreadingIndex; private int _graphicsBackendMultithreadingIndex;
private float _volume; private float _volume;
[ObservableProperty] private bool _isVulkanAvailable = true; [ObservableProperty] private bool _isVulkanAvailable = true;
[ObservableProperty] private bool _gameListNeedsRefresh; [ObservableProperty] private bool _gameDirectoryChanged;
[ObservableProperty] private bool _autoloadDirectoryChanged;
private readonly List<string> _gpuIds = []; private readonly List<string> _gpuIds = [];
private int _graphicsBackendIndex; private int _graphicsBackendIndex;
private int _scalingFilter; private int _scalingFilter;
@@ -127,8 +128,6 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool EnableMouse { get; set; } public bool EnableMouse { get; set; }
public bool DisableInputWhenOutOfFocus { get; set; } public bool DisableInputWhenOutOfFocus { get; set; }
public int FocusLostActionType { get; set; }
public VSyncMode VSyncMode public VSyncMode VSyncMode
{ {
get => _vSyncMode; get => _vSyncMode;
@@ -482,7 +481,6 @@ namespace Ryujinx.Ava.UI.ViewModels
ShowTitleBar = config.ShowTitleBar; ShowTitleBar = config.ShowTitleBar;
HideCursor = (int)config.HideCursor.Value; HideCursor = (int)config.HideCursor.Value;
UpdateCheckerType = (int)config.UpdateCheckerType.Value; UpdateCheckerType = (int)config.UpdateCheckerType.Value;
FocusLostActionType = (int)config.FocusLostActionType.Value;
GameDirectories.Clear(); GameDirectories.Clear();
GameDirectories.AddRange(config.UI.GameDirs.Value); GameDirectories.AddRange(config.UI.GameDirs.Value);
@@ -526,7 +524,7 @@ namespace Ryujinx.Ava.UI.ViewModels
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks; EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
DramSize = config.System.DramSize; DramSize = config.System.DramSize;
IgnoreMissingServices = config.System.IgnoreMissingServices; IgnoreMissingServices = config.System.IgnoreMissingServices;
IgnoreApplet = config.System.IgnoreControllerApplet; IgnoreApplet = config.System.IgnoreApplet;
// CPU // CPU
EnablePptc = config.System.EnablePtc; EnablePptc = config.System.EnablePtc;
@@ -591,9 +589,16 @@ namespace Ryujinx.Ava.UI.ViewModels
config.ShowTitleBar.Value = ShowTitleBar; config.ShowTitleBar.Value = ShowTitleBar;
config.HideCursor.Value = (HideCursorMode)HideCursor; config.HideCursor.Value = (HideCursorMode)HideCursor;
config.UpdateCheckerType.Value = (UpdaterType)UpdateCheckerType; config.UpdateCheckerType.Value = (UpdaterType)UpdateCheckerType;
config.FocusLostActionType.Value = (FocusLostType)FocusLostActionType;
config.UI.GameDirs.Value = [..GameDirectories]; if (GameDirectoryChanged)
config.UI.AutoloadDirs.Value = [..AutoloadDirectories]; {
config.UI.GameDirs.Value = [..GameDirectories];
}
if (AutoloadDirectoryChanged)
{
config.UI.AutoloadDirs.Value = [..AutoloadDirectories];
}
config.UI.BaseStyle.Value = BaseStyleIndex switch config.UI.BaseStyle.Value = BaseStyleIndex switch
{ {
@@ -614,11 +619,8 @@ namespace Ryujinx.Ava.UI.ViewModels
// System // System
config.System.Region.Value = (Region)Region; config.System.Region.Value = (Region)Region;
if (config.System.Language.Value != (Language)Language)
GameListNeedsRefresh = true;
config.System.Language.Value = (Language)Language; config.System.Language.Value = (Language)Language;
if (_validTzRegions.Contains(TimeZone)) if (_validTzRegions.Contains(TimeZone))
{ {
config.System.TimeZone.Value = TimeZone; config.System.TimeZone.Value = TimeZone;
@@ -629,7 +631,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks; config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
config.System.DramSize.Value = DramSize; config.System.DramSize.Value = DramSize;
config.System.IgnoreMissingServices.Value = IgnoreMissingServices; config.System.IgnoreMissingServices.Value = IgnoreMissingServices;
config.System.IgnoreControllerApplet.Value = IgnoreApplet; config.System.IgnoreApplet.Value = IgnoreApplet;
// CPU // CPU
config.System.EnablePtc.Value = EnablePptc; config.System.EnablePtc.Value = EnablePptc;
@@ -709,7 +711,8 @@ namespace Ryujinx.Ava.UI.ViewModels
SaveSettingsEvent?.Invoke(); SaveSettingsEvent?.Invoke();
GameListNeedsRefresh = false; GameDirectoryChanged = false;
AutoloadDirectoryChanged = false;
} }
private static void RevertIfNotSaved() private static void RevertIfNotSaved()

View File

@@ -66,10 +66,6 @@
Command="{Binding OpenRyujinxFolder}" Command="{Binding OpenRyujinxFolder}"
Header="{ext:Locale MenuBarFileOpenEmuFolder}" Header="{ext:Locale MenuBarFileOpenEmuFolder}"
ToolTip.Tip="{ext:Locale OpenRyujinxFolderTooltip}" /> ToolTip.Tip="{ext:Locale OpenRyujinxFolderTooltip}" />
<MenuItem
Command="{Binding OpenScreenshotsFolder}"
Header="{ext:Locale MenuBarFileOpenScreenshotsFolder}"
ToolTip.Tip="{ext:Locale OpenScreenshotFolderTooltip}"/>
<MenuItem <MenuItem
Command="{Binding OpenLogsFolder}" Command="{Binding OpenLogsFolder}"
Header="{ext:Locale MenuBarFileOpenLogsFolder}" Header="{ext:Locale MenuBarFileOpenLogsFolder}"

View File

@@ -1,4 +1,4 @@
<UserControl <UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsHotkeysView" x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsHotkeysView"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -15,17 +15,18 @@
<viewModels:SettingsViewModel /> <viewModels:SettingsViewModel />
</Design.DataContext> </Design.DataContext>
<UserControl.Styles> <UserControl.Styles>
<Style Selector="StackPanel > StackPanel"> <Style Selector="StackPanel StackPanel">
<Setter Property="Margin" Value="10, 0, 0, 0" /> <Setter Property="Margin" Value="10, 0, 0, 0" />
<Setter Property="Orientation" Value="Horizontal" /> <Setter Property="Orientation" Value="Horizontal" />
</Style> </Style>
<Style Selector="StackPanel > StackPanel > TextBlock"> <Style Selector="StackPanel StackPanel > TextBlock">
<Setter Property="VerticalAlignment" Value="Center" /> <Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Width" Value="230" /> <Setter Property="Width" Value="230" />
</Style> </Style>
<Style Selector="ToggleButton"> <Style Selector="ToggleButton, Button">
<Setter Property="Width" Value="90" /> <Setter Property="Width" Value="90" />
<Setter Property="Height" Value="27" /> <Setter Property="Height" Value="27" />
<Setter Property="Padding" Value="0,5,0,5" /> <!-- Added vertical padding -->
</Style> </Style>
<Style Selector="ToggleButton > TextBlock"> <Style Selector="ToggleButton > TextBlock">
<Setter Property="TextAlignment" Value="Center" /> <Setter Property="TextAlignment" Value="Center" />
@@ -39,79 +40,123 @@
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<Border Classes="settings"> <Border Classes="settings">
<StackPanel <StackPanel
Name="SettingButtons"
Margin="10" Margin="10"
HorizontalAlignment="Stretch"
Orientation="Vertical" Orientation="Vertical"
Spacing="10"> Spacing="10"
Name="SettingButtons">
<TextBlock <TextBlock
Classes="h1" Classes="h1"
Text="{ext:Locale SettingsTabHotkeysHotkeys}" /> Text="{ext:Locale SettingsTabHotkeysHotkeys}" />
<StackPanel> <StackPanel
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleVSyncModeHotkey}" /> Margin="10,0,0,0"
<ToggleButton Name="ToggleVSyncMode"> Spacing="10"
<TextBlock Text="{Binding KeyboardHotkey.ToggleVSyncMode, Converter={x:Static helpers:KeyValueConverter.Instance}}" /> Orientation="Vertical">
</ToggleButton> <StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleVSyncModeHotkey}" />
<ToggleButton Name="ToggleVSyncMode">
<TextBlock Text="{Binding KeyboardHotkey.ToggleVSyncMode, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysScreenshotHotkey}" />
<ToggleButton Name="Screenshot">
<TextBlock Text="{Binding KeyboardHotkey.Screenshot, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysShowUiHotkey}" />
<ToggleButton Name="ShowUI">
<TextBlock Text="{Binding KeyboardHotkey.ShowUI, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysPauseHotkey}" />
<ToggleButton Name="Pause">
<TextBlock Text="{Binding KeyboardHotkey.Pause, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleMuteHotkey}" />
<ToggleButton Name="ToggleMute">
<TextBlock Text="{Binding KeyboardHotkey.ToggleMute, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleUpHotkey}" />
<ToggleButton Name="ResScaleUp">
<TextBlock Text="{Binding KeyboardHotkey.ResScaleUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleDownHotkey}" />
<ToggleButton Name="ResScaleDown">
<TextBlock Text="{Binding KeyboardHotkey.ResScaleDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeUpHotkey}" />
<ToggleButton Name="VolumeUp">
<TextBlock Text="{Binding KeyboardHotkey.VolumeUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeDownHotkey}" />
<ToggleButton Name="VolumeDown">
<TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock Text="{ext:Locale SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey}" />
<ToggleButton Name="CustomVSyncIntervalIncrement">
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalIncrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock Text="{ext:Locale SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey}" />
<ToggleButton Name="CustomVSyncIntervalDecrement">
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalDecrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
</StackPanel> </StackPanel>
<StackPanel> <Separator Height="1" />
<TextBlock Text="{ext:Locale SettingsTabHotkeysScreenshotHotkey}" /> <StackPanel Margin="0">
<ToggleButton Name="Screenshot"> <TextBlock
<TextBlock Text="{Binding KeyboardHotkey.Screenshot, Converter={x:Static helpers:KeyValueConverter.Instance}}" /> Classes="h1"
</ToggleButton> Text="{ext:Locale SettingsTabHotkeysCycleControllers}" />
</StackPanel> <StackPanel Orientation="Horizontal" Spacing="10">
<StackPanel> <Button
<TextBlock Text="{ext:Locale SettingsTabHotkeysShowUiHotkey}" /> Content="{ext:Locale SettingsTabGeneralAdd}"
<ToggleButton Name="ShowUI"> Margin="10,0,0,0"
<TextBlock Text="{Binding KeyboardHotkey.ShowUI, Converter={x:Static helpers:KeyValueConverter.Instance}}" /> Command="{Binding KeyboardHotkey.AddCycleController}" />
</ToggleButton> <Button
</StackPanel> Content="{ext:Locale SettingsTabGeneralRemove}"
<StackPanel> IsEnabled="{Binding KeyboardHotkey.CanRemoveCycleController}"
<TextBlock Text="{ext:Locale SettingsTabHotkeysPauseHotkey}" /> Command="{Binding KeyboardHotkey.RemoveCycleController}" />
<ToggleButton Name="Pause"> </StackPanel>
<TextBlock Text="{Binding KeyboardHotkey.Pause, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleMuteHotkey}" />
<ToggleButton Name="ToggleMute">
<TextBlock Text="{Binding KeyboardHotkey.ToggleMute, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleUpHotkey}" />
<ToggleButton Name="ResScaleUp">
<TextBlock Text="{Binding KeyboardHotkey.ResScaleUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleDownHotkey}" />
<ToggleButton Name="ResScaleDown">
<TextBlock Text="{Binding KeyboardHotkey.ResScaleDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeUpHotkey}" />
<ToggleButton Name="VolumeUp">
<TextBlock Text="{Binding KeyboardHotkey.VolumeUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeDownHotkey}" />
<ToggleButton Name="VolumeDown">
<TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock Text="{ext:Locale SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey}" />
<ToggleButton Name="CustomVSyncIntervalIncrement">
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalIncrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock Text="{ext:Locale SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey}" />
<ToggleButton Name="CustomVSyncIntervalDecrement">
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalDecrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel> </StackPanel>
<ItemsControl ItemsSource="{Binding KeyboardHotkey.CycleControllers}"
Name="CycleControllers">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
Margin="10,0,0,0"
Orientation="Vertical"
Spacing="10" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Player}" />
<ToggleButton>
<TextBlock
Text="{Binding Hotkey, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel> </StackPanel>
</Border> </Border>
</ScrollViewer> </ScrollViewer>

View File

@@ -3,11 +3,14 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Avalonia.VisualTree;
using DynamicData;
using Ryujinx.Ava.Input; using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Input; using Ryujinx.Input;
using Ryujinx.Input.Assigner; using Ryujinx.Input.Assigner;
using System.Linq;
using Button = Ryujinx.Input.Button; using Button = Ryujinx.Input.Button;
using Key = Ryujinx.Common.Configuration.Hid.Key; using Key = Ryujinx.Common.Configuration.Hid.Key;
@@ -21,16 +24,21 @@ namespace Ryujinx.Ava.UI.Views.Settings
public SettingsHotkeysView() public SettingsHotkeysView()
{ {
InitializeComponent(); InitializeComponent();
RegisterEvents();
_avaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this);
CycleControllers.LayoutUpdated += (_, _1) => RegisterEvents();
}
private void RegisterEvents()
{
foreach (ILogical visual in SettingButtons.GetLogicalDescendants()) foreach (ILogical visual in SettingButtons.GetLogicalDescendants())
{ {
if (visual is ToggleButton button and not CheckBox) if (visual is ToggleButton button and not CheckBox)
{ {
button.IsCheckedChanged -= Button_IsCheckedChanged;
button.IsCheckedChanged += Button_IsCheckedChanged; button.IsCheckedChanged += Button_IsCheckedChanged;
} }
} }
_avaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this);
} }
protected override void OnPointerReleased(PointerReleasedEventArgs e) protected override void OnPointerReleased(PointerReleasedEventArgs e)
@@ -116,6 +124,13 @@ namespace Ryujinx.Ava.UI.Views.Settings
case "CustomVSyncIntervalDecrement": case "CustomVSyncIntervalDecrement":
viewModel.KeyboardHotkey.CustomVSyncIntervalDecrement = buttonValue.AsHidType<Key>(); viewModel.KeyboardHotkey.CustomVSyncIntervalDecrement = buttonValue.AsHidType<Key>();
break; break;
default:
var index = button.FindAncestorOfType<ItemsControl>().GetLogicalDescendants().OfType<ToggleButton>().IndexOf(button);
if (index >= 0 && viewModel.KeyboardHotkey.CycleControllers != null)
{
viewModel.KeyboardHotkey.CycleControllers[index].Hotkey = buttonValue.AsHidType<Key>();
}
break;
} }
} }
}; };

View File

@@ -316,8 +316,8 @@
</CheckBox> </CheckBox>
<CheckBox <CheckBox
IsChecked="{Binding IgnoreApplet}" IsChecked="{Binding IgnoreApplet}"
ToolTip.Tip="{ext:Locale IgnoreControllerAppletTooltip}"> ToolTip.Tip="{ext:Locale IgnoreAppletTooltip}">
<TextBlock Text="{ext:Locale SettingsTabSystemIgnoreControllerApplet}" /> <TextBlock Text="{ext:Locale SettingsTabSystemIgnoreApplet}" />
</CheckBox> </CheckBox>
<CheckBox <CheckBox
IsChecked="{Binding EnableCustomVSyncInterval}" IsChecked="{Binding EnableCustomVSyncInterval}"

View File

@@ -37,33 +37,12 @@
<CheckBox IsChecked="{Binding RememberWindowState}"> <CheckBox IsChecked="{Binding RememberWindowState}">
<TextBlock Text="{ext:Locale SettingsTabGeneralRememberWindowState}" /> <TextBlock Text="{ext:Locale SettingsTabGeneralRememberWindowState}" />
</CheckBox> </CheckBox>
<CheckBox IsChecked="{Binding DisableInputWhenOutOfFocus}">
<TextBlock Text="{ext:Locale SettingsTabGeneralDisableInputWhenOutOfFocus}" />
</CheckBox>
<CheckBox IsChecked="{Binding ShowTitleBar}" IsVisible="{x:Static helper:RunningPlatform.IsWindows}"> <CheckBox IsChecked="{Binding ShowTitleBar}" IsVisible="{x:Static helper:RunningPlatform.IsWindows}">
<TextBlock Text="{ext:Locale SettingsTabGeneralShowTitleBar}" /> <TextBlock Text="{ext:Locale SettingsTabGeneralShowTitleBar}" />
</CheckBox> </CheckBox>
<StackPanel Margin="0, 15, 0, 0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{ext:Locale SettingsTabGeneralFocusLossType}"
Width="150" />
<ComboBox SelectedIndex="{Binding FocusLostActionType}"
HorizontalContentAlignment="Left"
MinWidth="100">
<ComboBoxItem>
<TextBlock Text="{ext:Locale SettingsTabGeneralFocusLossTypeDoNothing}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{ext:Locale SettingsTabGeneralFocusLossTypeBlockInput}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{ext:Locale SettingsTabGeneralFocusLossTypeMuteAudio}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{ext:Locale SettingsTabGeneralFocusLossTypeBlockInputAndMuteAudio}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{ext:Locale SettingsTabGeneralFocusLossTypePauseEmulation}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Margin="0, 15, 0, 0" Orientation="Horizontal"> <StackPanel Margin="0, 15, 0, 0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" <TextBlock VerticalAlignment="Center"
Text="{ext:Locale SettingsTabGeneralCheckUpdatesOnLaunch}" Text="{ext:Locale SettingsTabGeneralCheckUpdatesOnLaunch}"

View File

@@ -37,7 +37,10 @@ namespace Ryujinx.Ava.UI.Views.Settings
addDirBox.Clear(); addDirBox.Clear();
ViewModel.GameListNeedsRefresh = true; if (isGameList)
ViewModel.GameDirectoryChanged = true;
else
ViewModel.AutoloadDirectoryChanged = true;
} }
else else
{ {
@@ -47,7 +50,10 @@ namespace Ryujinx.Ava.UI.Views.Settings
{ {
directories.Add(folder.Value.Path.LocalPath); directories.Add(folder.Value.Path.LocalPath);
ViewModel.GameListNeedsRefresh = true; if (isGameList)
ViewModel.GameDirectoryChanged = true;
else
ViewModel.AutoloadDirectoryChanged = true;
} }
} }
} }
@@ -59,7 +65,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
foreach (string path in new List<string>(GameDirsList.SelectedItems.Cast<string>())) foreach (string path in new List<string>(GameDirsList.SelectedItems.Cast<string>()))
{ {
ViewModel.GameDirectories.Remove(path); ViewModel.GameDirectories.Remove(path);
ViewModel.GameListNeedsRefresh = true; ViewModel.GameDirectoryChanged = true;
} }
if (GameDirsList.ItemCount > 0) if (GameDirsList.ItemCount > 0)
@@ -75,7 +81,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
foreach (string path in new List<string>(AutoloadDirsList.SelectedItems.Cast<string>())) foreach (string path in new List<string>(AutoloadDirsList.SelectedItems.Cast<string>()))
{ {
ViewModel.AutoloadDirectories.Remove(path); ViewModel.AutoloadDirectories.Remove(path);
ViewModel.GameListNeedsRefresh = true; ViewModel.AutoloadDirectoryChanged = true;
} }
if (AutoloadDirsList.ItemCount > 0) if (AutoloadDirsList.ItemCount > 0)

View File

@@ -765,116 +765,31 @@ namespace Ryujinx.Ava.UI.Windows
private void InputElement_OnGotFocus(object sender, GotFocusEventArgs e) private void InputElement_OnGotFocus(object sender, GotFocusEventArgs e)
{ {
if (ViewModel.AppHost is null) return; if (!_didDisableInputUpdates)
if (!_focusLoss.Active)
return; return;
switch (_focusLoss.Type) if (!ConfigurationState.Instance.Hid.DisableInputWhenOutOfFocus)
{ return;
case FocusLostType.BlockInput:
{
if (!ViewModel.AppHost.NpadManager.InputUpdatesBlocked)
{
_focusLoss = default;
return;
}
ViewModel.AppHost.NpadManager.UnblockInputUpdates(); if (ViewModel.AppHost is not { NpadManager.InputUpdatesBlocked: true } appHost)
_focusLoss = default; return;
break;
}
case FocusLostType.MuteAudio:
{
if (!ViewModel.AppHost.Device.IsAudioMuted())
{
_focusLoss = default;
return;
}
ViewModel.AppHost.Device.SetVolume(ViewModel.VolumeBeforeMute); appHost.NpadManager.UnblockInputUpdates();
_didDisableInputUpdates = appHost.NpadManager.InputUpdatesBlocked;
_focusLoss = default;
break;
}
case FocusLostType.BlockInputAndMuteAudio:
{
if (!ViewModel.AppHost.Device.IsAudioMuted())
goto case FocusLostType.BlockInput;
ViewModel.AppHost.Device.SetVolume(ViewModel.VolumeBeforeMute);
ViewModel.AppHost.NpadManager.UnblockInputUpdates();
_focusLoss = default;
break;
}
case FocusLostType.PauseEmulation:
{
if (!ViewModel.AppHost.Device.System.IsPaused)
{
_focusLoss = default;
return;
}
ViewModel.AppHost.Resume();
_focusLoss = default;
break;
}
}
} }
private (FocusLostType Type, bool Active) _focusLoss; private bool _didDisableInputUpdates;
private void InputElement_OnLostFocus(object sender, RoutedEventArgs e) private void InputElement_OnLostFocus(object sender, RoutedEventArgs e)
{ {
if (ConfigurationState.Instance.FocusLostActionType.Value is FocusLostType.DoNothing) if (!ConfigurationState.Instance.Hid.DisableInputWhenOutOfFocus)
return; return;
if (ViewModel.AppHost is null) return; if (ViewModel.AppHost is not { NpadManager.InputUpdatesBlocked: false } appHost)
return;
switch (ConfigurationState.Instance.FocusLostActionType.Value) appHost.NpadManager.BlockInputUpdates();
{ _didDisableInputUpdates = appHost.NpadManager.InputUpdatesBlocked;
case FocusLostType.BlockInput:
{
if (ViewModel.AppHost.NpadManager.InputUpdatesBlocked)
return;
ViewModel.AppHost.NpadManager.BlockInputUpdates();
_focusLoss = (FocusLostType.BlockInput, ViewModel.AppHost.NpadManager.InputUpdatesBlocked);
break;
}
case FocusLostType.MuteAudio:
{
if (ViewModel.AppHost.Device.GetVolume() is 0)
return;
ViewModel.VolumeBeforeMute = ViewModel.AppHost.Device.GetVolume();
ViewModel.AppHost.Device.SetVolume(0);
_focusLoss = (FocusLostType.MuteAudio, ViewModel.AppHost.Device.GetVolume() is 0f);
break;
}
case FocusLostType.BlockInputAndMuteAudio:
{
if (ViewModel.AppHost.Device.GetVolume() is 0)
goto case FocusLostType.BlockInput;
ViewModel.VolumeBeforeMute = ViewModel.AppHost.Device.GetVolume();
ViewModel.AppHost.Device.SetVolume(0);
ViewModel.AppHost.NpadManager.BlockInputUpdates();
_focusLoss = (FocusLostType.BlockInputAndMuteAudio, ViewModel.AppHost.Device.GetVolume() is 0f && ViewModel.AppHost.NpadManager.InputUpdatesBlocked);
break;
}
case FocusLostType.PauseEmulation:
{
if (ViewModel.AppHost.Device.System.IsPaused)
return;
ViewModel.AppHost.Pause();
_focusLoss = (FocusLostType.PauseEmulation, ViewModel.AppHost.Device.System.IsPaused);
break;
}
}
} }
} }
} }

View File

@@ -45,7 +45,7 @@ namespace Ryujinx.Ava.UI.Windows
{ {
InputPage.InputView?.SaveCurrentProfile(); InputPage.InputView?.SaveCurrentProfile();
if (Owner is MainWindow window && ViewModel.GameListNeedsRefresh) if (Owner is MainWindow window && (ViewModel.GameDirectoryChanged || ViewModel.AutoloadDirectoryChanged))
{ {
window.LoadApplications(); window.LoadApplications();
} }

View File

@@ -10,7 +10,6 @@ using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Utilities.Compat; using Ryujinx.Ava.Utilities.Compat;
using Ryujinx.Ava.Utilities.PlayReport;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.HLE.Loaders.Processes.Extensions;
@@ -36,14 +35,9 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
{ {
_id = value; _id = value;
Compatibility = CompatibilityCsv.Find(value); Compatibility = CompatibilityCsv.Find(Id);
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 Developer { get; set; } = "Unknown";
public string Version { get; set; } = "0"; public string Version { get; set; } = "0";
public int PlayerCount { get; set; } public int PlayerCount { get; set; }
@@ -52,7 +46,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
public bool HasLdnGames => PlayerCount != 0 && GameCount != 0; public bool HasLdnGames => PlayerCount != 0 && GameCount != 0;
public bool HasRichPresenceAsset => DiscordIntegrationModule.HasAssetImage(IdString); public bool HasRichPresenceAsset => DiscordIntegrationModule.HasAssetImage(IdString);
public bool HasDynamicRichPresenceSupport => RichPresenceSpec.HasValue; public bool HasDynamicRichPresenceSupport => DiscordIntegrationModule.HasAnalyzer(IdString);
public TimeSpan TimePlayed { get; set; } public TimeSpan TimePlayed { get; set; }
public DateTime? LastPlayed { get; set; } public DateTime? LastPlayed { get; set; }

View File

@@ -15,7 +15,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// <summary> /// <summary>
/// The current version of the file format /// The current version of the file format
/// </summary> /// </summary>
public const int CurrentVersion = 67; public const int CurrentVersion = 66;
/// <summary> /// <summary>
/// Version of the configuration file format /// Version of the configuration file format
@@ -172,18 +172,13 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// </summary> /// </summary>
public UpdaterType UpdateCheckerType { get; set; } public UpdaterType UpdateCheckerType { get; set; }
/// <summary>
/// How the emulator should behave when you click off/on the window.
/// </summary>
public FocusLostType FocusLostActionType { get; set; }
/// <summary> /// <summary>
/// Show "Confirm Exit" Dialog /// Show "Confirm Exit" Dialog
/// </summary> /// </summary>
public bool ShowConfirmExit { get; set; } public bool ShowConfirmExit { get; set; }
/// <summary> /// <summary>
/// Ignore Controller Applet dialog /// ignore "Applet" dialog
/// </summary> /// </summary>
public bool IgnoreApplet { get; set; } public bool IgnoreApplet { get; set; }

View File

@@ -46,7 +46,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
EnableDiscordIntegration.Value = cff.EnableDiscordIntegration; EnableDiscordIntegration.Value = cff.EnableDiscordIntegration;
CheckUpdatesOnStart.Value = cff.CheckUpdatesOnStart; CheckUpdatesOnStart.Value = cff.CheckUpdatesOnStart;
UpdateCheckerType.Value = cff.UpdateCheckerType; UpdateCheckerType.Value = cff.UpdateCheckerType;
FocusLostActionType.Value = cff.FocusLostActionType;
ShowConfirmExit.Value = cff.ShowConfirmExit; ShowConfirmExit.Value = cff.ShowConfirmExit;
RememberWindowState.Value = cff.RememberWindowState; RememberWindowState.Value = cff.RememberWindowState;
ShowTitleBar.Value = cff.ShowTitleBar; ShowTitleBar.Value = cff.ShowTitleBar;
@@ -88,7 +87,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
System.Region.Value = cff.SystemRegion; System.Region.Value = cff.SystemRegion;
System.TimeZone.Value = cff.SystemTimeZone; System.TimeZone.Value = cff.SystemTimeZone;
System.SystemTimeOffset.Value = cff.SystemTimeOffset; System.SystemTimeOffset.Value = cff.SystemTimeOffset;
System.MatchSystemTime.Value = cff.MatchSystemTime;
System.EnableDockedMode.Value = cff.DockedMode; System.EnableDockedMode.Value = cff.DockedMode;
System.EnablePtc.Value = cff.EnablePtc; System.EnablePtc.Value = cff.EnablePtc;
System.EnableLowPowerPtc.Value = cff.EnableLowPowerPtc; System.EnableLowPowerPtc.Value = cff.EnableLowPowerPtc;
@@ -100,7 +98,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
System.MemoryManagerMode.Value = cff.MemoryManagerMode; System.MemoryManagerMode.Value = cff.MemoryManagerMode;
System.DramSize.Value = cff.DramSize; System.DramSize.Value = cff.DramSize;
System.IgnoreMissingServices.Value = cff.IgnoreMissingServices; System.IgnoreMissingServices.Value = cff.IgnoreMissingServices;
System.IgnoreControllerApplet.Value = cff.IgnoreApplet; System.IgnoreApplet.Value = cff.IgnoreApplet;
System.UseHypervisor.Value = cff.UseHypervisor; System.UseHypervisor.Value = cff.UseHypervisor;
UI.GuiColumns.FavColumn.Value = cff.GuiColumns.FavColumn; UI.GuiColumns.FavColumn.Value = cff.GuiColumns.FavColumn;
@@ -437,8 +435,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
(63, static cff => cff.MatchSystemTime = false), (63, static cff => cff.MatchSystemTime = false),
(64, static cff => cff.LoggingEnableAvalonia = false), (64, static cff => cff.LoggingEnableAvalonia = false),
(65, static cff => cff.UpdateCheckerType = cff.CheckUpdatesOnStart ? UpdaterType.PromptAtStartup : UpdaterType.Off), (65, static cff => cff.UpdateCheckerType = cff.CheckUpdatesOnStart ? UpdaterType.PromptAtStartup : UpdaterType.Off),
(66, static cff => cff.DisableInputWhenOutOfFocus = false), (66, static cff => cff.DisableInputWhenOutOfFocus = false)
(67, static cff => cff.FocusLostActionType = cff.DisableInputWhenOutOfFocus ? FocusLostType.BlockInput : FocusLostType.DoNothing)
); );
} }
} }

View File

@@ -383,7 +383,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// <summary> /// <summary>
/// Ignore Controller Applet /// Ignore Controller Applet
/// </summary> /// </summary>
public ReactiveObject<bool> IgnoreControllerApplet { get; private set; } public ReactiveObject<bool> IgnoreApplet { get; private set; }
/// <summary> /// <summary>
/// Uses Hypervisor over JIT if available /// Uses Hypervisor over JIT if available
@@ -424,8 +424,8 @@ namespace Ryujinx.Ava.Utilities.Configuration
DramSize.LogChangesToValue(nameof(DramSize)); DramSize.LogChangesToValue(nameof(DramSize));
IgnoreMissingServices = new ReactiveObject<bool>(); IgnoreMissingServices = new ReactiveObject<bool>();
IgnoreMissingServices.LogChangesToValue(nameof(IgnoreMissingServices)); IgnoreMissingServices.LogChangesToValue(nameof(IgnoreMissingServices));
IgnoreControllerApplet = new ReactiveObject<bool>(); IgnoreApplet = new ReactiveObject<bool>();
IgnoreControllerApplet.LogChangesToValue(nameof(IgnoreControllerApplet)); IgnoreApplet.LogChangesToValue(nameof(IgnoreApplet));
AudioVolume = new ReactiveObject<float>(); AudioVolume = new ReactiveObject<float>();
AudioVolume.LogChangesToValue(nameof(AudioVolume)); AudioVolume.LogChangesToValue(nameof(AudioVolume));
UseHypervisor = new ReactiveObject<bool>(); UseHypervisor = new ReactiveObject<bool>();
@@ -780,11 +780,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// </summary> /// </summary>
public ReactiveObject<UpdaterType> UpdateCheckerType { get; private set; } public ReactiveObject<UpdaterType> UpdateCheckerType { get; private set; }
/// <summary>
/// How the emulator should behave when you click off/on the window.
/// </summary>
public ReactiveObject<FocusLostType> FocusLostActionType { get; private set; }
/// <summary> /// <summary>
/// Show "Confirm Exit" Dialog /// Show "Confirm Exit" Dialog
/// </summary> /// </summary>
@@ -822,7 +817,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
EnableDiscordIntegration = new ReactiveObject<bool>(); EnableDiscordIntegration = new ReactiveObject<bool>();
CheckUpdatesOnStart = new ReactiveObject<bool>(); CheckUpdatesOnStart = new ReactiveObject<bool>();
UpdateCheckerType = new ReactiveObject<UpdaterType>(); UpdateCheckerType = new ReactiveObject<UpdaterType>();
FocusLostActionType = new ReactiveObject<FocusLostType>();
ShowConfirmExit = new ReactiveObject<bool>(); ShowConfirmExit = new ReactiveObject<bool>();
RememberWindowState = new ReactiveObject<bool>(); RememberWindowState = new ReactiveObject<bool>();
ShowTitleBar = new ReactiveObject<bool>(); ShowTitleBar = new ReactiveObject<bool>();

View File

@@ -53,12 +53,10 @@ namespace Ryujinx.Ava.Utilities.Configuration
SystemRegion = System.Region, SystemRegion = System.Region,
SystemTimeZone = System.TimeZone, SystemTimeZone = System.TimeZone,
SystemTimeOffset = System.SystemTimeOffset, SystemTimeOffset = System.SystemTimeOffset,
MatchSystemTime = System.MatchSystemTime,
DockedMode = System.EnableDockedMode, DockedMode = System.EnableDockedMode,
EnableDiscordIntegration = EnableDiscordIntegration, EnableDiscordIntegration = EnableDiscordIntegration,
CheckUpdatesOnStart = CheckUpdatesOnStart, CheckUpdatesOnStart = CheckUpdatesOnStart,
UpdateCheckerType = UpdateCheckerType, UpdateCheckerType = UpdateCheckerType,
FocusLostActionType = FocusLostActionType,
ShowConfirmExit = ShowConfirmExit, ShowConfirmExit = ShowConfirmExit,
RememberWindowState = RememberWindowState, RememberWindowState = RememberWindowState,
ShowTitleBar = ShowTitleBar, ShowTitleBar = ShowTitleBar,
@@ -81,7 +79,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
MemoryManagerMode = System.MemoryManagerMode, MemoryManagerMode = System.MemoryManagerMode,
DramSize = System.DramSize, DramSize = System.DramSize,
IgnoreMissingServices = System.IgnoreMissingServices, IgnoreMissingServices = System.IgnoreMissingServices,
IgnoreApplet = System.IgnoreControllerApplet, IgnoreApplet = System.IgnoreApplet,
UseHypervisor = System.UseHypervisor, UseHypervisor = System.UseHypervisor,
GuiColumns = new GuiColumns GuiColumns = new GuiColumns
{ {
@@ -180,7 +178,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
System.EnableDockedMode.Value = true; System.EnableDockedMode.Value = true;
EnableDiscordIntegration.Value = true; EnableDiscordIntegration.Value = true;
UpdateCheckerType.Value = UpdaterType.PromptAtStartup; UpdateCheckerType.Value = UpdaterType.PromptAtStartup;
FocusLostActionType.Value = FocusLostType.DoNothing;
ShowConfirmExit.Value = true; ShowConfirmExit.Value = true;
RememberWindowState.Value = true; RememberWindowState.Value = true;
ShowTitleBar.Value = !OperatingSystem.IsWindows(); ShowTitleBar.Value = !OperatingSystem.IsWindows();
@@ -205,7 +202,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
System.MemoryManagerMode.Value = MemoryManagerMode.HostMappedUnsafe; System.MemoryManagerMode.Value = MemoryManagerMode.HostMappedUnsafe;
System.DramSize.Value = MemoryConfiguration.MemoryConfiguration4GiB; System.DramSize.Value = MemoryConfiguration.MemoryConfiguration4GiB;
System.IgnoreMissingServices.Value = false; System.IgnoreMissingServices.Value = false;
System.IgnoreControllerApplet.Value = false; System.IgnoreApplet.Value = false;
System.UseHypervisor.Value = true; System.UseHypervisor.Value = true;
Multiplayer.LanInterfaceId.Value = "0"; Multiplayer.LanInterfaceId.Value = "0";
Multiplayer.Mode.Value = MultiplayerMode.Disabled; Multiplayer.Mode.Value = MultiplayerMode.Disabled;

View File

@@ -1,15 +0,0 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.Configuration.UI
{
[JsonConverter(typeof(TypedStringEnumConverter<FocusLostType>))]
public enum FocusLostType
{
DoNothing,
BlockInput,
MuteAudio,
BlockInputAndMuteAudio,
PauseEmulation
}
}

View File

@@ -1,6 +1,5 @@
using Gommon; using Gommon;
using Ryujinx.Ava.Utilities.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Common.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@@ -20,11 +19,6 @@ namespace Ryujinx.Ava.Utilities.PlayReport
public IReadOnlyList<GameSpec> Specs => new ReadOnlyCollection<GameSpec>(_specs); 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> /// <summary>
/// Add an analysis spec matching a specific game by title ID, with the provided spec configuration. /// Add an analysis spec matching a specific game by title ID, with the provided spec configuration.
/// </summary> /// </summary>
@@ -33,12 +27,10 @@ namespace Ryujinx.Ava.Utilities.PlayReport
/// <returns>The current <see cref="Analyzer"/>, for chaining convenience.</returns> /// <returns>The current <see cref="Analyzer"/>, for chaining convenience.</returns>
public Analyzer AddSpec(string titleId, Func<GameSpec, GameSpec> transform) public Analyzer AddSpec(string titleId, Func<GameSpec, GameSpec> transform)
{ {
if (ulong.TryParse(titleId, NumberStyles.HexNumber, null, out _)) Guard.Ensure(ulong.TryParse(titleId, NumberStyles.HexNumber, null, out _),
return AddSpec(transform(GameSpec.Create(titleId))); $"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
Logger.Notice.PrintMsg(LogClass.Application, return AddSpec(transform(GameSpec.Create(titleId)));
$"Tried to add a {nameof(GameSpec)} with a non-hexadecimal title ID value. Input: '{titleId}'");
return this;
} }
/// <summary> /// <summary>
@@ -49,12 +41,10 @@ namespace Ryujinx.Ava.Utilities.PlayReport
/// <returns>The current <see cref="Analyzer"/>, for chaining convenience.</returns> /// <returns>The current <see cref="Analyzer"/>, for chaining convenience.</returns>
public Analyzer AddSpec(string titleId, Action<GameSpec> transform) public Analyzer AddSpec(string titleId, Action<GameSpec> transform)
{ {
if (ulong.TryParse(titleId, NumberStyles.HexNumber, null, out _)) Guard.Ensure(ulong.TryParse(titleId, NumberStyles.HexNumber, null, out _),
return AddSpec(GameSpec.Create(titleId).Apply(transform)); $"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
Logger.Notice.PrintMsg(LogClass.Application, return AddSpec(GameSpec.Create(titleId).Apply(transform));
$"Tried to add a {nameof(GameSpec)} with a non-hexadecimal title ID value. Input: '{titleId}'");
return this;
} }
/// <summary> /// <summary>
@@ -67,19 +57,10 @@ namespace Ryujinx.Ava.Utilities.PlayReport
Func<GameSpec, GameSpec> transform) Func<GameSpec, GameSpec> transform)
{ {
string[] tids = titleIds.ToArray(); string[] tids = titleIds.ToArray();
if (tids.All(x => ulong.TryParse(x, NumberStyles.HexNumber, null, out _) && !string.IsNullOrEmpty(x))) Guard.Ensure(tids.All(x => ulong.TryParse(x, NumberStyles.HexNumber, null, out _)),
return AddSpec(transform(GameSpec.Create(tids))); $"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
Logger.Notice.PrintMsg(LogClass.Application, return AddSpec(transform(GameSpec.Create(tids)));
$"Tried to add a {nameof(GameSpec)} with a non-hexadecimal title ID value. Input: '{
tids.FormatCollection(
x => x,
separator: ", ",
prefix: "[",
suffix: "]"
)
}'");
return this;
} }
/// <summary> /// <summary>
@@ -91,19 +72,10 @@ namespace Ryujinx.Ava.Utilities.PlayReport
public Analyzer AddSpec(IEnumerable<string> titleIds, Action<GameSpec> transform) public Analyzer AddSpec(IEnumerable<string> titleIds, Action<GameSpec> transform)
{ {
string[] tids = titleIds.ToArray(); string[] tids = titleIds.ToArray();
if (tids.All(x => ulong.TryParse(x, NumberStyles.HexNumber, null, out _) && !string.IsNullOrEmpty(x))) Guard.Ensure(tids.All(x => ulong.TryParse(x, NumberStyles.HexNumber, null, out _)),
return AddSpec(GameSpec.Create(tids).Apply(transform)); $"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
Logger.Notice.PrintMsg(LogClass.Application, return AddSpec(GameSpec.Create(tids).Apply(transform));
$"Tried to add a {nameof(GameSpec)} with a non-hexadecimal title ID value. Input: '{
tids.FormatCollection(
x => x,
separator: ", ",
prefix: "[",
suffix: "]"
)
}'");
return this;
} }
/// <summary> /// <summary>
@@ -134,12 +106,12 @@ namespace Ryujinx.Ava.Utilities.PlayReport
if (!playReport.ReportData.IsDictionary) if (!playReport.ReportData.IsDictionary)
return FormattedValue.Unhandled; return FormattedValue.Unhandled;
if (!TryGetSpec(runningGameId, out GameSpec spec)) if (!_specs.TryGetFirst(s => runningGameId.EqualsAnyIgnoreCase(s.TitleIds), out GameSpec spec))
return FormattedValue.Unhandled; return FormattedValue.Unhandled;
foreach (FormatterSpecBase formatSpec in spec.ValueFormatters.OrderBy(x => x.Priority)) foreach (FormatterSpecBase formatSpec in spec.ValueFormatters.OrderBy(x => x.Priority))
{ {
if (!formatSpec.TryFormat(appMeta, playReport, out FormattedValue value)) if (!formatSpec.Format(appMeta, playReport, out FormattedValue value))
continue; continue;
return value; return value;

View File

@@ -1,5 +1,4 @@
using Gommon; using Gommon;
using Humanizer;
using System; using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;
@@ -20,9 +19,6 @@ namespace Ryujinx.Ava.Utilities.PlayReport
_ => "Roaming Hyrule" _ => "Roaming Hyrule"
}; };
private static FormattedValue SkywardSwordHD_Rupees(SingleValue value)
=> "rupee".ToQuantity(value.Matched.IntValue);
private static FormattedValue SuperMarioOdyssey_AssistMode(SingleValue value) private static FormattedValue SuperMarioOdyssey_AssistMode(SingleValue value)
=> value.Matched.BoxedValue is 1 ? "Playing in Assist Mode" : "Playing in Regular Mode"; => value.Matched.BoxedValue is 1 ? "Playing in Assist Mode" : "Playing in Regular Mode";

View File

@@ -1,22 +1,11 @@
using System; namespace Ryujinx.Ava.Utilities.PlayReport
namespace Ryujinx.Ava.Utilities.PlayReport
{ {
public static partial class PlayReports public static partial class PlayReports
{ {
public static void Initialize() public static Analyzer Analyzer { get; } = new Analyzer()
{
// init lazy value
_ = Analyzer;
}
public static Analyzer Analyzer => _analyzerLazy.Value;
private static readonly Lazy<Analyzer> _analyzerLazy = new(() => new Analyzer()
.AddSpec( .AddSpec(
"01007ef00011e000", "01007ef00011e000",
spec => spec spec => spec
.WithDescription("based on being in Master Mode.")
.AddValueFormatter("IsHardMode", BreathOfTheWild_MasterMode) .AddValueFormatter("IsHardMode", BreathOfTheWild_MasterMode)
// reset to normal status when switching between normal & master mode in title screen // reset to normal status when switching between normal & master mode in title screen
.AddValueFormatter("AoCVer", FormattedValue.SingleAlwaysResets) .AddValueFormatter("AoCVer", FormattedValue.SingleAlwaysResets)
@@ -24,49 +13,34 @@ namespace Ryujinx.Ava.Utilities.PlayReport
.AddSpec( .AddSpec(
"0100f2c0115b6000", "0100f2c0115b6000",
spec => spec spec => spec
.WithDescription("based on where you are in Hyrule (Depths, Surface, Sky).")
.AddValueFormatter("PlayerPosY", TearsOfTheKingdom_CurrentField)) .AddValueFormatter("PlayerPosY", TearsOfTheKingdom_CurrentField))
.AddSpec(
"01002da013484000",
spec => spec
.WithDescription("based on how many Rupees you have.")
.AddValueFormatter("rupees", SkywardSwordHD_Rupees))
.AddSpec( .AddSpec(
"0100000000010000", "0100000000010000",
spec => spec spec =>
.WithDescription("based on if you're playing with Assist Mode.") spec.AddValueFormatter("is_kids_mode", SuperMarioOdyssey_AssistMode)
.AddValueFormatter("is_kids_mode", SuperMarioOdyssey_AssistMode)
) )
.AddSpec( .AddSpec(
"010075000ecbe000", "010075000ecbe000",
spec => spec spec =>
.WithDescription("based on if you're playing with Assist Mode.") spec.AddValueFormatter("is_kids_mode", SuperMarioOdysseyChina_AssistMode)
.AddValueFormatter("is_kids_mode", SuperMarioOdysseyChina_AssistMode)
) )
.AddSpec( .AddSpec(
"010028600ebda000", "010028600ebda000",
spec => spec spec => spec.AddValueFormatter("mode", SuperMario3DWorldOrBowsersFury)
.WithDescription("based on being in either Super Mario 3D World or Bowser's Fury.")
.AddValueFormatter("mode", SuperMario3DWorldOrBowsersFury)
) )
.AddSpec( // Global & China IDs .AddSpec( // Global & China IDs
["0100152000022000", "010075100e8ec000"], ["0100152000022000", "010075100e8ec000"],
spec => spec spec => spec.AddValueFormatter("To", MarioKart8Deluxe_Mode)
.WithDescription(
"based on what modes you're selecting in the menu & whether or not you're in a race.")
.AddValueFormatter("To", MarioKart8Deluxe_Mode)
) )
.AddSpec( .AddSpec(
["0100a3d008c5c000", "01008f6008c5e000"], ["0100a3d008c5c000", "01008f6008c5e000"],
spec => spec spec => spec
.WithDescription("based on what area of Paldea you're exploring.")
.AddValueFormatter("area_no", PokemonSVArea) .AddValueFormatter("area_no", PokemonSVArea)
.AddValueFormatter("team_circle", PokemonSVUnionCircle) .AddValueFormatter("team_circle", PokemonSVUnionCircle)
) )
.AddSpec( .AddSpec(
"01006a800016e000", "01006a800016e000",
spec => spec spec => spec
.WithDescription("based on what mode you're playing, who won, and what characters were present.")
.AddSparseMultiValueFormatter( .AddSparseMultiValueFormatter(
[ [
// Metadata to figure out what PlayReport we have. // Metadata to figure out what PlayReport we have.
@@ -85,14 +59,9 @@ namespace Ryujinx.Ava.Utilities.PlayReport
.AddSpec( .AddSpec(
[ [
"0100c9a00ece6000", "01008d300c50c000", "0100d870045b6000", "0100c9a00ece6000", "01008d300c50c000", "0100d870045b6000",
"010012f017576000", "0100c62011050000", "0100b3c014bda000" "010012f017576000", "0100c62011050000", "0100b3c014bda000"],
], spec => spec.AddValueFormatter("launch_title_id", NsoEmulator_LaunchedGame)
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}"; private static string Playing(string game) => $"Playing {game}";
} }

View File

@@ -23,20 +23,6 @@ namespace Ryujinx.Ava.Utilities.PlayReport
public required string[] TitleIds { get; init; } 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; } = []; public List<FormatterSpecBase> ValueFormatters { get; } = [];
@@ -211,7 +197,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
public string[] ReportKeys { get; init; } public string[] ReportKeys { get; init; }
public Delegate Formatter { get; init; } public Delegate Formatter { get; init; }
public bool TryFormat(ApplicationMetadata appMeta, Horizon.Prepo.Types.PlayReport playReport, public bool Format(ApplicationMetadata appMeta, Horizon.Prepo.Types.PlayReport playReport,
out FormattedValue formattedValue) out FormattedValue formattedValue)
{ {
formattedValue = default; formattedValue = default;