summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorJosJuice <josjuice@gmail.com>2025-03-30 11:24:55 +0200
committerJosJuice <josjuice@gmail.com>2025-03-31 21:24:25 +0200
commit01a7732d5065eaeba66ece0aec096ad18e3892b3 (patch)
treea300a98b444193de12bf640d41f10b04357a0294 /Source/Core
parent9819d66a478be3d57af9278e6c2cb433556a78d1 (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.cpp2
-rw-r--r--Source/Core/Core/Config/MainSettings.h1
-rw-r--r--Source/Core/Core/IOS/USB/LibusbDevice.cpp118
-rw-r--r--Source/Core/Core/IOS/USB/LibusbDevice.h6
-rw-r--r--Source/Core/UICommon/USBUtils.cpp24
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