diff options
| author | JosJuice <josjuice@gmail.com> | 2025-03-30 11:24:55 +0200 |
|---|---|---|
| committer | JosJuice <josjuice@gmail.com> | 2025-03-31 21:24:25 +0200 |
| commit | 01a7732d5065eaeba66ece0aec096ad18e3892b3 (patch) | |
| tree | a300a98b444193de12bf640d41f10b04357a0294 /Source/Core | |
| parent | 9819d66a478be3d57af9278e6c2cb433556a78d1 (diff) | |
IOS: Disguise Rock Band PlayStation USB devices as Wii equivalents
This lets you use PS3 Rock Band controllers with Wii Rock Band and
Guitar Hero games.
A normal user will probably never have any reason to disable this
behavior, but I figured maybe there's some person out there who would
like to disable it. (For instance, I know there's a mod for RB3 that's
trying to implement the same kind of cross-console controller
compatibility, and that can only be tested if the behavior I'm adding is
disabled.) So the behavior is controlled by an INI-only setting.
Diffstat (limited to 'Source/Core')
| -rw-r--r-- | Source/Core/Core/Config/MainSettings.cpp | 2 | ||||
| -rw-r--r-- | Source/Core/Core/Config/MainSettings.h | 1 | ||||
| -rw-r--r-- | Source/Core/Core/IOS/USB/LibusbDevice.cpp | 118 | ||||
| -rw-r--r-- | Source/Core/Core/IOS/USB/LibusbDevice.h | 6 | ||||
| -rw-r--r-- | Source/Core/UICommon/USBUtils.cpp | 24 |
5 files changed, 143 insertions, 8 deletions
diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index 751ab05656..b7df01da25 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -532,6 +532,8 @@ const Info<std::string> MAIN_BLUETOOTH_PASSTHROUGH_LINK_KEYS{ // Main.USBPassthrough +const Info<bool> MAIN_USB_PASSTHROUGH_DISGUISE_PLAYSTATION_AS_WII{ + {System::Main, "USBPassthrough", "DisguisePlayStationAsWii"}, true}; const Info<std::string> 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 8d8fbebb57..ba4cf4e7ea 100644 --- a/Source/Core/Core/Config/MainSettings.h +++ b/Source/Core/Core/Config/MainSettings.h @@ -350,6 +350,7 @@ extern const Info<std::string> MAIN_BLUETOOTH_PASSTHROUGH_LINK_KEYS; // Main.USBPassthrough +extern const Info<bool> MAIN_USB_PASSTHROUGH_DISGUISE_PLAYSTATION_AS_WII; extern const Info<std::string> MAIN_USB_PASSTHROUGH_DEVICES; std::set<std::pair<u16, u16>> GetUSBDeviceWhitelist(); void SetUSBDeviceWhitelist(const std::set<std::pair<u16, u16>>& devices); diff --git a/Source/Core/Core/IOS/USB/LibusbDevice.cpp b/Source/Core/Core/IOS/USB/LibusbDevice.cpp index 6770990a9d..24de176d7d 100644 --- a/Source/Core/Core/IOS/USB/LibusbDevice.cpp +++ b/Source/Core/Core/IOS/USB/LibusbDevice.cpp @@ -10,14 +10,17 @@ #include <map> #include <memory> #include <mutex> +#include <ranges> #include <utility> #include <vector> #include <libusb.h> #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" @@ -30,8 +33,8 @@ LibusbDevice::LibusbDevice(EmulationKernel& ios, libusb_device* device, : m_ios(ios), 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<u64>(m_vid) << 32 | static_cast<u64>(m_pid) << 16 | static_cast<u64>(libusb_get_bus_number(device)) << 8 | static_cast<u64>(libusb_get_device_address(device))); @@ -46,6 +49,9 @@ LibusbDevice::LibusbDevice(EmulationKernel& ios, libusb_device* device, } m_config_descriptors.emplace_back(std::move(config_descriptor)); } + + if (Config::Get(Config::MAIN_USB_PASSTHROUGH_DISGUISE_PLAYSTATION_AS_WII)) + DisguisePlayStationDevice(); } LibusbDevice::~LibusbDevice() @@ -65,6 +71,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; } @@ -196,6 +204,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<u8, length> 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<u8[]>(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<CtrlMessage> cmd) { if (!m_device_attached) @@ -253,11 +354,22 @@ int LibusbDevice::SubmitTransfer(std::unique_ptr<CtrlMessage> 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<BulkMessage> cmd) diff --git a/Source/Core/Core/IOS/USB/LibusbDevice.h b/Source/Core/Core/IOS/USB/LibusbDevice.h index 11d940e75b..2562b73816 100644 --- a/Source/Core/Core/IOS/USB/LibusbDevice.h +++ b/Source/Core/Core/IOS/USB/LibusbDevice.h @@ -51,8 +51,11 @@ private: std::vector<LibusbUtils::ConfigDescriptor> 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; @@ -72,6 +75,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::pair<u16, u16>, std::string_view> s_wii_peripherals{{ +static const std::map<std::pair<u16, u16>, 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::pair<u16, u16>, std::string> GetInsertedDevices() std::string GetDeviceName(const std::pair<u16, u16> 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 |
