diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index 7af0fd2766..9f4d1cce17 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -543,6 +543,8 @@ const Info MAIN_BLUETOOTH_PASSTHROUGH_LINK_KEYS{ // Main.USBPassthrough +const Info MAIN_USB_PASSTHROUGH_DISGUISE_PLAYSTATION_AS_WII{ + {System::Main, "USBPassthrough", "DisguisePlayStationAsWii"}, true}; const Info MAIN_USB_PASSTHROUGH_DEVICES{{System::Main, "USBPassthrough", "Devices"}, ""}; diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h index 344263ba10..28a044d253 100644 --- a/Source/Core/Core/Config/MainSettings.h +++ b/Source/Core/Core/Config/MainSettings.h @@ -353,6 +353,7 @@ extern const Info MAIN_BLUETOOTH_PASSTHROUGH_LINK_KEYS; // Main.USBPassthrough +extern const Info MAIN_USB_PASSTHROUGH_DISGUISE_PLAYSTATION_AS_WII; extern const Info MAIN_USB_PASSTHROUGH_DEVICES; std::set> GetUSBDeviceWhitelist(); void SetUSBDeviceWhitelist(const std::set>& devices); diff --git a/Source/Core/Core/IOS/USB/LibusbDevice.cpp b/Source/Core/Core/IOS/USB/LibusbDevice.cpp index d5c38277cc..da127b70d5 100644 --- a/Source/Core/Core/IOS/USB/LibusbDevice.cpp +++ b/Source/Core/Core/IOS/USB/LibusbDevice.cpp @@ -10,14 +10,17 @@ #include #include #include +#include #include #include #include #include "Common/Assert.h" +#include "Common/Config/Config.h" #include "Common/Logging/Log.h" #include "Common/StringUtil.h" +#include "Core/Config/MainSettings.h" #include "Core/HW/Memmap.h" #include "Core/IOS/Device.h" #include "Core/IOS/IOS.h" @@ -29,8 +32,8 @@ LibusbDevice::LibusbDevice(libusb_device* device, const libusb_device_descriptor : m_device(device) { libusb_ref_device(m_device); - m_vid = descriptor.idVendor; - m_pid = descriptor.idProduct; + m_vid = m_spoofed_vid = descriptor.idVendor; + m_pid = m_spoofed_pid = descriptor.idProduct; m_id = (static_cast(m_vid) << 32 | static_cast(m_pid) << 16 | static_cast(libusb_get_bus_number(device)) << 8 | static_cast(libusb_get_device_address(device))); @@ -45,6 +48,9 @@ LibusbDevice::LibusbDevice(libusb_device* device, const libusb_device_descriptor } m_config_descriptors.emplace_back(std::move(config_descriptor)); } + + if (Config::Get(Config::MAIN_USB_PASSTHROUGH_DISGUISE_PLAYSTATION_AS_WII)) + DisguisePlayStationDevice(); } LibusbDevice::~LibusbDevice() @@ -64,6 +70,8 @@ DeviceDescriptor LibusbDevice::GetDeviceDescriptor() const DeviceDescriptor descriptor; // The libusb_device_descriptor struct is the same as the IOS one, and it's not going to change. std::memcpy(&descriptor, &device_descriptor, sizeof(descriptor)); + descriptor.idVendor = m_spoofed_vid; + descriptor.idProduct = m_spoofed_pid; return descriptor; } @@ -195,6 +203,99 @@ int LibusbDevice::SetAltSetting(const u8 alt_setting) return libusb_set_interface_alt_setting(m_handle, m_active_interface, alt_setting); } +void LibusbDevice::DisguisePlayStationDevice() +{ + // PS3 and Wii Rock Band controllers are very similar to each other, but the VIDs and PIDs differ. + // By reporting PS3 Rock Band controllers as having Wii VIDs and PIDs, we can get PS3 controllers + // working with Wii games. + // + // Microphones are already cross-platform and therefore work without us doing anything here. + // + // The PS3 versions of the controllers that are new for Rock Band 3 - keyboards and pro guitars - + // have a feature that isn't present on Wii equivalents. By default, the controller won't send any + // data for the keys or frets & strings respectively, presumably to avoid them triggering + // unintended actions in the XMB (PS3 system menu). The PS3 version of Rock Band 3 sends a control + // transfer to enable these inputs. Because Wii controllers always have these inputs enabled, the + // Wii version of Rock Band 3 doesn't send the necessary control transfer, so we have to send it + // ourselves. Whether we should do this is controlled by + // m_needs_playstation_rock_band_3_instrument_control_transfer. + + if (m_vid != 0x12ba) // Sony Computer Entertainment America + return; + + switch (m_pid) + { + case 0x0200: // Rock Band guitar + // Unlike the Wii, the PS3 uses the same PID (0x0200) for Rock Band 1 and Rock Band 2 guitars. + // The Wii VID here is set to the Rock Band 2 device (0x3010) rather than the Rock Band 1 device + // (0x0004) because the Rock Band 2 device has more functionality (automatic latency + // calibration). + m_spoofed_pid = 0x3010; + break; + case 0x0210: // Rock Band drums + // Unlike the Wii, the PS3 uses the same PID (0x0210) for Rock Band 1 and Rock Band 2 drums. + // The Wii VID here is set to the Rock Band 2 device (0x3110) rather than the Rock Band 1 device + // (0x0005) because the Rock Band 2 device has more functionality (cymbals). + m_spoofed_pid = 0x3110; + break; + case 0x0218: // Rock Band 3 MIDI Pro Adapter with drums + m_spoofed_pid = 0x3138; + break; + case 0x2330: // Rock Band 3 keyboard + m_spoofed_pid = 0x3330; + m_needs_playstation_rock_band_3_instrument_control_transfer = true; + break; + case 0x2338: // Rock Band 3 MIDI Pro Adapter with keyboard + m_spoofed_pid = 0x3338; + m_needs_playstation_rock_band_3_instrument_control_transfer = true; + break; + case 0x2430: // Rock Band 3 Mustang pro guitar + m_spoofed_pid = 0x3430; + m_needs_playstation_rock_band_3_instrument_control_transfer = true; + break; + case 0x2438: // Rock Band 3 MIDI Pro Adapter with Mustang pro guitar + m_spoofed_pid = 0x3438; + m_needs_playstation_rock_band_3_instrument_control_transfer = true; + break; + case 0x2530: // Rock Band 3 Squier pro guitar (doesn't exist in reality, but game supports it) + m_spoofed_pid = 0x3530; + m_needs_playstation_rock_band_3_instrument_control_transfer = true; + break; + case 0x2538: // Rock Band 3 MIDI Pro Adapter with Squier pro guitar + m_spoofed_pid = 0x3538; + m_needs_playstation_rock_band_3_instrument_control_transfer = true; + break; + default: + return; + } + + m_spoofed_vid = 0x1bad; +} + +int LibusbDevice::SubmitPlayStationRockBand3InstrumentControlTransfer() +{ + constexpr size_t length = 40; + constexpr std::array enable_instrument_inputs = { + 0xe9, 0x00, 0x89, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x89, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe9, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + const size_t size = length + LIBUSB_CONTROL_SETUP_SIZE; + auto buffer = std::make_unique(size); + std::ranges::copy(enable_instrument_inputs, buffer.get() + LIBUSB_CONTROL_SETUP_SIZE); + + libusb_fill_control_setup(buffer.get(), 0x21, 0x09, 0x0300, 0x00, length); + + libusb_transfer* transfer = libusb_alloc_transfer(0); + transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER; + libusb_fill_control_transfer(transfer, m_handle, buffer.release(), nullptr, this, 0); + return libusb_submit_transfer(transfer); +} + +static bool IsRockBand3LEDControlTransfer(const CtrlMessage& cmd) +{ + return cmd.request_type == 0x21 && cmd.request == 0x9 && cmd.length == 8; +} + int LibusbDevice::SubmitTransfer(std::unique_ptr cmd) { if (!m_device_attached) @@ -252,11 +353,22 @@ int LibusbDevice::SubmitTransfer(std::unique_ptr cmd) auto& memory = system.GetMemory(); memory.CopyFromEmu(buffer.get() + LIBUSB_CONTROL_SETUP_SIZE, cmd->data_address, cmd->length); + // If the game is telling a Rock Band 3 device what player LEDs to turn on, take the opportunity + // to also tell the device to enable instrument inputs if necessary + const bool submit_rock_band_3_instrument_control_transfer = + m_needs_playstation_rock_band_3_instrument_control_transfer && + IsRockBand3LEDControlTransfer(*cmd); + libusb_transfer* transfer = libusb_alloc_transfer(0); transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER; libusb_fill_control_transfer(transfer, m_handle, buffer.release(), CtrlTransferCallback, this, 0); m_transfer_endpoints[0].AddTransfer(std::move(cmd), transfer); - return libusb_submit_transfer(transfer); + int ret = libusb_submit_transfer(transfer); + + if (submit_rock_band_3_instrument_control_transfer) + SubmitPlayStationRockBand3InstrumentControlTransfer(); + + return ret; } int LibusbDevice::SubmitTransfer(std::unique_ptr cmd) diff --git a/Source/Core/Core/IOS/USB/LibusbDevice.h b/Source/Core/Core/IOS/USB/LibusbDevice.h index 707fd0cee7..908e9e8b0d 100644 --- a/Source/Core/Core/IOS/USB/LibusbDevice.h +++ b/Source/Core/Core/IOS/USB/LibusbDevice.h @@ -48,8 +48,11 @@ private: std::vector m_config_descriptors; u16 m_vid = 0; u16 m_pid = 0; + u16 m_spoofed_vid = 0; + u16 m_spoofed_pid = 0; u8 m_active_interface = 0; bool m_device_attached = false; + bool m_needs_playstation_rock_band_3_instrument_control_transfer = false; libusb_device* m_device = nullptr; libusb_device_handle* m_handle = nullptr; @@ -69,6 +72,9 @@ private: static void CtrlTransferCallback(libusb_transfer* transfer); static void TransferCallback(libusb_transfer* transfer); + void DisguisePlayStationDevice(); + int SubmitPlayStationRockBand3InstrumentControlTransfer(); + int ClaimAllInterfaces(u8 config_num) const; int ReleaseAllInterfaces(u8 config_num) const; int ReleaseAllInterfacesForCurrentConfig() const; diff --git a/Source/Core/UICommon/USBUtils.cpp b/Source/Core/UICommon/USBUtils.cpp index 2264afe82c..42c4bd93fd 100644 --- a/Source/Core/UICommon/USBUtils.cpp +++ b/Source/Core/UICommon/USBUtils.cpp @@ -17,20 +17,33 @@ // Because opening and getting the device name from devices is slow, especially on Windows // with usbdk, we cannot do that for every single device. We should however still show // device names for known Wii peripherals. -static const std::map, std::string_view> s_wii_peripherals{{ +static const std::map, std::string_view> s_known_peripherals{{ {{0x046d, 0x0a03}, "Logitech Microphone"}, {{0x057e, 0x0308}, "Wii Speak"}, {{0x057e, 0x0309}, "Nintendo USB Microphone"}, {{0x057e, 0x030a}, "Ubisoft Motion Tracking Camera"}, {{0x0e6f, 0x0129}, "Disney Infinity Reader (Portal Device)"}, + {{0x12ba, 0x0200}, "Harmonix Guitar for PlayStation 3"}, + {{0x12ba, 0x0210}, "Harmonix Drum Kit for PlayStation 3"}, + {{0x12ba, 0x0218}, "Harmonix Drum Kit for PlayStation 3"}, + {{0x12ba, 0x2330}, "Harmonix RB3 Keyboard for PlayStation 3"}, + {{0x12ba, 0x2338}, "Harmonix RB3 MIDI Keyboard Interface for PlayStation 3"}, + {{0x12ba, 0x2430}, "Harmonix RB3 Mustang Guitar for PlayStation 3"}, + {{0x12ba, 0x2438}, "Harmonix RB3 MIDI Guitar Interface for PlayStation 3"}, + {{0x12ba, 0x2530}, "Harmonix RB3 Squier Guitar for PlayStation 3"}, + {{0x12ba, 0x2538}, "Harmonix RB3 MIDI Guitar Interface for PlayStation 3"}, {{0x1430, 0x0100}, "Tony Hawk Ride Skateboard"}, {{0x1430, 0x0150}, "Skylanders Portal"}, - {{0x1bad, 0x0004}, "Harmonix Guitar Controller"}, - {{0x1bad, 0x3110}, "Rock Band Drum Set"}, + {{0x1bad, 0x0004}, "Harmonix Guitar Controller for Nintendo Wii"}, + {{0x1bad, 0x0005}, "Harmonix Drum Controller for Nintendo Wii"}, + {{0x1bad, 0x3010}, "Harmonix Guitar Controller for Nintendo Wii"}, + {{0x1bad, 0x3110}, "Harmonix Drum Controller for Nintendo Wii"}, {{0x1bad, 0x3138}, "Harmonix Drum Controller for Nintendo Wii"}, {{0x1bad, 0x3330}, "Harmonix RB3 Keyboard for Nintendo Wii"}, {{0x1bad, 0x3338}, "Harmonix RB3 MIDI Keyboard Interface for Nintendo Wii"}, {{0x1bad, 0x3430}, "Harmonix RB3 Mustang Guitar for Nintendo Wii"}, + {{0x1bad, 0x3438}, "Harmonix RB3 MIDI Guitar Interface for Nintendo Wii"}, + {{0x1bad, 0x3530}, "Harmonix RB3 Squier Guitar for Nintendo Wii"}, {{0x1bad, 0x3538}, "Harmonix RB3 MIDI Guitar Interface for Nintendo Wii"}, {{0x21a4, 0xac40}, "EA Active NFL"}, }}; @@ -62,8 +75,9 @@ std::map, std::string> GetInsertedDevices() std::string GetDeviceName(const std::pair vid_pid) { - const auto iter = s_wii_peripherals.find(vid_pid); - const std::string_view device_name = iter == s_wii_peripherals.cend() ? "Unknown" : iter->second; + const auto iter = s_known_peripherals.find(vid_pid); + const std::string_view device_name = + iter == s_known_peripherals.cend() ? "Unknown" : iter->second; return fmt::format("{:04x}:{:04x} - {}", vid_pid.first, vid_pid.second, device_name); } } // namespace USBUtils