// DJ Hero Turntable controller emulator #include "stdafx.h" #include "Turntable.h" #include "Emu/Cell/lv2/sys_usbd.h" #include "Input/pad_thread.h" LOG_CHANNEL(turntable_log); usb_device_turntable::usb_device_turntable(int controller_index, const std::array& location) : usb_device_emulated(location), m_controller_index(controller_index) { device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{0x0100, 0x00, 0x00, 0x00, 0x40, 0x12BA, 0x0140, 0x0005, 0x01, 0x02, 0x00, 0x01}); auto& config0 = device.add_node(UsbDescriptorNode(USB_DESCRIPTOR_CONFIG, UsbDeviceConfiguration{0x0029, 0x01, 0x01, 0x00, 0x80, 0x19})); config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, UsbDeviceInterface{0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00})); config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_HID, UsbDeviceHID{0x0110, 0x00, 0x01, 0x22, 0x0089})); config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x81, 0x03, 0x0040, 0x0a})); config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x02, 0x03, 0x0040, 0x0a})); } usb_device_turntable::~usb_device_turntable() { } void usb_device_turntable::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) { transfer->fake = true; // Control transfers are nearly instant switch (bmRequestType) { case 0x21: switch (bRequest) { case 0x09: // Do nothing here - not sure what it should do. break; default: turntable_log.error("Unhandled Query Type: 0x%02X", buf[0]); break; } break; default: usb_device_emulated::control_transfer(bmRequestType, bRequest, wValue, wIndex, wLength, buf_size, buf, transfer); break; } } void usb_device_turntable::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint*/, UsbTransfer* transfer) { transfer->fake = true; transfer->expected_count = buf_size; transfer->expected_result = HC_CC_NOERR; // Turntable runs at 100hz --> 10ms // But make the emulated table go as fast as possible for better input behavior transfer->expected_time = get_timestamp(); memset(buf, 0, buf_size); buf[0] = 0x00; // Face Buttons // FACE BUTTON HEXMASK: // 0x01 = Square // 0x02 = Cross // 0x04 = Circle // 0x08 = Triangle / Euphoria buf[1] = 0x00; // Start/Select Buttons // START/SELECT HEXMASK: // 0x01 = Select // 0x02 = Start // 0x10 = PS Button buf[2] = 0x0F; // D-Pad // DPAD VALUES: // 0x00 = Up // 0x01 = Up-Right // 0x02 = Right // 0x03 = Right-Down // 0x04 = Down // 0x05 = Down-Left // 0x06 = Left // 0x07 = Up-Left // 0x0F = None buf[3] = 0x80; // Unknown, always 0x80 buf[4] = 0x80; // Unknown, always 0x80 buf[5] = 0x80; // Left Turntable buf[6] = 0x80; // Right Turntable // The following bytes are NOTed (set to 0xFF) when active. // If multiple buttons are pressed for one byte, the byte is NOTed twice (reset to 0x00). buf[7] = 0x00; // Square Button / D-Pad Right buf[8] = 0x00; // D-Pad Left buf[9] = 0x00; // Cross Button / D-Pad Up buf[10] = 0x00; // D-Pad Down buf[11] = 0x00; // Triangle / Euphoria Button buf[12] = 0x00; // Circle Button buf[19] = 0x00; // Effects Dial, lower 8 bits buf[20] = 0x02; // Effects Dial, upper 2 bits buf[21] = 0x00; // Crossfader, lower 8 bits buf[22] = 0x02; // Crossfader, upper 2 bits buf[23] = 0x00; // Platter Buttons // PLATTER BUTTON VALUES: // 0x01 = Right Platter, Green // 0x02 = Right Platter, Red // 0x04 = Right Platter, Blue // 0x10 = Left Platter, Green // 0x20 = Left Platter, Red // 0x40 = Left Platter, Blue buf[24] = 0x02; // Unknown, always 0x02 buf[26] = 0x02; // Unknown, always 0x02 // All other bufs are always 0x00 std::lock_guard lock(pad::g_pad_mutex); const auto handler = pad::get_current_handler(); const auto& pad = handler->GetPads()[m_controller_index]; if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return; for (const Button& button : pad->m_buttons) { if (button.m_pressed) { if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2) { switch (button.m_outKeyCode) { case CELL_PAD_CTRL_SQUARE: buf[0] |= 0x01; // Square Button buf[7] = ~buf[7]; // Square Button buf[23] |= 0x04; // Right Platter Blue break; case CELL_PAD_CTRL_CROSS: buf[0] |= 0x02; // Cross Button buf[9] = ~buf[9]; // Cross Button buf[23] |= 0x01; // Right Platter Green break; case CELL_PAD_CTRL_CIRCLE: buf[0] |= 0x04; // Circle Button buf[12] = ~buf[12]; // Circle Button buf[23] |= 0x02; // Right Platter Red break; case CELL_PAD_CTRL_TRIANGLE: buf[0] |= 0x08; // Triangle Button / Euphoria buf[11] = ~buf[11]; // Triangle Button / Euphoria break; case CELL_PAD_CTRL_R1: buf[0] |= 0x02; // Cross Button Only buf[9] = ~buf[9]; // Cross Button Only break; case CELL_PAD_CTRL_L1: buf[0] |= 0x04; // Circle Button Only buf[12] = ~buf[12]; // Circle Button Only break; case CELL_PAD_CTRL_R2: buf[0] |= 0x01; // Square Button Only buf[7] = ~buf[7]; // Square Button Only break; default: break; } } else if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1) { switch (button.m_outKeyCode) { case CELL_PAD_CTRL_DOWN: if (buf[2] == 0x02) // Right D-Pad { buf[2] = 0x03; // Right-Down D-Pad } else if (buf[2] == 0x06) // Left D-Pad { buf[2] = 0x05; // Left-Down D-Pad } else { buf[2] = 0x04; // Down D-Pad } buf[10] = ~buf[10]; // Down D-Pad; break; case CELL_PAD_CTRL_UP: if (buf[2] == 0x02) // Right D-Pad { buf[2] = 0x01; // Right-Up D-Pad } else if (buf[2] == 0x06) // Left D-Pad { buf[2] = 0x07; // Left-Up D-Pad } else { buf[2] = 0x00; // Up D-Pad } buf[9] = ~buf[9]; // Up D-Pad; break; case CELL_PAD_CTRL_LEFT: if (buf[2] == 0x00) // Up D-Pad { buf[2] = 0x07; // Left-Up D-Pad } else if (buf[2] == 0x04) // Down D-Pad { buf[2] = 0x05; // Left-Down D-Pad } else { buf[2] = 0x06; // Left D-Pad } buf[8] = ~buf[8]; // Left D-Pad; break; case CELL_PAD_CTRL_RIGHT: if (buf[2] == 0x00) // Up D-Pad { buf[2] = 0x01; // Right-Up D-Pad } else if (buf[2] == 0x04) // Down D-Pad { buf[2] = 0x03; // Right-Down D-Pad } else { buf[2] = 0x02; // Right D-Pad } buf[7] = ~buf[7]; // Right D-Pad break; case CELL_PAD_CTRL_START: buf[1] |= 0x02; // Start break; case CELL_PAD_CTRL_SELECT: buf[1] |= 0x01; // Select break; default: break; } } } } for (const AnalogStick& stick : pad->m_sticks) { switch (stick.m_offset) { case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y: buf[6] = ~(stick.m_value) + 0x01; // Right Turntable // Some pad handlers like MMJoystick are centered at 0x81 instead of 0x80 // which leaves the turntable stuck at 0x7F instead of 0x80, causing auto-scrolling menus // so force 0x7F to 0x80. if (buf[6] == 0x7F) buf[6] = 0x80; break; case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y: buf[21] = (stick.m_value & 0x3F) << 2; // Crossfader, lower 6 bits buf[22] = (stick.m_value & 0xC0) >> 6; // Crossfader, upper 2 bits break; case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X: buf[19] = (stick.m_value & 0x3F) << 2; // Effects Dial, lower 6 bits buf[20] = (stick.m_value & 0xC0) >> 6; // Effects Dial, upper 2 bits break; default: break; } } }