Compare commits

..
Author SHA1 Message Date
Shyanne 6fc8816deb Merge branch 'master' into implement-IsSixAxisSensorAtRest 2025-11-25 22:00:55 -05:00
Shyanne 0e92352e72 HLE: Implement IHidServer IsSixAxisSensorAtRest
Fixes the actually insane amount of log spam in games that check for this, such as Luigi's Mansion 3.

Values in HidDevices.NpadDevices.isAtRest may need to be tuned to a better range for resting detection. I originally set them to 0, and my controller rests at definitely NOT 0.

Will need to be revisited when implementing functionality for the global SixAxisActive bool, IHidServer.StartSixAxisTracking, and IHidServer.StopSixAxisTracking.
2025-11-25 21:31:34 -05:00
3 changed files with 84 additions and 53 deletions
+26 -46
View File
@@ -483,29 +483,10 @@ namespace Ryujinx.HLE.FileSystem
{
if (Directory.Exists(keysSource))
{
string[] keyPaths = Directory.EnumerateFiles(keysSource, "*.keys").ToArray();
if (keyPaths.Length is 0)
throw new FileNotFoundException($"Directory '{keysSource}' contained no '.keys' files.");
foreach (string filePath in keyPaths)
foreach (string filePath in Directory.EnumerateFiles(keysSource, "*.keys"))
{
try
{
VerifyKeysFile(filePath);
}
catch (Exception e)
{
Logger.Error?.Print(LogClass.Application, e.Message);
continue;
}
string destPath = Path.Combine(installDirectory, Path.GetFileName(filePath));
if (File.Exists(destPath))
File.Delete(destPath);
File.Copy(filePath, destPath, true);
VerifyKeysFile(filePath);
File.Copy(filePath, Path.Combine(installDirectory, Path.GetFileName(filePath)), true);
}
return;
@@ -520,25 +501,13 @@ namespace Ryujinx.HLE.FileSystem
using FileStream file = File.OpenRead(keysSource);
if (info.Extension is not ".keys")
throw new InvalidFirmwarePackageException("Input file extension is not .keys");
try
if (info.Extension is ".keys")
{
VerifyKeysFile(keysSource);
}
catch
{
File.Copy(keysSource, Path.Combine(installDirectory, info.Name), true);
}
else
throw new InvalidFirmwarePackageException("Input file is not a valid key package");
}
string dest = Path.Combine(installDirectory, info.Name);
if (File.Exists(dest))
File.Delete(dest);
// overwrite: true seems to not work on its own? https://github.com/Ryubing/Issues/issues/189
File.Copy(keysSource, dest, true);
}
private void FinishInstallation(string temporaryDirectory, string registeredDirectory)
@@ -1016,8 +985,8 @@ namespace Ryujinx.HLE.FileSystem
public static void VerifyKeysFile(string filePath)
{
// Verify the keys file format refers to https://github.com/Thealexbarney/LibHac/blob/master/KEYS.md
string genericPattern = "^[a-z0-9_]+ = [a-z0-9]+$";
string titlePattern = "^[a-z0-9]{32} = [a-z0-9]{32}$";
string genericPattern = @"^[a-z0-9_]+ = [a-z0-9]+$";
string titlePattern = @"^[a-z0-9]{32} = [a-z0-9]{32}$";
if (File.Exists(filePath))
{
@@ -1025,13 +994,24 @@ namespace Ryujinx.HLE.FileSystem
string fileName = Path.GetFileName(filePath);
string[] lines = File.ReadAllLines(filePath);
bool verified = fileName switch
bool verified;
switch (fileName)
{
"prod.keys" or "console.keys" or "dev.keys" => VerifyKeys(lines, genericPattern),
"title.keys" => VerifyKeys(lines, titlePattern),
_ => throw new FormatException(
$"Keys file name \"{fileName}\" not supported. Only \"prod.keys\", \"title.keys\", \"console.keys\", \"dev.keys\" are supported.")
};
case "prod.keys":
verified = VerifyKeys(lines, genericPattern);
break;
case "title.keys":
verified = VerifyKeys(lines, titlePattern);
break;
case "console.keys":
verified = VerifyKeys(lines, genericPattern);
break;
case "dev.keys":
verified = VerifyKeys(lines, genericPattern);
break;
default:
throw new FormatException($"Keys file name \"{fileName}\" not supported. Only \"prod.keys\", \"title.keys\", \"console.keys\", \"dev.keys\" are supported.");
}
if (!verified)
{
@@ -56,6 +56,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
_activeCount = 0;
JoyHold = NpadJoyHoldType.Vertical;
SixAxisActive = false;
}
internal ref KEvent GetStyleSetUpdateEvent(PlayerIndex player)
@@ -580,6 +581,23 @@ namespace Ryujinx.HLE.HOS.Services.Hid
return needUpdateRight;
}
public bool isAtRest(int playerNumber)
{
ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[playerNumber].InternalState;
ref SixAxisSensorState storage = ref GetSixAxisSensorLifo(ref currentNpad, false).GetCurrentEntryRef();
float acceleration = Math.Abs(storage.Acceleration.X)
+ Math.Abs(storage.Acceleration.Y)
+ Math.Abs(storage.Acceleration.Z);
float angularVelocity = Math.Abs(storage.AngularVelocity.X)
+ Math.Abs(storage.AngularVelocity.Y)
+ Math.Abs(storage.AngularVelocity.Z);
return ((acceleration <= 1.5F) && (angularVelocity <= 1.1F));
}
private void UpdateDisconnectedInputSixAxis(PlayerIndex index)
{
+40 -7
View File
@@ -602,19 +602,52 @@ namespace Ryujinx.HLE.HOS.Services.Hid
}
[CommandCmif(82)]
// IsSixAxisSensorAtRest(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsAsRest
// IsSixAxisSensorAtRest(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsAtRest
public ResultCode IsSixAxisSensorAtRest(ServiceCtx context)
{
int sixAxisSensorHandle = context.RequestData.ReadInt32();
// 4 byte struct w/ 4-byte alignment
// 0x0 0x4 TypeValue
// 0x0 0x1 NpadStyleIndex
// 0x1 0x1 PlayerNumber
// 0x2 0x1 DeviceIdx
// uint typeValue = (uint) sixAxisSensorHandle;
// uint npadStyleIndex = (uint) sixAxisSensorHandle & 0xff;
int playerNumber = (sixAxisSensorHandle << 8) & 0xff;
// uint deviceIdx= ((uint) sixAxisSensorHandle << 16) & 0xff;
// uint unknown = ((uint) sixAxisSensorHandle << 24) & 0xff;
// 32bit sign extension padding
// if = 0, + offset, else - offset
// npadStyleIndex = ((npadStyleIndex & 0x8000) == 0) ? npadStyleIndex | 0xFFFF0000 : npadStyleIndex & 0xFFFF0000;
// playerNumber = ((playerNumber & 0x8000) == 0) ? playerNumber | 0xFFFF0000 : playerNumber & 0xFFFF0000;
// deviceIdx = ((deviceIdx & 0x8000) == 0) ? deviceIdx | 0xFFFF0000 : deviceIdx & 0xFFFF0000;
// unknown = ((unknown & 0x8000) == 0) ? unknown | 0xFFFF0000 : unknown & 0xFFFF0000;
context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
bool isAtRest = true;
bool isAtRest;
// TODO: link to context.Device.Hid.Npads.SixAxisActive when properly implemented
// We currently do not support stopping or starting SixAxisTracking.
// It is just always tracking, the bool is unused.
// See Ryujinx.HLE.HOS.Services.Hid.NpadDevices
// common cases: if
// controller is keyboard (keyboards don't have gyroscopes, silly goose!)
// controller has no gyroscope
// SixAxisActive == false
// SixAxisTracking == false
// then isAtRest = true
// else check gyroscopic activity
// check gyroscopic activity
isAtRest = context.Device.Hid.Npads.isAtRest(playerNumber);
context.ResponseData.Write(isAtRest);
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, isAtRest });
return ResultCode.Success;
}
@@ -629,7 +662,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
context.ResponseData.Write(_isFirmwareUpdateAvailableForSixAxisSensor);
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _isFirmwareUpdateAvailableForSixAxisSensor });
return ResultCode.Success;
}