Compare commits
21 Commits
bcbf9d40e6
...
Canary-1.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12f0dbcc70 | ||
|
|
719be560ec | ||
|
|
18238736be | ||
|
|
5d9e4ad7a4 | ||
|
|
adba775f0c | ||
|
|
2ffaeb2803 | ||
|
|
b16b844760 | ||
|
|
bc07bc482d | ||
|
|
61975ca44d | ||
|
|
66054dd225 | ||
|
|
b1f61e5143 | ||
|
|
0d7d0e8092 | ||
|
|
aa2178dbe5 | ||
|
|
f92d09711b | ||
|
|
45ee8cd0e8 | ||
|
|
395bbd144a | ||
|
|
744d813b87 | ||
|
|
7d59ada798 | ||
|
|
a4b5304935 | ||
|
|
0965ee905d | ||
|
|
855161b23b |
@@ -3,7 +3,6 @@
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Alimer.Bindings.SDL" Version="3.7.1" />
|
||||
<PackageVersion Include="Avalonia" Version="11.0.13" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.13" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.13" />
|
||||
|
||||
@@ -75,8 +75,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "src\Ryuj
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Input.SDL3", "src\Ryujinx.Input.SDL3\Ryujinx.Input.SDL3.csproj", "{3BF24278-547D-42C2-9D43-182B978F54DD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal", "src\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj", "{C08931FA-1191-417A-864F-3882D93E683B}"
|
||||
@@ -261,10 +259,6 @@ Global
|
||||
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3BF24278-547D-42C2-9D43-182B978F54DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3BF24278-547D-42C2-9D43-182B978F54DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3BF24278-547D-42C2-9D43-182B978F54DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3BF24278-547D-42C2-9D43-182B978F54DD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||
return motionBackendType switch
|
||||
{
|
||||
MotionInputBackendType.GamepadDriver => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardMotionConfigController),
|
||||
MotionInputBackendType.Handheld => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardMotionConfigController),
|
||||
MotionInputBackendType.CemuHook => JsonSerializer.Deserialize(ref reader, _serializerContext.CemuHookMotionConfigController),
|
||||
_ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"),
|
||||
};
|
||||
@@ -67,7 +66,6 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||
switch (value.MotionBackend)
|
||||
{
|
||||
case MotionInputBackendType.GamepadDriver:
|
||||
case MotionInputBackendType.Handheld:
|
||||
JsonSerializer.Serialize(writer, value as StandardMotionConfigController, _serializerContext.StandardMotionConfigController);
|
||||
break;
|
||||
case MotionInputBackendType.CemuHook:
|
||||
|
||||
@@ -9,6 +9,5 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||
Invalid,
|
||||
GamepadDriver,
|
||||
CemuHook,
|
||||
Handheld,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
||||
<PackageReference Include="Alimer.Bindings.SDL" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,85 +0,0 @@
|
||||
using SDL3;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using static SDL3.SDL3;
|
||||
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
public unsafe class SDL3MotionDriver : IHandheld, IDisposable
|
||||
{
|
||||
private readonly Dictionary<SDL_SensorType, SDL_Sensor> sensors;
|
||||
private bool _disposed;
|
||||
|
||||
public SDL3MotionDriver()
|
||||
{
|
||||
int result = SDL_Init(SDL_InitFlags.Sensor);
|
||||
if (result < 0)
|
||||
{
|
||||
throw new InvalidOperationException($"SDL sensor initialization failed: {SDL_GetError()}");
|
||||
}
|
||||
sensors = SDL_GetSensors().ToArray().ToDictionary(SDL_GetSensorTypeForID, SDL_OpenSensor);
|
||||
}
|
||||
|
||||
~SDL3MotionDriver()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (disposing && sensors != null)
|
||||
{
|
||||
foreach (var sensor in sensors.Values)
|
||||
{
|
||||
if (sensor != IntPtr.Zero)
|
||||
{
|
||||
SDL_CloseSensor(sensor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public Vector3 GetMotionData(MotionInputId inputType)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
return inputType switch
|
||||
{
|
||||
MotionInputId.Gyroscope => GetSensorVector(SDL_SensorType.Gyro) * 180 / MathF.PI,
|
||||
MotionInputId.Accelerometer => GetSensorVector(SDL_SensorType.Accel) / SDL_STANDARD_GRAVITY,
|
||||
_ => Vector3.Zero
|
||||
};
|
||||
}
|
||||
|
||||
private Vector3 GetSensorVector(SDL_SensorType sensorType)
|
||||
{
|
||||
if (!sensors.TryGetValue(sensorType, out SDL_Sensor sensor))
|
||||
{
|
||||
return Vector3.Zero;
|
||||
}
|
||||
|
||||
var data = stackalloc float[3];
|
||||
if (SDL_GetSensorData(sensor, data, 3) < 0)
|
||||
{
|
||||
return Vector3.Zero;
|
||||
}
|
||||
|
||||
return new Vector3(data[0], data[1], data[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,12 @@ using System;
|
||||
|
||||
namespace Ryujinx.Input.HLE
|
||||
{
|
||||
public class InputManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IHandheld handheld)
|
||||
public class InputManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver)
|
||||
: IDisposable
|
||||
{
|
||||
public IGamepadDriver KeyboardDriver { get; } = keyboardDriver;
|
||||
public IGamepadDriver GamepadDriver { get; } = gamepadDriver;
|
||||
public IGamepadDriver MouseDriver { get; private set; }
|
||||
public IHandheld Handheld { get; } = handheld;
|
||||
|
||||
public void SetMouseDriver(IGamepadDriver mouseDriver)
|
||||
{
|
||||
@@ -19,7 +18,7 @@ namespace Ryujinx.Input.HLE
|
||||
|
||||
public NpadManager CreateNpadManager()
|
||||
{
|
||||
return new NpadManager(KeyboardDriver, GamepadDriver, MouseDriver, Handheld);
|
||||
return new NpadManager(KeyboardDriver, GamepadDriver, MouseDriver);
|
||||
}
|
||||
|
||||
public TouchScreenManager CreateTouchScreenManager()
|
||||
@@ -39,7 +38,6 @@ namespace Ryujinx.Input.HLE
|
||||
KeyboardDriver?.Dispose();
|
||||
GamepadDriver?.Dispose();
|
||||
MouseDriver?.Dispose();
|
||||
Handheld?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -218,14 +218,12 @@ namespace Ryujinx.Input.HLE
|
||||
public string Id { get; private set; }
|
||||
|
||||
private readonly CemuHookClient _cemuHookClient;
|
||||
private readonly IHandheld _handheld;
|
||||
|
||||
public NpadController(CemuHookClient cemuHookClient, IHandheld handheld)
|
||||
public NpadController(CemuHookClient cemuHookClient)
|
||||
{
|
||||
State = default;
|
||||
Id = null;
|
||||
_cemuHookClient = cemuHookClient;
|
||||
_handheld = handheld;
|
||||
}
|
||||
|
||||
public bool UpdateDriverConfiguration(IGamepadDriver gamepadDriver, InputConfig config)
|
||||
@@ -289,18 +287,6 @@ namespace Ryujinx.Input.HLE
|
||||
|
||||
if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Motion.EnableMotion)
|
||||
{
|
||||
if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.Handheld)
|
||||
{
|
||||
Vector3 accelerometer = _handheld.GetMotionData(MotionInputId.Accelerometer);
|
||||
Vector3 gyroscope = _handheld.GetMotionData(MotionInputId.Gyroscope);
|
||||
|
||||
accelerometer = new Vector3(accelerometer.X, -accelerometer.Z, accelerometer.Y);
|
||||
gyroscope = new Vector3(gyroscope.X, -gyroscope.Z, gyroscope.Y);
|
||||
|
||||
_leftMotionInput.Update(accelerometer, gyroscope, (ulong)PerformanceCounter.ElapsedNanoseconds / 1000, controllerConfig.Motion.Sensitivity, (float)controllerConfig.Motion.GyroDeadzone);
|
||||
_rightMotionInput = _leftMotionInput;
|
||||
}
|
||||
|
||||
if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.GamepadDriver)
|
||||
{
|
||||
if (gamepad.Features.HasFlag(GamepadFeaturesFlag.Motion))
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace Ryujinx.Input.HLE
|
||||
private readonly IGamepadDriver _keyboardDriver;
|
||||
private readonly IGamepadDriver _gamepadDriver;
|
||||
private readonly IGamepadDriver _mouseDriver;
|
||||
private readonly IHandheld _handheld;
|
||||
private bool _isDisposed;
|
||||
|
||||
private List<InputConfig> _inputConfig;
|
||||
@@ -39,7 +38,7 @@ namespace Ryujinx.Input.HLE
|
||||
private bool _enableMouse;
|
||||
private Switch _device;
|
||||
|
||||
public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IGamepadDriver mouseDriver, IHandheld handheld)
|
||||
public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IGamepadDriver mouseDriver)
|
||||
{
|
||||
_controllers = new NpadController[MaxControllers];
|
||||
_cemuHookClient = new CemuHookClient(this);
|
||||
@@ -48,7 +47,6 @@ namespace Ryujinx.Input.HLE
|
||||
_gamepadDriver = gamepadDriver;
|
||||
_mouseDriver = mouseDriver;
|
||||
_inputConfig = [];
|
||||
_handheld = handheld;
|
||||
|
||||
_gamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
|
||||
_gamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
|
||||
@@ -141,7 +139,7 @@ namespace Ryujinx.Input.HLE
|
||||
}
|
||||
else
|
||||
{
|
||||
controller = new(_cemuHookClient, _handheld);
|
||||
controller = new(_cemuHookClient);
|
||||
}
|
||||
|
||||
bool isValid = DriverConfigurationUpdate(ref controller, inputConfigEntry);
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Input
|
||||
{
|
||||
public interface IHandheld : IDisposable
|
||||
{
|
||||
Vector3 GetMotionData(MotionInputId gyroscope);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
|
||||
@@ -23,7 +23,6 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL2;
|
||||
using Ryujinx.Input.SDL3;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -183,7 +182,7 @@ namespace Ryujinx.Headless
|
||||
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, option.UserProfile);
|
||||
_userChannelPersistence = new UserChannelPersistence();
|
||||
|
||||
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver(), new SDL3MotionDriver());
|
||||
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
|
||||
|
||||
GraphicsConfig.EnableShaderCache = !option.DisableShaderCache;
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace Ryujinx.Headless
|
||||
IgnoreMissingServices = configurationState.System.IgnoreMissingServices;
|
||||
|
||||
if (NeedsOverride(nameof(IgnoreControllerApplet)))
|
||||
IgnoreControllerApplet = configurationState.System.IgnoreApplet;
|
||||
IgnoreControllerApplet = configurationState.System.IgnoreControllerApplet;
|
||||
|
||||
return;
|
||||
|
||||
|
||||
@@ -47,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;
|
||||
|
||||
@@ -76,7 +76,6 @@
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input.SDL3\Ryujinx.Input.SDL3.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
|
||||
@@ -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 () =>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -10,7 +10,6 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||
public partial class GamepadInputConfig : BaseModel
|
||||
{
|
||||
public bool EnableCemuHookMotion { get; set; }
|
||||
public bool EnableHandheldMotion { get; set; }
|
||||
public string DsuServerHost { get; set; }
|
||||
public int DsuServerPort { get; set; }
|
||||
public int Slot { get; set; }
|
||||
@@ -163,7 +162,7 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||
EnableMotion = controllerInput.Motion.EnableMotion;
|
||||
GyroDeadzone = controllerInput.Motion.GyroDeadzone;
|
||||
Sensitivity = controllerInput.Motion.Sensitivity;
|
||||
EnableHandheldMotion = controllerInput.Motion.MotionBackend == MotionInputBackendType.Handheld;
|
||||
|
||||
if (controllerInput.Motion is CemuHookMotionConfigController cemuHook)
|
||||
{
|
||||
EnableCemuHookMotion = true;
|
||||
@@ -286,7 +285,7 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||
config.Motion = new StandardMotionConfigController
|
||||
{
|
||||
EnableMotion = EnableMotion,
|
||||
MotionBackend = EnableHandheldMotion ? MotionInputBackendType.Handheld : MotionInputBackendType.GamepadDriver,
|
||||
MotionBackend = MotionInputBackendType.GamepadDriver,
|
||||
GyroDeadzone = GyroDeadzone,
|
||||
Sensitivity = Sensitivity,
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -18,34 +18,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
|
||||
[ObservableProperty] private double _gyroDeadzone;
|
||||
|
||||
private bool _enableCemuHookMotion;
|
||||
public bool EnableCemuHookMotion
|
||||
{
|
||||
get => _enableCemuHookMotion;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
EnableHandheldMotion = false;
|
||||
}
|
||||
_enableCemuHookMotion = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _enableHandheldMotion;
|
||||
public bool EnableHandheldMotion
|
||||
{
|
||||
get => _enableHandheldMotion;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
EnableCemuHookMotion = false;
|
||||
}
|
||||
_enableHandheldMotion = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
[ObservableProperty] private bool _enableCemuHookMotion;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1347,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();
|
||||
|
||||
@@ -49,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;
|
||||
@@ -527,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;
|
||||
@@ -593,16 +592,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
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.GameDirs.Value = [..GameDirectories];
|
||||
config.UI.AutoloadDirs.Value = [..AutoloadDirectories];
|
||||
|
||||
config.UI.BaseStyle.Value = BaseStyleIndex switch
|
||||
{
|
||||
@@ -623,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;
|
||||
@@ -635,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;
|
||||
@@ -715,8 +709,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
SaveSettingsEvent?.Invoke();
|
||||
|
||||
GameDirectoryChanged = false;
|
||||
AutoloadDirectoryChanged = false;
|
||||
GameListNeedsRefresh = false;
|
||||
}
|
||||
|
||||
private static void RevertIfNotSaved()
|
||||
|
||||
@@ -61,17 +61,6 @@
|
||||
Margin="5, 0"
|
||||
Text="{Binding GyroDeadzone, StringFormat=\{0:0.00\}}" />
|
||||
</StackPanel>
|
||||
<Separator
|
||||
Height="1"
|
||||
Margin="0,5" />
|
||||
<CheckBox
|
||||
Margin="5"
|
||||
IsChecked="{Binding EnableHandheldMotion}">
|
||||
<TextBlock
|
||||
Margin="0,3,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{ext:Locale ControllerSettingsMotionUseHandheldCompatibleMotion}" />
|
||||
</CheckBox>
|
||||
<Separator
|
||||
Height="1"
|
||||
Margin="0,5" />
|
||||
|
||||
@@ -30,7 +30,6 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
Sensitivity = config.Sensitivity,
|
||||
GyroDeadzone = config.GyroDeadzone,
|
||||
EnableCemuHookMotion = config.EnableCemuHookMotion,
|
||||
EnableHandheldMotion = config.EnableHandheldMotion,
|
||||
};
|
||||
|
||||
InitializeComponent();
|
||||
@@ -59,7 +58,6 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
config.DsuServerHost = content._viewModel.DsuServerHost;
|
||||
config.DsuServerPort = content._viewModel.DsuServerPort;
|
||||
config.EnableCemuHookMotion = content._viewModel.EnableCemuHookMotion;
|
||||
config.EnableHandheldMotion = content._viewModel.EnableHandheldMotion;
|
||||
config.MirrorInput = content._viewModel.MirrorInput;
|
||||
};
|
||||
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -31,7 +31,6 @@ using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL2;
|
||||
using Ryujinx.Input.SDL3;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -110,7 +109,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver(), new SDL3MotionDriver());
|
||||
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
|
||||
|
||||
_ = this.GetObservable(IsActiveProperty).Subscribe(it => ViewModel.IsActive = it);
|
||||
this.ScalingChanged += OnScalingChanged;
|
||||
|
||||
@@ -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;
|
||||
@@ -35,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; }
|
||||
@@ -46,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; }
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
System.Region.Value = cff.SystemRegion;
|
||||
System.TimeZone.Value = cff.SystemTimeZone;
|
||||
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;
|
||||
@@ -99,7 +100,7 @@ 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 = cff.GuiColumns.FavColumn;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user