Added support for NES controllers.#38
Conversation
|
Based on Davidobot#1220 |
d3xMachina
left a comment
There was a problem hiding this comment.
Thanks for your contribution. Please look at the review.
| { | ||
| Span<byte> resp = stackalloc byte[ReportLength]; | ||
|
|
||
| for (int i = 0; i < 100; ++i) { |
There was a problem hiding this comment.
100 is too much, reduce this to 5. (same as ReadSPICheck) and keep the style as
for ()
{
}
instead of
for () {
}
| Span<byte> resp = stackalloc byte[ReportLength]; | ||
|
|
||
| for (int i = 0; i < 100; ++i) { | ||
| int respLength = SubcommandCheck(0x02, Array.Empty<byte>(), resp, false, 200); |
There was a problem hiding this comment.
Remove the ", 200" parameter and use a collection expression [] instead of Array.Empty<byte>().
| for (int i = 0; i < 100; ++i) { | ||
| int respLength = SubcommandCheck(0x02, Array.Empty<byte>(), resp, false, 200); | ||
|
|
||
| if (respLength >= 15 && resp[15] == 0x03) { |
There was a problem hiding this comment.
15 is incorrect, it's the index to the first byte of the firmware version. You should use 17. (information here)
You might want to change 0x03 too unless it's really the value that is used for the NES controller. (I don't have one to test). 1 is left joycon, 2 is right joycon and 3 is pro controller.
You should use > and not >= since the index starts at 0.
There was a problem hiding this comment.
Great catch! I think I misread the output and was for some reason thinking it was replying as a pro controller. I was having a heck of a time getting a response on the original fork (hence the massive attempt numbers) so I also may have read the wrong packet at some point and it just happen to have worked due to firmware versioning.
| break; | ||
| } | ||
|
|
||
| if (resp[15] == 0x02) { |
There was a problem hiding this comment.
Any other value than the one for the NES should break the loop. So the content of the for should be changed to :
if (respLength > 17)
{
if (resp[17] == 0x03) // you should check that the 0x03 value is correct as stated in my previous comment
{
Type = ControllerType.NES;
}
break;
}
| } | ||
|
|
||
| //Make sure we're not actually a nes controller | ||
| if (this.Type == ControllerType.JoyconRight) |
| } | ||
|
|
||
| private int SubcommandCheck(byte sc, ReadOnlySpan<byte> bufParameters, Span<byte> response, bool print = true) | ||
| private int SubcommandCheck(byte sc, ReadOnlySpan<byte> bufParameters, Span<byte> response, bool print = true, int retries = 10) |
There was a problem hiding this comment.
Remove this change , int retries = 10
| JoyconLeft, | ||
| JoyconRight, | ||
| SNES, | ||
| NES, |
There was a problem hiding this comment.
You need to add NES at the end of the enum or it will break the saved datas for third party controller window.
Also for the third party controller feature to work properly, you need to add to the switch in the TypeToProdId function :
case 6:
return ProductNES;
There was a problem hiding this comment.
Fixed, and made the numerics explicit in the enum to reduce brittleness.
|
Thanks for the review! I'll fix these and get back to you. |
|
Hold on, I think I broke something. |
|
Ok, that should have fixed it. Turns out the NES controllers report differently (left or right) as to what they are. |
d3xMachina
left a comment
There was a problem hiding this comment.
A few more changes and we're good.
| //Make sure we're not actually a nes controller | ||
| if (Type == ControllerType.JoyconRight) | ||
| { | ||
| CheckIfRightIsNes(); | ||
| } |
There was a problem hiding this comment.
Move this below just after SetReportMode(ReportMode.SimpleHID, false);
| { | ||
| var respLength = SubcommandCheck(0x02, [], response, false); | ||
|
|
||
| if (respLength >= 20 && response[0] == 0x21 && response[14] == 0x02) |
There was a problem hiding this comment.
You can remove && response[0] == 0x21 && response[14] == 0x02 as it is already checked by SubcommandCheck. Keeping respLength >= 20 is fine.
There was a problem hiding this comment.
While this is checked, it is not checked in a way that is useful for us here.
private int SubcommandCheck(byte sc, ReadOnlySpan<byte> bufParameters, Span<byte> response, bool print = true)
{
int length = Subcommand(sc, bufParameters, print);
if (length <= 0)
{
DebugPrint($"Subcommand write error: {ErrorMessage()}", DebugType.Comms);
return length;
}
int tries = 0;
bool responseFound;
do
{
length = Read(response, 100); // don't set the timeout lower than 100 or might not always work
responseFound = length >= 20 && response[0] == 0x21 && response[14] == sc;
if (length < 0)
{
DebugPrint($"Subcommand read error: {ErrorMessage()}", DebugType.Comms);
}
tries++;
} while (tries < 10 && !responseFound && length >= 0);
if (!responseFound)
{
DebugPrint("No response.", DebugType.Comms);
return length;
}
if (print)
{
PrintArray<byte>(
response,
DebugType.Comms,
length - 1,
1,
$"Response ID {response[0]:X2}." + " Data: {0:S}"
);
}
return length;
}
The while loop continues until a response is found OR until we run out of tries. In the event we run out of tries, the function just gives us the last read item, regardless of if it passed that check or not. We check it again here so we can run another iteration of the for loop if necessary.
There was a problem hiding this comment.
Good catch, that was a bug I fixed yesterday in e2d603e, now it returns 0 properly if no response was found so you can remove it.
| SetNFCIR(false); | ||
| SetReportMode(ReportMode.StandardFull); | ||
|
|
||
There was a problem hiding this comment.
Those files shouldn't be changed. It was changed by your merge commit.
There was a problem hiding this comment.
Sorry, this is my first time working with submodules. I think I fixed it.
| return true; | ||
| } | ||
|
|
||
| private void CheckIfRightIsNes() |
There was a problem hiding this comment.
Should throw a DeviceComFailedException if it failed so it tries to reconnect the controller like so :
private void CheckIfRightIsNes()
{
Span<byte> response = stackalloc byte[ReportLength];
var ok = false;
for (var i = 0; i < 5; ++i)
{
var respLength = SubcommandCheck(0x02, [], response, false);
if (respLength >= 20)
{
// NES controllers share the hardware ID of the right joycon, but respond here differently.
if (response[17] == 0x0A || response[17] == 0x09)
{
Type = ControllerType.NES;
}
ok = true;
break;
}
}
if (!ok)
{
throw new DeviceComFailedException("reset device info");
}
}
|
|
||
| if (respLength >= 20 && response[0] == 0x21 && response[14] == 0x02) | ||
| { | ||
| if (response[17] == 0x0A || response[17] == 0x09) //NES controllers share the Right hardware ID of the right joycon, but respond here differently. |
There was a problem hiding this comment.
Please add to the comment which value correspond to left and right for 0x0A and 0x09
|
I got all the stuff that needed fixes and replied to your one comment regarding the duplicated message check. Let me know if that is sufficient or if you need anything tweaked. 👍 |
|
Thank you, only the duplicate check in CheckIfRightIsNes needs to be removed since it was a bug that was fixed very recently and I can merge. |
|
Ok, I removed the redundant check, now we just check for a valid response via looking if the response length is greater than 0. |
|
Thanks, merged! |
Added support for nes controllers.