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.
This commit is contained in:
JosJuice 2025-03-30 11:24:55 +02:00
parent 9819d66a47
commit 01a7732d50
5 changed files with 143 additions and 8 deletions

View File

@ -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"},
""};

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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