mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-01-30 12:32:43 +00:00
sys_usbd implementation
This commit is contained in:
parent
2f884de885
commit
e98c7f4e1a
@ -59,6 +59,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
../Crypto/ec.cpp
|
||||
../Crypto/key_vault.cpp
|
||||
../Crypto/lz.cpp
|
||||
../Crypto/md5.cpp
|
||||
../Crypto/sha1.cpp
|
||||
../Crypto/unedat.cpp
|
||||
../Crypto/unpkg.cpp
|
||||
@ -301,6 +302,8 @@ target_link_libraries(rpcs3_emu
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
Io/KeyboardHandler.cpp
|
||||
Io/PadHandler.cpp
|
||||
Io/usb_device.cpp
|
||||
Io/Skylander.cpp
|
||||
)
|
||||
|
||||
|
||||
@ -398,6 +401,7 @@ target_link_libraries(rpcs3_emu
|
||||
3rdparty::ffmpeg 3rdparty::cereal
|
||||
3rdparty::opengl 3rdparty::stblib
|
||||
3rdparty::vulkan 3rdparty::glew
|
||||
3rdparty::libusb
|
||||
PRIVATE
|
||||
3rdparty::gsl 3rdparty::xxhash
|
||||
3rdparty::dx12)
|
||||
|
@ -1,167 +1,473 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "sys_usbd.h"
|
||||
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "sys_ppu_thread.h"
|
||||
|
||||
#include "Emu/Io/usb_device.h"
|
||||
#include "Emu/Io/Skylander.h"
|
||||
|
||||
#include <libusb.h>
|
||||
|
||||
LOG_CHANNEL(sys_usbd);
|
||||
|
||||
std::vector<usbDevice> devices = {
|
||||
// System devices
|
||||
usbDevice{ DeviceListUnknownDataType{0x1, 0x2, 0x2, 0x44}, 50, deviceDescriptor{0x12, 0x1, 0x0200, 0, 0, 0, 0x40, 0x054C, 0x0250, 0x0009, 3, 4, 5, 1}},
|
||||
usbDevice{ DeviceListUnknownDataType{0x1, 0x3, 0x2, 0xAD}, 326, deviceDescriptor{0x12, 0x1, 0x0200, 0xE0, 0x1, 0x1, 0x40, 0x054C, 0x0267, 0x0001, 1, 2, 0, 1}},
|
||||
// USB Drive
|
||||
usbDevice{ DeviceListUnknownDataType{0x0, 0x4, 0x2, 0x0}, 50, deviceDescriptor{0x12, 0x1, 0x0200, 0, 0, 0, 0x40, 0x1516, 0x1226, 0x0100, 1, 2, 3, 1}},
|
||||
// Skylanders Portal
|
||||
usbDevice{ DeviceListUnknownDataType{0x0, 0x5, 0x2, 0xF8}, 59, deviceDescriptor{0x12, 0x1, 0x0200, 0, 0, 0, 0x20, 0x1430, 0x0150, 0x0100, 1, 2, 0, 1}},
|
||||
template <>
|
||||
void fmt_class_string<libusb_transfer>::format(std::string& out, u64 arg)
|
||||
{
|
||||
const auto& transfer = get_object(arg);
|
||||
|
||||
std::string datrace;
|
||||
const char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
|
||||
for (int index = 0; index < transfer.actual_length; index++)
|
||||
{
|
||||
datrace += hex[transfer.buffer[index] >> 4];
|
||||
datrace += hex[(transfer.buffer[index]) & 15];
|
||||
datrace += ' ';
|
||||
}
|
||||
|
||||
fmt::append(out, "TR[r:%d][sz:%d] => %s", (u8)transfer.status, transfer.actual_length, datrace);
|
||||
}
|
||||
|
||||
void LIBUSB_CALL callback_transfer(struct libusb_transfer* transfer)
|
||||
{
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
if (!usbh)
|
||||
return;
|
||||
|
||||
usbh->transfer_complete(transfer);
|
||||
}
|
||||
|
||||
usb_handler_thread::usb_handler_thread()
|
||||
{
|
||||
if (libusb_init(&ctx) < 0)
|
||||
return;
|
||||
|
||||
// look if any device which we could be interested in is actually connected
|
||||
libusb_device** list;
|
||||
ssize_t ndev = libusb_get_device_list(ctx, &list);
|
||||
|
||||
bool found_skylander = false;
|
||||
|
||||
for (ssize_t index = 0; index < ndev; index++)
|
||||
{
|
||||
libusb_device_descriptor desc;
|
||||
libusb_get_device_descriptor(list[index], &desc);
|
||||
|
||||
// clang-format off
|
||||
auto check_device = [&](const u16 id_vendor, const u16 id_product_min, const u16 id_product_max, const char* s_name) -> bool
|
||||
{
|
||||
if (desc.idVendor == id_vendor && desc.idProduct >= id_product_min && desc.idProduct <= id_product_max)
|
||||
{
|
||||
sys_usbd.success("Found device: %s", s_name);
|
||||
libusb_ref_device(list[index]);
|
||||
std::shared_ptr<usb_device_passthrough> usb_dev = std::make_shared<usb_device_passthrough>(list[index], desc);
|
||||
usb_devices.push_back(usb_dev);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
bool has_register_extra_ldd;
|
||||
int receive_event_called_count;
|
||||
if (check_device(0x1430, 0x0150, 0x0150, "Skylanders Portal"))
|
||||
{
|
||||
found_skylander = true;
|
||||
}
|
||||
check_device(0x1415, 0x0000, 0x0000, "Singstar Microphone");
|
||||
check_device(0x12BA, 0x0100, 0x0100, "Guitar Hero Guitar");
|
||||
check_device(0x12BA, 0x0120, 0x0120, "Guitar Hero Drums");
|
||||
check_device(0x12BA, 0x074B, 0x074B, "Guitar Hero Live Guitar");
|
||||
check_device(0x12BA, 0x0140, 0x0140, "DJ Hero Turntable");
|
||||
check_device(0x12BA, 0x0200, 0x020F, "Harmonix Guitar");
|
||||
check_device(0x12BA, 0x0210, 0x021F, "Harmonix Drums");
|
||||
check_device(0x12BA, 0x2330, 0x233F, "Harmonix Keyboard");
|
||||
check_device(0x12BA, 0x2430, 0x243F, "Harmonix Button Guitar");
|
||||
check_device(0x12BA, 0x2530, 0x253F, "Harmonix Real Guitar");
|
||||
|
||||
// GT5 Wheels&co
|
||||
check_device(0x046D, 0xC283, 0xC29B, "lgFF_c283_c29b");
|
||||
check_device(0x044F, 0xB653, 0xB653, "Thrustmaster RGT FFB Pro");
|
||||
check_device(0x044F, 0xB65A, 0xB65A, "Thrustmaster F430");
|
||||
check_device(0x044F, 0xB65D, 0xB65D, "Thrustmaster FFB");
|
||||
check_device(0x044F, 0xB65E, 0xB65E, "Thrustmaster TRS");
|
||||
check_device(0x044F, 0xB660, 0xB660, "Thrustmaster T500 RS Gear Shift");
|
||||
}
|
||||
|
||||
libusb_free_device_list(list, 1);
|
||||
|
||||
if (!found_skylander)
|
||||
{
|
||||
sys_usbd.success("Adding emulated skylander");
|
||||
usb_devices.push_back(std::make_shared<usb_device_skylander>());
|
||||
}
|
||||
|
||||
for (u32 index = 0; index < MAX_SYS_USBD_TRANSFERS; index++)
|
||||
{
|
||||
transfers[index].transfer = libusb_alloc_transfer(8);
|
||||
transfers[index].transfer_id = index;
|
||||
}
|
||||
}
|
||||
|
||||
usb_handler_thread::~usb_handler_thread()
|
||||
{
|
||||
while (is_running == true)
|
||||
{
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
// Ensures shared_ptr<usb_device> are all cleared before terminating libusb
|
||||
handled_devices.clear();
|
||||
open_pipes.clear();
|
||||
usb_devices.clear();
|
||||
|
||||
if (ctx)
|
||||
libusb_exit(ctx);
|
||||
|
||||
for (u32 index = 0; index < MAX_SYS_USBD_TRANSFERS; index++)
|
||||
{
|
||||
if (transfers[index].transfer)
|
||||
libusb_free_transfer(transfers[index].transfer);
|
||||
}
|
||||
}
|
||||
|
||||
void usb_handler_thread::operator()()
|
||||
{
|
||||
timeval lusb_tv;
|
||||
memset(&lusb_tv, 0, sizeof(timeval));
|
||||
lusb_tv.tv_usec = 200;
|
||||
|
||||
is_running = true;
|
||||
|
||||
while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped())
|
||||
{
|
||||
// Todo: Hotplug here?
|
||||
|
||||
// Process asynchronous requests that are pending
|
||||
libusb_handle_events_timeout_completed(ctx, &lusb_tv, nullptr);
|
||||
|
||||
// Process fake transfers
|
||||
if (!fake_transfers.empty())
|
||||
{
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
u64 timestamp = get_system_time() - Emu.GetPauseTime();
|
||||
|
||||
for (auto it = fake_transfers.begin(); it != fake_transfers.end(); it++)
|
||||
{
|
||||
auto transfer = *it;
|
||||
|
||||
ASSERT(transfer->busy && transfer->fake);
|
||||
|
||||
if (transfer->expected_time > timestamp)
|
||||
continue;
|
||||
|
||||
transfer->result = transfer->expected_result;
|
||||
transfer->count = transfer->expected_count;
|
||||
transfer->fake = false;
|
||||
transfer->busy = false;
|
||||
|
||||
fake_transfers.erase(it--);
|
||||
|
||||
send_message(SYS_USBD_TRANSFER_COMPLETE, transfer->transfer_id);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no handled devices usb thread is not actively needed
|
||||
if (!handled_devices.size())
|
||||
std::this_thread::sleep_for(500ms);
|
||||
else
|
||||
std::this_thread::sleep_for(200us);
|
||||
}
|
||||
|
||||
is_running = false;
|
||||
}
|
||||
|
||||
void usb_handler_thread::send_message(u32 message, u32 tr_id)
|
||||
{
|
||||
sys_usbd.trace("Sending event: arg1=0x%x arg2=0x%x arg3=0x00", message, tr_id);
|
||||
|
||||
usbd_events.push({message, tr_id, 0x00});
|
||||
if (receive_threads.size())
|
||||
{
|
||||
lv2_obj::awake(receive_threads.front());
|
||||
receive_threads.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void usb_handler_thread::transfer_complete(struct libusb_transfer* transfer)
|
||||
{
|
||||
UsbTransfer* usbd_transfer = (UsbTransfer*)transfer->user_data;
|
||||
|
||||
if (transfer->status != 0)
|
||||
{
|
||||
sys_usbd.error("Transfer Error: %d", (s32)transfer->status);
|
||||
}
|
||||
|
||||
switch (transfer->status)
|
||||
{
|
||||
case LIBUSB_TRANSFER_COMPLETED: usbd_transfer->result = HC_CC_NOERR; break;
|
||||
case LIBUSB_TRANSFER_TIMED_OUT: usbd_transfer->result = EHCI_CC_XACT; break;
|
||||
case LIBUSB_TRANSFER_OVERFLOW: usbd_transfer->result = EHCI_CC_BABBLE; break;
|
||||
case LIBUSB_TRANSFER_ERROR:
|
||||
case LIBUSB_TRANSFER_CANCELLED:
|
||||
case LIBUSB_TRANSFER_STALL:
|
||||
case LIBUSB_TRANSFER_NO_DEVICE:
|
||||
default: usbd_transfer->result = EHCI_CC_HALTED; break;
|
||||
}
|
||||
|
||||
usbd_transfer->count = transfer->actual_length;
|
||||
|
||||
for (s32 index = 0; index < transfer->num_iso_packets; index++)
|
||||
{
|
||||
u8 iso_status;
|
||||
switch (transfer->iso_packet_desc[index].status)
|
||||
{
|
||||
case LIBUSB_TRANSFER_COMPLETED: iso_status = USBD_HC_CC_NOERR; break;
|
||||
case LIBUSB_TRANSFER_TIMED_OUT: iso_status = USBD_HC_CC_XACT; break;
|
||||
case LIBUSB_TRANSFER_OVERFLOW: iso_status = USBD_HC_CC_BABBLE; break;
|
||||
case LIBUSB_TRANSFER_ERROR:
|
||||
case LIBUSB_TRANSFER_CANCELLED:
|
||||
case LIBUSB_TRANSFER_STALL:
|
||||
case LIBUSB_TRANSFER_NO_DEVICE:
|
||||
default: iso_status = USBD_HC_CC_MISSMF; break;
|
||||
}
|
||||
|
||||
usbd_transfer->iso_request.packets[index] = ((iso_status & 0xF) << 12 | (transfer->iso_packet_desc[index].actual_length & 0xFFF));
|
||||
}
|
||||
|
||||
usbd_transfer->busy = false;
|
||||
|
||||
send_message(SYS_USBD_TRANSFER_COMPLETE, usbd_transfer->transfer_id);
|
||||
|
||||
sys_usbd.trace("Transfer complete(0x%x): %s", usbd_transfer->transfer_id, *transfer);
|
||||
}
|
||||
|
||||
u32 usb_handler_thread::add_ldd(vm::ptr<char> s_product, u16 slen_product, u16 id_vendor, u16 id_product_min, u16 id_product_max)
|
||||
{
|
||||
UsbLdd new_ldd;
|
||||
new_ldd.name.resize(slen_product);
|
||||
memcpy(new_ldd.name.data(), s_product.get_ptr(), (u32)slen_product);
|
||||
new_ldd.id_vendor = id_vendor;
|
||||
new_ldd.id_product_min = id_product_min;
|
||||
new_ldd.id_product_max = id_product_max;
|
||||
ldds.push_back(new_ldd);
|
||||
|
||||
return (u32)ldds.size(); // TODO: to check
|
||||
}
|
||||
|
||||
u32 usb_handler_thread::open_pipe(u32 device_handle, u8 endpoint)
|
||||
{
|
||||
open_pipes.emplace(pipe_counter, UsbPipe{handled_devices[device_handle].second, endpoint});
|
||||
return pipe_counter++;
|
||||
}
|
||||
|
||||
bool usb_handler_thread::close_pipe(u32 pipe_id)
|
||||
{
|
||||
return (bool)open_pipes.erase(pipe_id);
|
||||
}
|
||||
|
||||
bool usb_handler_thread::is_pipe(u32 pipe_id) const
|
||||
{
|
||||
return open_pipes.count(pipe_id) != 0;
|
||||
}
|
||||
|
||||
const UsbPipe& usb_handler_thread::get_pipe(u32 pipe_id) const
|
||||
{
|
||||
return open_pipes.at(pipe_id);
|
||||
}
|
||||
|
||||
void usb_handler_thread::check_devices_vs_ldds()
|
||||
{
|
||||
for (const auto& dev : usb_devices)
|
||||
{
|
||||
for (const auto& ldd : ldds)
|
||||
{
|
||||
if (dev->device._device.idVendor == ldd.id_vendor && dev->device._device.idProduct >= ldd.id_product_min && dev->device._device.idProduct <= ldd.id_product_max && !dev->assigned_number)
|
||||
{
|
||||
if (!dev->open_device())
|
||||
{
|
||||
sys_usbd.error("Failed to open device for LDD(VID:0x%x PID:0x%x)", dev->device._device.idVendor, dev->device._device.idProduct);
|
||||
continue;
|
||||
}
|
||||
|
||||
sys_usbd.success("Ldd device matchup for <%s>", ldd.name);
|
||||
|
||||
dev->read_descriptors();
|
||||
|
||||
dev->assigned_number = dev_counter;
|
||||
handled_devices.emplace(dev_counter, std::pair(UsbInternalDevice{0x00, dev_counter, 0x02, 0x40}, dev));
|
||||
send_message(SYS_USBD_ATTACH, dev_counter);
|
||||
dev_counter++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool usb_handler_thread::get_event(vm::ptr<u64>& arg1, vm::ptr<u64>& arg2, vm::ptr<u64>& arg3)
|
||||
{
|
||||
if (usbd_events.size())
|
||||
{
|
||||
const auto& usb_event = usbd_events.front();
|
||||
*arg1 = (u64)std::get<0>(usb_event);
|
||||
*arg2 = (u64)std::get<1>(usb_event);
|
||||
*arg3 = (u64)std::get<2>(usb_event);
|
||||
usbd_events.pop();
|
||||
sys_usbd.trace("Received event: arg1=0x%x arg2=0x%x arg3=0x%x", *arg1, *arg2, *arg3);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void usb_handler_thread::add_to_receive_queue(ppu_thread* ppu)
|
||||
{
|
||||
lv2_obj::sleep(*ppu);
|
||||
receive_threads.push(ppu);
|
||||
}
|
||||
|
||||
u32 usb_handler_thread::get_free_transfer_id()
|
||||
{
|
||||
do
|
||||
{
|
||||
transfer_counter++;
|
||||
|
||||
if (transfer_counter >= MAX_SYS_USBD_TRANSFERS)
|
||||
transfer_counter = 0;
|
||||
} while (transfers[transfer_counter].busy);
|
||||
|
||||
return transfer_counter;
|
||||
}
|
||||
|
||||
UsbTransfer& usb_handler_thread::get_transfer(u32 transfer_id)
|
||||
{
|
||||
return transfers[transfer_id];
|
||||
}
|
||||
|
||||
/*
|
||||
* sys_usbd_initialize changes handle to a semi-unique identifier.
|
||||
* identifier generation is speculated to be:
|
||||
* f(n+1) = (f(n) + 2<<9) % 14847 + f_min (probably just f(0)-14847 but it could be different)
|
||||
* Thanks to @flash-fire for thinking up the identifier generation code.
|
||||
* TODO: try to get hardware to return not CELL_OK (perhaps set handle to NULL).
|
||||
*/
|
||||
s32 sys_usbd_initialize(vm::ptr<u32> handle)
|
||||
{
|
||||
sys_usbd.warning("sys_usbd_initialize(0x%x)", handle.get_ptr());
|
||||
*handle = 805322496;
|
||||
has_register_extra_ldd = false;
|
||||
receive_event_called_count = 0;
|
||||
sys_usbd.warning("sys_usbd_initialize(handle=*0x%x)", handle);
|
||||
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(id_new);
|
||||
|
||||
if (usbh)
|
||||
{
|
||||
usbh.create("Usb Manager Thread");
|
||||
}
|
||||
|
||||
*handle = 0x115B;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sys_usbd_finalize()
|
||||
s32 sys_usbd_finalize(ppu_thread& ppu, u32 handle)
|
||||
{
|
||||
sys_usbd.warning("sys_usbd_finalize(handle=0x%x)", handle);
|
||||
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
|
||||
if (!usbh)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_finalize()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sys_usbd_get_device_list(u32 handle, vm::ptr<DeviceListUnknownDataType> device_list, char unknown)
|
||||
*usbh.get() = thread_state::aborting;
|
||||
usbh.unlock();
|
||||
|
||||
while (true)
|
||||
{
|
||||
sys_usbd.warning("sys_usbd_get_device_list(handle=%d, device_list=0x%x, unknown=%c)", handle, device_list, unknown);
|
||||
// Unknown is just 0x7F
|
||||
for (int i = 0; i < devices.size(); i++)
|
||||
if (ppu.is_stopped())
|
||||
{
|
||||
device_list[i] = devices[i].basicDevice;
|
||||
}
|
||||
return ::size32(devices);
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 sys_usbd_register_extra_ldd(u32 handle, vm::ptr<void> lddOps, u16 strLen, u16 vendorID, u16 productID, u16 unk1)
|
||||
{
|
||||
sys_usbd.warning("sys_usbd_register_extra_ldd(handle=%u, lddOps=0x%x, unk1=%u, vendorID=%u, productID=%u, unk2=%u)", handle, lddOps, strLen, vendorID, productID, unk1);
|
||||
has_register_extra_ldd = true;
|
||||
thread_ctrl::wait_for(1000);
|
||||
|
||||
// No idea what 9 means.
|
||||
return 9;
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
|
||||
if (*usbh.get() == thread_state::finished)
|
||||
{
|
||||
usbh.unlock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s32 sys_usbd_get_descriptor_size(u32 handle, u8 deviceNumber)
|
||||
{
|
||||
sys_usbd.warning("sys_usbd_get_descriptor_size(handle=%u, deviceNumber=%u)", handle, deviceNumber);
|
||||
return devices[deviceNumber-2].descSize;
|
||||
}
|
||||
|
||||
s32 sys_usbd_get_descriptor(u32 handle, u8 deviceNumber, vm::ptr<deviceDescriptor> descriptor, s64 descSize)
|
||||
{
|
||||
sys_usbd.warning("sys_usbd_get_descriptor(handle=%u, deviceNumber=%u, descriptor=0x%x, descSize=%u)", handle, deviceNumber, descriptor, descSize);
|
||||
|
||||
// Just gonna have to hack it for now
|
||||
|
||||
u8 device = deviceNumber-2;
|
||||
unsigned char* desc = (unsigned char*)descriptor.get_ptr();
|
||||
if (device == 0)
|
||||
{
|
||||
unsigned char bytes[] = {0x12, 0x1, 0x0, 0x2, 0x0, 0x0, 0x0, 0x40, 0x4c, 0x5, 0x50, 0x2, 0x9, 0x0, 0x3, 0x4, 0x5, 0x1,
|
||||
0x9, 0x2, 0x20, 0x0, 0x1, 0x1, 0x0, 0x80, 0xfa,
|
||||
0x9, 0x4, 0x0, 0x0, 0x2, 0x8, 0x5, 0x50, 0x0,
|
||||
0x7, 0x5, 0x81, 0x2, 0x0, 0x2, 0x0,
|
||||
0x7, 0x5, 0x2, 0x2, 0x0, 0x2, 0x0};
|
||||
memcpy(desc, bytes, descSize);
|
||||
}
|
||||
else if (device == 1)
|
||||
{
|
||||
unsigned char bytes[] = {0x12, 0x1, 0x0, 0x2, 0xe0, 0x1, 0x1, 0x40, 0x4c, 0x5, 0x67, 0x2, 0x0, 0x1, 0x1, 0x2, 0x0, 0x1,
|
||||
0x9, 0x2, 0x34, 0x1, 0x4, 0x1, 0x0, 0xc0, 0x0,
|
||||
0x9, 0x4, 0x0, 0x0, 0x3, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x81, 0x3, 0x40, 0x0, 0x1,
|
||||
0x7, 0x5, 0x2, 0x2, 0x0, 0x2, 0x0,
|
||||
0x7, 0x5, 0x82, 0x2, 0x0, 0x2, 0x0,
|
||||
0x9, 0x4, 0x1, 0x0, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x3, 0x1, 0x0, 0x0, 0x4,
|
||||
0x7, 0x5, 0x83, 0x1, 0x0, 0x0, 0x4,
|
||||
0x9, 0x4, 0x1, 0x1, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x3, 0x1, 0xb, 0x0, 0x4,
|
||||
0x7, 0x5, 0x83, 0x1, 0xb, 0x0, 0x4,
|
||||
0x9, 0x4, 0x1, 0x2, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x3, 0x1, 0x13, 0x0, 0x4,
|
||||
0x7, 0x5, 0x83, 0x1, 0x13, 0x0, 0x4,
|
||||
0x9, 0x4, 0x1, 0x3, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x3, 0x1, 0x1b, 0x0, 0x4,
|
||||
0x7, 0x5, 0x83, 0x1, 0x1b, 0x0, 0x4,
|
||||
0x9, 0x4, 0x1, 0x4, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x3, 0x1, 0x23, 0x0, 0x4,
|
||||
0x7, 0x5, 0x83, 0x1, 0x23, 0x0, 0x4,
|
||||
0x9, 0x4, 0x1, 0x5, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x3, 0x1, 0x33, 0x0, 0x4,
|
||||
0x7, 0x5, 0x83, 0x1, 0x33, 0x0, 0x4,
|
||||
0x9, 0x4, 0x1, 0x6, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x3, 0x1, 0x40, 0x0, 0x4,
|
||||
0x7, 0x5, 0x83, 0x1, 0x40, 0x0, 0x4,
|
||||
0x9, 0x4, 0x2, 0x0, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x4, 0x3, 0x40, 0x0, 0x1,
|
||||
0x7, 0x5, 0x85, 0x3, 0x40, 0x0, 0x1,
|
||||
0x9, 0x4, 0x2, 0x1, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x4, 0x3, 0x80, 0x0, 0x1,
|
||||
0x7, 0x5, 0x85, 0x3, 0x80, 0x0, 0x1,
|
||||
0x9, 0x4, 0x2, 0x2, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x4, 0x3, 0x0, 0x1, 0x1,
|
||||
0x7, 0x5, 0x85, 0x3, 0x0, 0x1, 0x1,
|
||||
0x9, 0x4, 0x2, 0x3, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x4, 0x3, 0x0, 0x4, 0x1,
|
||||
0x7, 0x5, 0x85, 0x3, 0x0, 0x4, 0x1,
|
||||
0x9, 0x4, 0x3, 0x0, 0x0, 0xfe, 0x1, 0x0, 0x0,
|
||||
0x7, 0x21, 0x7, 0x88, 0x13, 0xff, 0x3};
|
||||
|
||||
memcpy(desc, bytes, descSize);
|
||||
}
|
||||
else if (device == 2)
|
||||
{
|
||||
// USB Drive
|
||||
unsigned char bytes[] = {0x12, 0x1, 0x0, 0x2, 0x0, 0x0, 0x0, 0x40, 0x16, 0x15, 0x26, 0x12, 0x0, 0x1, 0x1, 0x2, 0x3, 0x1,
|
||||
0x9, 0x2, 0x20, 0x0, 0x1, 0x1, 0x0, 0x80, 0xfa,
|
||||
0x9, 0x4, 0x0, 0x0, 0x2, 0x8, 0x6, 0x50, 0x0,
|
||||
0x7, 0x5, 0x81, 0x2, 0x0, 0x2, 0x0,
|
||||
0x7, 0x5, 0x2, 0x2, 0x0, 0x2, 0x0};
|
||||
memcpy(desc, bytes, descSize);
|
||||
}
|
||||
else if (device == 3 || device == 4)
|
||||
{
|
||||
// Skylanders Portal
|
||||
unsigned char bytes[] = {0x12, 0x1, 0x0, 0x2, 0x0, 0x0, 0x0, 0x20, 0x30, 0x14, 0x50, 0x1, 0x0, 0x1, 0x1, 0x2, 0x0, 0x1,
|
||||
0x9, 0x2, 0x29, 0x0, 0x1, 0x1, 0x0, 0x80, 0x96,
|
||||
0x9, 0x4, 0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0,
|
||||
0x9, 0x21, 0x11, 0x1, 0x0, 0x1, 0x22, 0x1d, 0x0,
|
||||
0x7, 0x5, 0x81, 0x3, 0x20, 0x0, 0x1,
|
||||
0x7, 0x5, 0x1, 0x3, 0x20, 0x0, 0x1};
|
||||
memcpy(desc, bytes, descSize);
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sys_usbd_register_ldd()
|
||||
s32 sys_usbd_get_device_list(u32 handle, vm::ptr<UsbInternalDevice> device_list, u32 max_devices)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_register_ldd()");
|
||||
sys_usbd.warning("sys_usbd_get_device_list(handle=0x%x, device_list=*0x%x, max_devices=0x%x)", handle, device_list, max_devices);
|
||||
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
if (!usbh)
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
u32 i_tocopy = std::min((s32)max_devices, (s32)usbh->handled_devices.size());
|
||||
|
||||
for (u32 index = 0; index < i_tocopy; index++)
|
||||
{
|
||||
device_list[index] = usbh->handled_devices[index].first;
|
||||
}
|
||||
|
||||
return i_tocopy;
|
||||
}
|
||||
|
||||
s32 sys_usbd_register_extra_ldd(u32 handle, vm::ptr<char> s_product, u16 slen_product, u16 id_vendor, u16 id_product_min, u16 id_product_max)
|
||||
{
|
||||
sys_usbd.warning("sys_usbd_register_extra_ldd(handle=0x%x, s_product=%s, slen_product=0x%x, id_vendor=0x%x, id_product_min=0x%x, id_product_max=0x%x)", handle, s_product, slen_product, id_vendor,
|
||||
id_product_min, id_product_max);
|
||||
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
if (!usbh)
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
s32 res = usbh->add_ldd(s_product, slen_product, id_vendor, id_product_min, id_product_max);
|
||||
usbh->check_devices_vs_ldds();
|
||||
|
||||
return res; // To check
|
||||
}
|
||||
|
||||
s32 sys_usbd_get_descriptor_size(u32 handle, u32 device_handle)
|
||||
{
|
||||
sys_usbd.trace("sys_usbd_get_descriptor_size(handle=0x%x, deviceNumber=0x%x)", handle, device_handle);
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
if (!usbh || !usbh->handled_devices.count(device_handle))
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
return usbh->handled_devices[device_handle].second->device.get_size();
|
||||
}
|
||||
|
||||
s32 sys_usbd_get_descriptor(u32 handle, u32 device_handle, vm::ptr<void> descriptor, u32 desc_size)
|
||||
{
|
||||
sys_usbd.trace("sys_usbd_get_descriptor(handle=0x%x, deviceNumber=0x%x, descriptor=0x%x, desc_size=0x%x)", handle, device_handle, descriptor, desc_size);
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
if (!usbh || !usbh->handled_devices.count(device_handle))
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
u8* ptr = (u8*)descriptor.get_ptr();
|
||||
usbh->handled_devices[device_handle].second->device.write_data(ptr);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// This function is used for psp(cellUsbPspcm), dongles in ps3 arcade cabinets(PS3A-USJ), ps2 cam(eyetoy), generic usb camera?(sample_usb2cam)
|
||||
s32 sys_usbd_register_ldd(u32 handle, vm::ptr<char> s_product, u16 slen_product)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_register_ldd(handle=0x%x, s_product=%s, slen_product=0x%x)", handle, s_product, slen_product);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@ -171,49 +477,90 @@ s32 sys_usbd_unregister_ldd()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sys_usbd_open_pipe(u32 handle, vm::ptr<void> endDisc)
|
||||
// TODO: determine what the unknown params are
|
||||
s32 sys_usbd_open_pipe(u32 handle, u32 device_handle, u32 unk1, u64 unk2, u64 unk3, u32 endpoint, u64 unk4)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_open_pipe(handle=%d, endDisc=0x%x)", handle, endDisc);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sys_usbd_open_default_pipe()
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_open_default_pipe()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sys_usbd_close_pipe()
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_close_pipe()");
|
||||
sys_usbd.warning("sys_usbd_open_pipe(handle=0x%x, device_handle=0x%x, unk1=0x%x, unk2=0x%x, unk3=0x%x, endpoint=0x%x, unk4=0x%x)", handle, device_handle, unk1, unk2, unk3, endpoint, unk4);
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
if (!usbh || !usbh->handled_devices.count(device_handle))
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
return usbh->open_pipe(device_handle, (u8)endpoint);
|
||||
}
|
||||
|
||||
s32 sys_usbd_open_default_pipe(u32 handle, u32 device_handle)
|
||||
{
|
||||
sys_usbd.trace("sys_usbd_open_default_pipe(handle=0x%x, device_handle=0x%x)", handle, device_handle);
|
||||
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
if (!usbh || !usbh->handled_devices.count(device_handle))
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
return usbh->open_pipe(device_handle, 0);
|
||||
}
|
||||
|
||||
s32 sys_usbd_close_pipe(u32 handle, u32 pipe_handle)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_close_pipe(handle=0x%x, pipe_handle=0x%x)", handle, pipe_handle);
|
||||
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
if (!usbh || !usbh->is_pipe(pipe_handle))
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
usbh->close_pipe(pipe_handle);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// From RE:
|
||||
// In libusbd_callback_thread
|
||||
// *arg1 = 4 will terminate CellUsbd libusbd_callback_thread
|
||||
// *arg1 = 3 will do some extra processing right away(notification of transfer finishing)
|
||||
// *arg1 < 1 || *arg1 > 4 are ignored(rewait instantly for event)
|
||||
// *arg1 == 1 || *arg1 == 2 will send a sys_event to internal CellUsbd event queue with same parameters as received and loop(attach and detach event)
|
||||
s32 sys_usbd_receive_event(ppu_thread& ppu, u32 handle, vm::ptr<u64> arg1, vm::ptr<u64> arg2, vm::ptr<u64> arg3)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_receive_event(handle=%u, arg1=0x%x, arg2=0x%x, arg3=0x%x)", handle, arg1, arg2, arg3);
|
||||
sys_usbd.trace("sys_usbd_receive_event(handle=%u, arg1=*0x%x, arg2=*0x%x, arg3=*0x%x)", handle, arg1, arg2, arg3);
|
||||
|
||||
_sys_ppu_thread_exit(ppu, CELL_OK);
|
||||
while (!Emu.IsStopped())
|
||||
{
|
||||
{
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
if (!usbh)
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
// TODO
|
||||
/*if (receive_event_called_count == 0)
|
||||
if (usbh->get_event(arg1, arg2, arg3))
|
||||
{
|
||||
*arg1 = 2;
|
||||
*arg2 = 5;
|
||||
*arg3 = 0;
|
||||
receive_event_called_count++;
|
||||
// hack for Guitar Hero Live
|
||||
// Attaching the device too fast seems to result in a nullptr along the way
|
||||
if (*arg1 == SYS_USBD_ATTACH)
|
||||
std::this_thread::sleep_for(5ms);
|
||||
|
||||
break;
|
||||
}
|
||||
else if (receive_event_called_count == 1)
|
||||
{
|
||||
*arg1 = 1;
|
||||
*arg2 = 6;
|
||||
*arg3 = 0;
|
||||
receive_event_called_count++;
|
||||
|
||||
usbh->add_to_receive_queue(&ppu);
|
||||
}
|
||||
else
|
||||
|
||||
while (!ppu.state.test_and_reset(cpu_flag::signal))
|
||||
{
|
||||
_sys_ppu_thread_exit(ppu, CELL_OK);
|
||||
}*/
|
||||
if (ppu.is_stopped())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
thread_ctrl::wait();
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@ -223,33 +570,128 @@ s32 sys_usbd_detect_event()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sys_usbd_attach()
|
||||
s32 sys_usbd_attach(u32 handle)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_attach()");
|
||||
sys_usbd.todo("sys_usbd_attach(handle=0x%x)", handle);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sys_usbd_transfer_data()
|
||||
s32 sys_usbd_transfer_data(u32 handle, u32 id_pipe, vm::ptr<u8> buf, u32 buf_size, vm::ptr<UsbDeviceRequest> request, u32 type_transfer)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_transfer_data()");
|
||||
sys_usbd.trace("sys_usbd_transfer_data(handle=0x%x, id_pipe=0x%x, buf=*0x%x, buf_length=0x%x, request=*0x%x, type=0x%x)", handle, id_pipe, buf, buf_size, request, type_transfer);
|
||||
if (sys_usbd.enabled == logs::level::trace && request.addr())
|
||||
{
|
||||
sys_usbd.trace("RequestType:0x%x, Request:0x%x, wValue:0x%x, wIndex:0x%x, wLength:0x%x", request->bmRequestType, request->bRequest, request->wValue, request->wIndex, request->wLength);
|
||||
if ((request->bmRequestType & 0x80) == 0 && buf && buf_size != 0)
|
||||
{
|
||||
std::string datrace;
|
||||
const char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
|
||||
for (u32 index = 0; index < buf_size; index++)
|
||||
{
|
||||
datrace += hex[(buf[index] >> 4) & 15];
|
||||
datrace += hex[(buf[index]) & 15];
|
||||
datrace += ' ';
|
||||
}
|
||||
|
||||
sys_usbd.trace("Control sent: %s", datrace);
|
||||
}
|
||||
}
|
||||
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
if (!usbh || !usbh->is_pipe(id_pipe))
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
u32 id_transfer = usbh->get_free_transfer_id();
|
||||
const auto& pipe = usbh->get_pipe(id_pipe);
|
||||
auto& transfer = usbh->get_transfer(id_transfer);
|
||||
|
||||
// Default endpoint is control endpoint
|
||||
if (pipe.endpoint == 0)
|
||||
{
|
||||
if (!request.addr())
|
||||
{
|
||||
sys_usbd.error("Tried to use control pipe without proper request pointer");
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
// Claiming interface
|
||||
if (request->bmRequestType == 0 && request->bRequest == 0x09)
|
||||
{
|
||||
pipe.device->set_configuration((u8)request->wValue);
|
||||
pipe.device->set_interface(0);
|
||||
}
|
||||
|
||||
pipe.device->control_transfer(request->bmRequestType, request->bRequest, request->wValue, request->wIndex, request->wLength, buf_size, buf.get_ptr(), &transfer);
|
||||
transfer.busy = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pipe.device->interrupt_transfer(buf_size, buf.get_ptr(), pipe.endpoint, &transfer);
|
||||
transfer.busy = true;
|
||||
}
|
||||
|
||||
if (transfer.fake)
|
||||
usbh->fake_transfers.push_back(&transfer);
|
||||
|
||||
// returns an identifier specific to the transfer
|
||||
return id_transfer;
|
||||
}
|
||||
|
||||
s32 sys_usbd_isochronous_transfer_data(u32 handle, u32 id_pipe, vm::ptr<UsbDeviceIsoRequest> iso_request)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_isochronous_transfer_data(handle=0x%x, id_pipe=0x%x, iso_request=*0x%x)", handle, id_pipe, iso_request);
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
if (!usbh || !usbh->is_pipe(id_pipe))
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
u32 id_transfer = usbh->get_free_transfer_id();
|
||||
const auto& pipe = usbh->get_pipe(id_pipe);
|
||||
auto& transfer = usbh->get_transfer(id_transfer);
|
||||
|
||||
memcpy(&transfer.iso_request, iso_request.get_ptr(), sizeof(UsbDeviceIsoRequest));
|
||||
pipe.device->isochronous_transfer(&transfer);
|
||||
|
||||
// returns an identifier specific to the transfer
|
||||
return id_transfer;
|
||||
}
|
||||
|
||||
s32 sys_usbd_get_transfer_status(u32 handle, u32 id_transfer, u32 unk1, vm::ptr<u32> result, vm::ptr<u32> count)
|
||||
{
|
||||
sys_usbd.trace("sys_usbd_get_transfer_status(handle=0x%x, id_transfer=0x%x, unk1=0x%x, result=*0x%x, count=*0x%x)", handle, id_transfer, unk1, result, count);
|
||||
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
if (!usbh)
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
auto& transfer = usbh->get_transfer(id_transfer);
|
||||
|
||||
*result = transfer.result;
|
||||
*count = transfer.count;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sys_usbd_isochronous_transfer_data()
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_isochronous_transfer_data()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sys_usbd_get_transfer_status()
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_get_transfer_status()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sys_usbd_get_isochronous_transfer_status()
|
||||
s32 sys_usbd_get_isochronous_transfer_status(u32 handle, u32 id_transfer, u32 unk1, vm::ptr<UsbDeviceIsoRequest> request, vm::ptr<u32> result)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_get_isochronous_transfer_status()");
|
||||
auto usbh = g_idm->lock<named_thread<usb_handler_thread>>(0);
|
||||
if (!usbh)
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
auto& transfer = usbh->get_transfer(id_transfer);
|
||||
|
||||
*result = transfer.result;
|
||||
memcpy(request.get_ptr(), &transfer.iso_request, sizeof(UsbDeviceIsoRequest));
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -1,64 +1,187 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <libusb.h>
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Emu/Io/usb_device.h"
|
||||
|
||||
// SysCalls
|
||||
#define MAX_SYS_USBD_TRANSFERS 0x44
|
||||
|
||||
struct DeviceListUnknownDataType
|
||||
// PS3 internal codes
|
||||
enum PS3StandardUsbErrors : u32
|
||||
{
|
||||
u8 unk1;
|
||||
u8 deviceID;
|
||||
u8 unk3;
|
||||
u8 unk4;
|
||||
HC_CC_NOERR = 0x00,
|
||||
EHCI_CC_MISSMF = 0x10,
|
||||
EHCI_CC_XACT = 0x20,
|
||||
EHCI_CC_BABBLE = 0x30,
|
||||
EHCI_CC_DATABUF = 0x40,
|
||||
EHCI_CC_HALTED = 0x50,
|
||||
};
|
||||
|
||||
/* 0x01 device descriptor */
|
||||
struct deviceDescriptor
|
||||
enum PS3IsochronousUsbErrors : u8
|
||||
{
|
||||
u8 bLength; /* descriptor size in bytes */
|
||||
u8 bDescriptorType; /* constant USB_DESCRIPTOR_TYPE_DEVICE */
|
||||
u16 bcdUSB; /* USB spec release compliance number */
|
||||
u8 bDeviceClass; /* class code */
|
||||
u8 bDeviceSubClass; /* subclass code */
|
||||
u8 bDeviceProtocol; /* protocol code */
|
||||
u8 bMaxPacketSize0; /* max packet size for endpoint 0 */
|
||||
u16 idVendor; /* USB-IF Vendor ID (VID) */
|
||||
u16 idProduct; /* Product ID (PID) */
|
||||
u16 bcdDevice; /* device release number */
|
||||
u8 iManufacturer; /* manufacturer string descriptor index */
|
||||
u8 iProduct; /* product string desccriptor index */
|
||||
u8 iSerialNumber; /* serial number string descriptor index */
|
||||
u8 bNumConfigurations; /* number of configurations */
|
||||
USBD_HC_CC_NOERR = 0x00,
|
||||
USBD_HC_CC_MISSMF = 0x01,
|
||||
USBD_HC_CC_XACT = 0x02,
|
||||
USBD_HC_CC_BABBLE = 0x04,
|
||||
USBD_HC_CC_DATABUF = 0x08,
|
||||
};
|
||||
|
||||
struct usbDevice
|
||||
enum SysUsbdEvents : u32
|
||||
{
|
||||
DeviceListUnknownDataType basicDevice;
|
||||
s32 descSize;
|
||||
deviceDescriptor descriptor;
|
||||
SYS_USBD_ATTACH = 0x01,
|
||||
SYS_USBD_DETACH = 0x02,
|
||||
SYS_USBD_TRANSFER_COMPLETE = 0x03,
|
||||
SYS_USBD_TERMINATE = 0x04,
|
||||
};
|
||||
|
||||
// PS3 internal structures
|
||||
struct UsbInternalDevice
|
||||
{
|
||||
u8 device_high; // System flag maybe (used in generating actual device number)
|
||||
u8 device_low; // Just a number identifying the device (used in generating actual device number)
|
||||
u8 unk3; // ? Seems to always be 2?
|
||||
u8 unk4; // ?
|
||||
};
|
||||
|
||||
struct UsbDeviceRequest
|
||||
{
|
||||
u8 bmRequestType;
|
||||
u8 bRequest;
|
||||
be_t<u16> wValue;
|
||||
be_t<u16> wIndex;
|
||||
be_t<u16> wLength;
|
||||
};
|
||||
|
||||
struct UsbDeviceIsoRequest
|
||||
{
|
||||
vm::ptr<void> buf;
|
||||
be_t<u32> start_frame;
|
||||
be_t<u32> num_packets;
|
||||
be_t<u16> packets[8];
|
||||
};
|
||||
|
||||
// HLE usbd
|
||||
void LIBUSB_CALL callback_transfer(struct libusb_transfer* transfer);
|
||||
|
||||
struct UsbTransfer
|
||||
{
|
||||
u32 transfer_id = 0;
|
||||
|
||||
s32 result = 0;
|
||||
u32 count = 0;
|
||||
UsbDeviceIsoRequest iso_request;
|
||||
|
||||
std::vector<u8> setup_buf;
|
||||
libusb_transfer* transfer = nullptr;
|
||||
bool busy = false;
|
||||
|
||||
// For fake transfers
|
||||
bool fake = false;
|
||||
u64 expected_time = 0;
|
||||
s32 expected_result = 0;
|
||||
u32 expected_count = 0;
|
||||
};
|
||||
|
||||
struct UsbLdd
|
||||
{
|
||||
std::string name;
|
||||
u16 id_vendor;
|
||||
u16 id_product_min;
|
||||
u16 id_product_max;
|
||||
};
|
||||
|
||||
struct UsbPipe
|
||||
{
|
||||
std::shared_ptr<usb_device> device = nullptr;
|
||||
u8 endpoint = 0;
|
||||
};
|
||||
|
||||
class usb_handler_thread
|
||||
{
|
||||
public:
|
||||
usb_handler_thread();
|
||||
~usb_handler_thread();
|
||||
|
||||
// Thread loop
|
||||
void operator()();
|
||||
|
||||
// Called by the libusb callback function to notify transfer completion
|
||||
void transfer_complete(libusb_transfer* transfer);
|
||||
|
||||
// LDDs handling functions
|
||||
u32 add_ldd(vm::ptr<char> s_product, u16 slen_product, u16 id_vendor, u16 id_product_min, u16 id_product_max);
|
||||
void check_devices_vs_ldds();
|
||||
|
||||
// Pipe functions
|
||||
u32 open_pipe(u32 device_handle, u8 endpoint);
|
||||
bool close_pipe(u32 pipe_id);
|
||||
bool is_pipe(u32 pipe_id) const;
|
||||
const UsbPipe& get_pipe(u32 pipe_id) const;
|
||||
|
||||
// Events related functions
|
||||
bool get_event(vm::ptr<u64>& arg1, vm::ptr<u64>& arg2, vm::ptr<u64>& arg3);
|
||||
void add_to_receive_queue(ppu_thread* ppu);
|
||||
|
||||
// Transfers related functions
|
||||
u32 get_free_transfer_id();
|
||||
UsbTransfer& get_transfer(u32 transfer_id);
|
||||
|
||||
// Map of devices actively handled by the ps3(device_id, device)
|
||||
std::map<u32, std::pair<UsbInternalDevice, std::shared_ptr<usb_device>>> handled_devices;
|
||||
// Fake transfers
|
||||
std::vector<UsbTransfer*> fake_transfers;
|
||||
|
||||
private:
|
||||
void send_message(u32 message, u32 tr_id);
|
||||
|
||||
private:
|
||||
// Counters for device IDs, transfer IDs and pipe IDs
|
||||
atomic_t<u8> dev_counter = 1;
|
||||
u32 transfer_counter = 0;
|
||||
u32 pipe_counter = 0x10; // Start at 0x10 only for tracing purposes
|
||||
|
||||
// List of device drivers
|
||||
std::vector<UsbLdd> ldds;
|
||||
|
||||
// List of pipes
|
||||
std::map<u32, UsbPipe> open_pipes;
|
||||
// Transfers infos
|
||||
std::array<UsbTransfer, 0x44> transfers;
|
||||
|
||||
// Queue of pending usbd events
|
||||
std::queue<std::tuple<u32, u32, u32>> usbd_events;
|
||||
// sys_usbd_receive_event PPU Threads
|
||||
std::queue<ppu_thread*> receive_threads;
|
||||
|
||||
// List of devices "connected" to the ps3
|
||||
std::vector<std::shared_ptr<usb_device>> usb_devices;
|
||||
|
||||
libusb_context* ctx = nullptr;
|
||||
atomic_t<bool> is_running = false;
|
||||
};
|
||||
|
||||
s32 sys_usbd_initialize(vm::ptr<u32> handle);
|
||||
s32 sys_usbd_finalize();
|
||||
s32 sys_usbd_get_device_list(u32 handle, vm::ptr<DeviceListUnknownDataType> device_list, char unknown);
|
||||
s32 sys_usbd_get_descriptor_size(u32 handle, u8 deviceNumber);
|
||||
s32 sys_usbd_get_descriptor(u32 handle, u8 deviceNumber, vm::ptr<deviceDescriptor> descriptor, s64 descSize);
|
||||
s32 sys_usbd_register_ldd();
|
||||
s32 sys_usbd_finalize(ppu_thread& ppu, u32 handle);
|
||||
s32 sys_usbd_get_device_list(u32 handle, vm::ptr<UsbInternalDevice> device_list, u32 max_devices);
|
||||
s32 sys_usbd_get_descriptor_size(u32 handle, u32 device_handle);
|
||||
s32 sys_usbd_get_descriptor(u32 handle, u32 device_handle, vm::ptr<void> descriptor, u32 desc_size);
|
||||
s32 sys_usbd_register_ldd(u32 handle, vm::ptr<char> s_product, u16 slen_product);
|
||||
s32 sys_usbd_unregister_ldd();
|
||||
s32 sys_usbd_open_pipe(u32 handle, vm::ptr<void> endDisc);
|
||||
s32 sys_usbd_open_default_pipe();
|
||||
s32 sys_usbd_close_pipe();
|
||||
s32 sys_usbd_open_pipe(u32 handle, u32 device_handle, u32 unk1, u64 unk2, u64 unk3, u32 endpoint, u64 unk4);
|
||||
s32 sys_usbd_open_default_pipe(u32 handle, u32 device_handle);
|
||||
s32 sys_usbd_close_pipe(u32 handle, u32 pipe_handle);
|
||||
s32 sys_usbd_receive_event(ppu_thread& ppu, u32 handle, vm::ptr<u64> arg1, vm::ptr<u64> arg2, vm::ptr<u64> arg3);
|
||||
s32 sys_usbd_detect_event();
|
||||
s32 sys_usbd_attach();
|
||||
s32 sys_usbd_transfer_data();
|
||||
s32 sys_usbd_isochronous_transfer_data();
|
||||
s32 sys_usbd_get_transfer_status();
|
||||
s32 sys_usbd_get_isochronous_transfer_status();
|
||||
s32 sys_usbd_attach(u32 handle);
|
||||
s32 sys_usbd_transfer_data(u32 handle, u32 id_pipe, vm::ptr<u8> buf, u32 buf_size, vm::ptr<UsbDeviceRequest> request, u32 type_transfer);
|
||||
s32 sys_usbd_isochronous_transfer_data(u32 handle, u32 id_pipe, vm::ptr<UsbDeviceIsoRequest> iso_request);
|
||||
s32 sys_usbd_get_transfer_status(u32 handle, u32 id_transfer, u32 unk1, vm::ptr<u32> result, vm::ptr<u32> count);
|
||||
s32 sys_usbd_get_isochronous_transfer_status(u32 handle, u32 id_transfer, u32 unk1, vm::ptr<UsbDeviceIsoRequest> request, vm::ptr<u32> result);
|
||||
s32 sys_usbd_get_device_location();
|
||||
s32 sys_usbd_send_event();
|
||||
s32 sys_usbd_event_port_send();
|
||||
s32 sys_usbd_allocate_memory();
|
||||
s32 sys_usbd_free_memory();
|
||||
s32 sys_usbd_get_device_speed();
|
||||
s32 sys_usbd_register_extra_ldd(u32 handle, vm::ptr<void> lddOps, u16 strLen, u16 vendorID, u16 productID, u16 unk1);
|
||||
s32 sys_usbd_register_extra_ldd(u32 handle, vm::ptr<char> s_product, u16 slen_product, u16 id_vendor, u16 id_product_min, u16 id_product_max);
|
||||
|
171
rpcs3/Emu/Io/Skylander.cpp
Normal file
171
rpcs3/Emu/Io/Skylander.cpp
Normal file
@ -0,0 +1,171 @@
|
||||
#include "stdafx.h"
|
||||
#include "Skylander.h"
|
||||
#include "Emu/Cell/lv2/sys_usbd.h"
|
||||
|
||||
LOG_CHANNEL(skylander_log);
|
||||
|
||||
sky_portal g_skylander;
|
||||
|
||||
void sky_portal::sky_load()
|
||||
{
|
||||
if (!sky_file)
|
||||
{
|
||||
skylander_log.error("Tried to load skylander but no skylander is active");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard lock(sky_mutex);
|
||||
|
||||
sky_file.seek(0, fs::seek_set);
|
||||
sky_file.read(sky_dump, 0x40 * 0x10);
|
||||
}
|
||||
}
|
||||
|
||||
void sky_portal::sky_save()
|
||||
{
|
||||
if (!sky_file)
|
||||
{
|
||||
skylander_log.error("Tried to save skylander to file but no skylander is active");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard lock(sky_mutex);
|
||||
|
||||
sky_file.seek(0, fs::seek_set);
|
||||
sky_file.write(sky_dump, 0x40 * 0x10);
|
||||
}
|
||||
}
|
||||
|
||||
usb_device_skylander::usb_device_skylander()
|
||||
{
|
||||
device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{0x0200, 0x00, 0x00, 0x00, 0x20, 0x1430, 0x0150, 0x0100, 0x01, 0x02, 0x00, 0x01});
|
||||
auto& config0 = device.add_node(UsbDescriptorNode(USB_DESCRIPTOR_CONFIG, UsbDeviceConfiguration{0x0029, 0x01, 0x01, 0x00, 0x80, 0x96}));
|
||||
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, UsbDeviceInterface{0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00}));
|
||||
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_HID, UsbDeviceHID{0x0111, 0x00, 0x01, 0x22, 0x001d}));
|
||||
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x81, 0x03, 0x0020, 0x01}));
|
||||
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x01, 0x03, 0x0020, 0x01}));
|
||||
}
|
||||
|
||||
usb_device_skylander::~usb_device_skylander()
|
||||
{
|
||||
}
|
||||
|
||||
void usb_device_skylander::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)
|
||||
{
|
||||
// HID Host 2 Device
|
||||
case 0x21:
|
||||
switch (bRequest)
|
||||
{
|
||||
case 0x09:
|
||||
transfer->expected_count = buf_size;
|
||||
transfer->expected_result = HC_CC_NOERR;
|
||||
// 100 usec, control transfers are very fast
|
||||
transfer->expected_time = get_timestamp() + 100;
|
||||
|
||||
std::array<u8, 32> q_result = {};
|
||||
|
||||
switch (buf[0])
|
||||
{
|
||||
case 'A':
|
||||
// Activate command
|
||||
verify(HERE), buf_size == 2;
|
||||
q_result = {0x41, buf[1], 0xFF, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00};
|
||||
|
||||
q_queries.push(q_result);
|
||||
break;
|
||||
case 'C':
|
||||
// Set LEDs colour
|
||||
verify(HERE), buf_size == 4;
|
||||
break;
|
||||
case 'Q':
|
||||
// Queries a block
|
||||
verify(HERE), buf_size == 3;
|
||||
|
||||
q_result[0] = 'Q';
|
||||
q_result[1] = 0x10;
|
||||
q_result[2] = buf[2];
|
||||
|
||||
{
|
||||
std::lock_guard lock(g_skylander.sky_mutex);
|
||||
memcpy(q_result.data() + 3, g_skylander.sky_dump + (16 * buf[2]), 16);
|
||||
}
|
||||
|
||||
q_queries.push(q_result);
|
||||
break;
|
||||
case 'R':
|
||||
// Reset
|
||||
verify(HERE), buf_size == 2;
|
||||
q_result = {
|
||||
0x52, 0x02, 0x0A, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
q_queries.push(q_result);
|
||||
break;
|
||||
case 'S':
|
||||
// ?
|
||||
verify(HERE), buf_size == 1;
|
||||
break;
|
||||
case 'W':
|
||||
// Write a block
|
||||
verify(HERE), buf_size == 19;
|
||||
q_result[0] = 'W';
|
||||
q_result[1] = 0x10;
|
||||
q_result[2] = buf[2];
|
||||
|
||||
{
|
||||
std::lock_guard lock(g_skylander.sky_mutex);
|
||||
memcpy(g_skylander.sky_dump + (buf[2] * 16), &buf[3], 16);
|
||||
}
|
||||
|
||||
g_skylander.sky_save();
|
||||
|
||||
q_queries.push(q_result);
|
||||
break;
|
||||
default: skylander_log.error("Unhandled Query Type: 0x%02X", buf[0]); break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Follow to default emulated handler
|
||||
usb_device_emulated::control_transfer(bmRequestType, bRequest, wValue, wIndex, wLength, buf_size, buf, transfer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_device_skylander::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer)
|
||||
{
|
||||
verify(HERE), buf_size == 0x20;
|
||||
|
||||
transfer->fake = true;
|
||||
transfer->expected_count = buf_size;
|
||||
transfer->expected_result = HC_CC_NOERR;
|
||||
// Interrupt transfers are slow(6ms, TODO accurate measurement)
|
||||
transfer->expected_time = get_timestamp() + 6000;
|
||||
|
||||
if (q_queries.size())
|
||||
{
|
||||
memcpy(buf, q_queries.front().data(), 0x20);
|
||||
q_queries.pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::lock_guard lock(g_skylander.sky_mutex);
|
||||
memset(buf, 0, 0x20);
|
||||
buf[0] = 0x53;
|
||||
// Varies per skylander type
|
||||
buf[1] = (g_skylander.sky_file && !g_skylander.sky_reload) ? 1 : 0;
|
||||
|
||||
buf[5] = interrupt_counter++;
|
||||
buf[6] = 0x01;
|
||||
|
||||
g_skylander.sky_reload = false;
|
||||
}
|
||||
}
|
32
rpcs3/Emu/Io/Skylander.h
Normal file
32
rpcs3/Emu/Io/Skylander.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Io/usb_device.h"
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
|
||||
struct sky_portal
|
||||
{
|
||||
std::mutex sky_mutex;
|
||||
fs::file sky_file;
|
||||
bool sky_reload = false;
|
||||
u8 sky_dump[0x40 * 0x10] = {};
|
||||
|
||||
void sky_save();
|
||||
void sky_load();
|
||||
};
|
||||
|
||||
extern sky_portal g_skylander;
|
||||
|
||||
class usb_device_skylander : public usb_device_emulated
|
||||
{
|
||||
public:
|
||||
usb_device_skylander();
|
||||
~usb_device_skylander();
|
||||
|
||||
void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override;
|
||||
void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override;
|
||||
|
||||
protected:
|
||||
u8 interrupt_counter = 0;
|
||||
std::queue<std::array<u8, 32>> q_queries;
|
||||
};
|
208
rpcs3/Emu/Io/usb_device.cpp
Normal file
208
rpcs3/Emu/Io/usb_device.cpp
Normal file
@ -0,0 +1,208 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Cell/lv2/sys_time.h"
|
||||
|
||||
#include "Emu/Cell/lv2/sys_usbd.h"
|
||||
#include "Emu/Io/usb_device.h"
|
||||
|
||||
extern logs::channel sys_usbd;
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// ALL DEVICES ///////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
void usb_device::read_descriptors()
|
||||
{
|
||||
}
|
||||
|
||||
bool usb_device::set_configuration(u8 cfg_num)
|
||||
{
|
||||
current_config = cfg_num;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool usb_device::set_interface(u8 int_num)
|
||||
{
|
||||
current_interface = int_num;
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 usb_device::get_timestamp()
|
||||
{
|
||||
return (get_system_time() - Emu.GetPauseTime());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// PASSTHROUGH DEVICE ////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////
|
||||
usb_device_passthrough::usb_device_passthrough(libusb_device* _device, libusb_device_descriptor& desc)
|
||||
: lusb_device(_device)
|
||||
{
|
||||
device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{desc.bcdUSB, desc.bDeviceClass, desc.bDeviceSubClass, desc.bDeviceProtocol, desc.bMaxPacketSize0, desc.idVendor, desc.idProduct,
|
||||
desc.bcdDevice, desc.iManufacturer, desc.iProduct, desc.iSerialNumber, desc.bNumConfigurations});
|
||||
}
|
||||
|
||||
usb_device_passthrough::~usb_device_passthrough()
|
||||
{
|
||||
if (lusb_handle)
|
||||
libusb_close(lusb_handle);
|
||||
|
||||
if (lusb_device)
|
||||
libusb_unref_device(lusb_device);
|
||||
}
|
||||
|
||||
bool usb_device_passthrough::open_device()
|
||||
{
|
||||
if (libusb_open(lusb_device, &lusb_handle) == LIBUSB_SUCCESS)
|
||||
{
|
||||
#ifdef __linux__
|
||||
libusb_set_auto_detach_kernel_driver(lusb_handle, true);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void usb_device_passthrough::read_descriptors()
|
||||
{
|
||||
// Directly getting configuration descriptors from the device instead of going through libusb parsing functions as they're not needed
|
||||
for (u8 index = 0; index < device._device.bNumConfigurations; index++)
|
||||
{
|
||||
u8 buf[1000];
|
||||
int ssize = libusb_control_transfer(lusb_handle, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE, LIBUSB_REQUEST_GET_DESCRIPTOR, 0x0200 | index, 0, buf, 1000, 0);
|
||||
if (ssize < 0)
|
||||
{
|
||||
sys_usbd.fatal("Couldn't get the config from the device: %d", ssize);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Minimalistic parse
|
||||
auto& conf = device.add_node(UsbDescriptorNode(buf[0], buf[1], &buf[2]));
|
||||
|
||||
for (int index = buf[0]; index < ssize;)
|
||||
{
|
||||
conf.add_node(UsbDescriptorNode(buf[index], buf[index + 1], &buf[index + 2]));
|
||||
index += buf[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool usb_device_passthrough::set_configuration(u8 cfg_num)
|
||||
{
|
||||
usb_device::set_configuration(cfg_num);
|
||||
return (libusb_set_configuration(lusb_handle, cfg_num) == LIBUSB_SUCCESS);
|
||||
};
|
||||
|
||||
bool usb_device_passthrough::set_interface(u8 int_num)
|
||||
{
|
||||
usb_device::set_interface(int_num);
|
||||
return (libusb_claim_interface(lusb_handle, int_num) == LIBUSB_SUCCESS);
|
||||
}
|
||||
|
||||
void usb_device_passthrough::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer)
|
||||
{
|
||||
if (transfer->setup_buf.size() < buf_size + 8)
|
||||
transfer->setup_buf.resize(buf_size + 8);
|
||||
|
||||
libusb_fill_control_setup(transfer->setup_buf.data(), bmRequestType, bRequest, wValue, wIndex, buf_size);
|
||||
memcpy(transfer->setup_buf.data() + 8, buf, buf_size);
|
||||
libusb_fill_control_transfer(transfer->transfer, lusb_handle, transfer->setup_buf.data(), callback_transfer, (void*)transfer, 0);
|
||||
libusb_submit_transfer(transfer->transfer);
|
||||
}
|
||||
|
||||
void usb_device_passthrough::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer)
|
||||
{
|
||||
libusb_fill_interrupt_transfer(transfer->transfer, lusb_handle, endpoint, buf, buf_size, callback_transfer, (void*)transfer, 0);
|
||||
libusb_submit_transfer(transfer->transfer);
|
||||
}
|
||||
|
||||
void usb_device_passthrough::isochronous_transfer(UsbTransfer* transfer)
|
||||
{
|
||||
// TODO actual endpoint
|
||||
// TODO actual size?
|
||||
libusb_fill_iso_transfer(transfer->transfer, lusb_handle, 0x81, (u8*)transfer->iso_request.buf.get_ptr(), 0xFFFF, transfer->iso_request.num_packets, callback_transfer, (void*)transfer, 0);
|
||||
|
||||
for (u32 index = 0; index < transfer->iso_request.num_packets; index++)
|
||||
{
|
||||
transfer->transfer->iso_packet_desc[index].length = transfer->iso_request.packets[index];
|
||||
}
|
||||
|
||||
libusb_submit_transfer(transfer->transfer);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// EMULATED DEVICE ///////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////
|
||||
usb_device_emulated::usb_device_emulated()
|
||||
{
|
||||
}
|
||||
|
||||
usb_device_emulated::usb_device_emulated(const UsbDeviceDescriptor& _device)
|
||||
{
|
||||
device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, _device);
|
||||
}
|
||||
|
||||
bool usb_device_emulated::open_device()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
s32 usb_device_emulated::get_descriptor(u8 type, u8 index, u8* ptr, u32 max_size)
|
||||
{
|
||||
if (type == USB_DESCRIPTOR_STRING)
|
||||
{
|
||||
if (index < strings.size())
|
||||
{
|
||||
u8 string_len = (u8)strings[index].size();
|
||||
ptr[0] = (string_len * 2) + 2;
|
||||
ptr[1] = USB_DESCRIPTOR_STRING;
|
||||
for (u32 i = 0; i < string_len; i++)
|
||||
{
|
||||
ptr[2 + (i * 2)] = strings[index].data()[i];
|
||||
ptr[3 + (i * 2)] = 0;
|
||||
}
|
||||
return (s32)ptr[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sys_usbd.error("[Emulated]: Trying to get a descriptor other than string descriptor");
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void usb_device_emulated::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer)
|
||||
{
|
||||
transfer->fake = true;
|
||||
transfer->expected_count = buf_size;
|
||||
transfer->expected_result = HC_CC_NOERR;
|
||||
transfer->expected_time = usb_device::get_timestamp() + 100;
|
||||
|
||||
switch (bmRequestType)
|
||||
{
|
||||
case 0:
|
||||
switch (bRequest)
|
||||
{
|
||||
case 0x09: usb_device::set_configuration(wValue); break;
|
||||
default: sys_usbd.fatal("Unhandled control transfer(0): 0x%x", bRequest); break;
|
||||
}
|
||||
break;
|
||||
default: sys_usbd.fatal("Unhandled control transfer: 0x%x", bmRequestType); break;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_device_emulated::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer)
|
||||
{
|
||||
}
|
||||
|
||||
void usb_device_emulated::isochronous_transfer(UsbTransfer* transfer)
|
||||
{
|
||||
}
|
||||
|
||||
void usb_device_emulated::add_string(char* str)
|
||||
{
|
||||
strings.push_back(str);
|
||||
}
|
198
rpcs3/Emu/Io/usb_device.h
Normal file
198
rpcs3/Emu/Io/usb_device.h
Normal file
@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
|
||||
#include <libusb.h>
|
||||
#include "Emu/Memory/vm.h"
|
||||
|
||||
struct UsbTransfer;
|
||||
|
||||
// Usb descriptors
|
||||
enum : u8
|
||||
{
|
||||
USB_DESCRIPTOR_DEVICE = 0x01,
|
||||
USB_DESCRIPTOR_CONFIG = 0x02,
|
||||
USB_DESCRIPTOR_STRING = 0x03,
|
||||
USB_DESCRIPTOR_INTERFACE = 0x04,
|
||||
USB_DESCRIPTOR_ENDPOINT = 0x05,
|
||||
USB_DESCRIPTOR_HID = 0x21,
|
||||
USB_DESCRIPTOR_ACI = 0x24,
|
||||
USB_DESCRIPTOR_ENDPOINT_ASI = 0x25,
|
||||
};
|
||||
|
||||
struct UsbDeviceDescriptor
|
||||
{
|
||||
le_t<u16, 1> bcdUSB;
|
||||
u8 bDeviceClass;
|
||||
u8 bDeviceSubClass;
|
||||
u8 bDeviceProtocol;
|
||||
u8 bMaxPacketSize0;
|
||||
le_t<u16, 1> idVendor;
|
||||
le_t<u16, 1> idProduct;
|
||||
le_t<u16, 1> bcdDevice;
|
||||
u8 iManufacturer;
|
||||
u8 iProduct;
|
||||
u8 iSerialNumber;
|
||||
u8 bNumConfigurations;
|
||||
};
|
||||
|
||||
struct UsbDeviceConfiguration
|
||||
{
|
||||
le_t<u16, 1> wTotalLength;
|
||||
u8 bNumInterfaces;
|
||||
u8 bConfigurationValue;
|
||||
u8 iConfiguration;
|
||||
u8 bmAttributes;
|
||||
u8 bMaxPower;
|
||||
};
|
||||
|
||||
struct UsbDeviceInterface
|
||||
{
|
||||
u8 bInterfaceNumber;
|
||||
u8 bAlternateSetting;
|
||||
u8 bNumEndpoints;
|
||||
u8 bInterfaceClass;
|
||||
u8 bInterfaceSubClass;
|
||||
u8 bInterfaceProtocol;
|
||||
u8 iInterface;
|
||||
};
|
||||
|
||||
struct UsbDeviceEndpoint
|
||||
{
|
||||
u8 bEndpointAddress;
|
||||
u8 bmAttributes;
|
||||
le_t<u16, 1> wMaxPacketSize;
|
||||
u8 bInterval;
|
||||
};
|
||||
|
||||
struct UsbDeviceHID
|
||||
{
|
||||
le_t<u16, 1> bcdHID;
|
||||
u8 bCountryCode;
|
||||
u8 bNumDescriptors;
|
||||
u8 bDescriptorType;
|
||||
le_t<u16, 1> wDescriptorLength;
|
||||
};
|
||||
|
||||
// Usb descriptor helper
|
||||
struct UsbDescriptorNode
|
||||
{
|
||||
u8 bLength;
|
||||
u8 bDescriptorType;
|
||||
|
||||
union {
|
||||
UsbDeviceDescriptor _device;
|
||||
UsbDeviceConfiguration _configuration;
|
||||
UsbDeviceInterface _interface;
|
||||
UsbDeviceEndpoint _endpoint;
|
||||
UsbDeviceHID _hid;
|
||||
u8 data[0xFF];
|
||||
};
|
||||
|
||||
std::vector<UsbDescriptorNode> subnodes;
|
||||
|
||||
UsbDescriptorNode(){};
|
||||
template <typename T>
|
||||
UsbDescriptorNode(u8 _bDescriptorType, const T& _data)
|
||||
: bLength(sizeof(T) + 2)
|
||||
, bDescriptorType(_bDescriptorType)
|
||||
{
|
||||
memcpy(data, &_data, sizeof(T));
|
||||
}
|
||||
UsbDescriptorNode(u8 _bLength, u8 _bDescriptorType, u8* _data)
|
||||
: bLength(_bLength)
|
||||
, bDescriptorType(_bDescriptorType)
|
||||
{
|
||||
memcpy(data, _data, _bLength - 2);
|
||||
}
|
||||
|
||||
UsbDescriptorNode& add_node(const UsbDescriptorNode& newnode)
|
||||
{
|
||||
subnodes.push_back(newnode);
|
||||
return subnodes.back();
|
||||
}
|
||||
u32 get_size() const
|
||||
{
|
||||
u32 nodesize = bLength;
|
||||
for (const auto& node : subnodes)
|
||||
{
|
||||
nodesize += node.get_size();
|
||||
}
|
||||
return nodesize;
|
||||
}
|
||||
void write_data(u8*& ptr)
|
||||
{
|
||||
ptr[0] = bLength;
|
||||
ptr[1] = bDescriptorType;
|
||||
memcpy(ptr + 2, data, bLength - 2);
|
||||
ptr += bLength;
|
||||
for (auto& node : subnodes)
|
||||
{
|
||||
node.write_data(ptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class usb_device
|
||||
{
|
||||
public:
|
||||
virtual bool open_device() = 0;
|
||||
|
||||
virtual void read_descriptors();
|
||||
|
||||
virtual bool set_configuration(u8 cfg_num);
|
||||
virtual bool set_interface(u8 int_num);
|
||||
|
||||
virtual void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) = 0;
|
||||
virtual void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) = 0;
|
||||
virtual void isochronous_transfer(UsbTransfer* transfer) = 0;
|
||||
|
||||
public:
|
||||
// device ID if the device has been ldded(0 otherwise)
|
||||
u32 assigned_number = 0;
|
||||
// base device descriptor, every other descriptor is a subnode
|
||||
UsbDescriptorNode device;
|
||||
|
||||
protected:
|
||||
u8 current_config = 1;
|
||||
u8 current_interface = 0;
|
||||
|
||||
protected:
|
||||
static u64 get_timestamp();
|
||||
};
|
||||
|
||||
class usb_device_passthrough : public usb_device
|
||||
{
|
||||
public:
|
||||
usb_device_passthrough(libusb_device* _device, libusb_device_descriptor& desc);
|
||||
~usb_device_passthrough();
|
||||
|
||||
bool open_device() override;
|
||||
void read_descriptors() override;
|
||||
bool set_configuration(u8 cfg_num) override;
|
||||
bool set_interface(u8 int_num) override;
|
||||
void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override;
|
||||
void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override;
|
||||
void isochronous_transfer(UsbTransfer* transfer) override;
|
||||
|
||||
protected:
|
||||
libusb_device* lusb_device = nullptr;
|
||||
libusb_device_handle* lusb_handle = nullptr;
|
||||
};
|
||||
|
||||
class usb_device_emulated : public usb_device
|
||||
{
|
||||
public:
|
||||
usb_device_emulated();
|
||||
usb_device_emulated(const UsbDeviceDescriptor& _device);
|
||||
|
||||
bool open_device() override;
|
||||
void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override;
|
||||
void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override;
|
||||
void isochronous_transfer(UsbTransfer* transfer) override;
|
||||
|
||||
// Emulated specific functions
|
||||
void add_string(char* str);
|
||||
s32 get_descriptor(u8 type, u8 index, u8* ptr, u32 max_size);
|
||||
|
||||
protected:
|
||||
std::vector<std::string> strings;
|
||||
};
|
@ -61,7 +61,7 @@
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\zlib;..\llvm\include;..\llvm_build\include;$(VULKAN_SDK)\Include</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\libusb\libusb;..\3rdparty\zlib;..\llvm\include;..\llvm_build\include;$(VULKAN_SDK)\Include</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<PreBuildEvent>
|
||||
<Command>%windir%\sysnative\cmd.exe /c "$(SolutionDir)\Utilities\git-version-gen.cmd"</Command>
|
||||
@ -297,6 +297,8 @@
|
||||
<ClCompile Include="Emu\Cell\SPUDisAsm.cpp" />
|
||||
<ClCompile Include="Emu\Cell\SPUInterpreter.cpp" />
|
||||
<ClCompile Include="Emu\IdManager.cpp" />
|
||||
<ClCompile Include="Emu\Io\Skylander.cpp" />
|
||||
<ClCompile Include="Emu\Io\usb_device.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Capture\rsx_capture.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Capture\rsx_replay.cpp" />
|
||||
<ClCompile Include="Emu\RSX\CgBinaryFragmentProgram.cpp" />
|
||||
@ -444,6 +446,8 @@
|
||||
<ClInclude Include="Emu\Cell\PPUAnalyser.h" />
|
||||
<ClInclude Include="Emu\Cell\PPUTranslator.h" />
|
||||
<ClInclude Include="Emu\CPU\CPUTranslator.h" />
|
||||
<ClInclude Include="Emu\Io\Skylander.h" />
|
||||
<ClInclude Include="Emu\Io\usb_device.h" />
|
||||
<ClInclude Include="Emu\IPC.h" />
|
||||
<ClInclude Include="Emu\Audio\AudioDumper.h" />
|
||||
<ClInclude Include="Emu\Audio\AudioBackend.h" />
|
||||
|
@ -782,6 +782,9 @@
|
||||
<ClCompile Include="Emu\Cell\lv2\sys_config.cpp">
|
||||
<Filter>Emu\Cell\lv2</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Io\Skylander.cpp">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Cell\lv2\sys_overlay.cpp">
|
||||
<Filter>Emu\Cell\lv2</Filter>
|
||||
</ClCompile>
|
||||
@ -818,6 +821,9 @@
|
||||
<ClCompile Include="Crypto\md5.cpp">
|
||||
<Filter>Crypto</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Io\usb_device.cpp">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Crypto\aes.h">
|
||||
@ -1552,5 +1558,8 @@
|
||||
<ClInclude Include="Crypto\md5.h">
|
||||
<Filter>Crypto</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Io\usb_device.h">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1108,6 +1108,7 @@
|
||||
<ClCompile Include="rpcs3qt\gui_application.cpp" />
|
||||
<ClCompile Include="rpcs3qt\input_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\osk_dialog_frame.cpp" />
|
||||
<ClCompile Include="rpcs3qt\skylander_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\_discord_utils.cpp" />
|
||||
<ClCompile Include="rpcs3qt\find_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\game_compatibility.cpp" />
|
||||
@ -1649,6 +1650,7 @@
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -D%(PreprocessorDefinitions) "-I$(VULKAN_SDK)\Include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"</Command>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="rpcs3qt\stylesheets.h" />
|
||||
<ClInclude Include="rpcs3qt\skylander_dialog.h" />
|
||||
<ClInclude Include="rpcs3qt\_discord_utils.h" />
|
||||
<ClInclude Include="rpcs3qt\find_dialog.h" />
|
||||
<ClInclude Include="rpcs3qt\custom_table_widget_item.h" />
|
||||
|
@ -111,6 +111,9 @@
|
||||
<Filter Include="Io\DS3">
|
||||
<UniqueIdentifier>{66e6027b-d3dd-4894-814c-cc4444a4c7df}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Gui\skylanders">
|
||||
<UniqueIdentifier>{c25f8f80-cc74-4760-8488-a291b3026b1d}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="\rpcs3qt\*.cpp">
|
||||
@ -743,6 +746,9 @@
|
||||
<ClCompile Include="headless_application.cpp">
|
||||
<Filter>rpcs3</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\skylander_dialog.cpp">
|
||||
<Filter>Gui\skylanders</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="\rpcs3qt\*.h">
|
||||
@ -832,6 +838,9 @@
|
||||
<ClInclude Include="rpcs3qt\stylesheets.h">
|
||||
<Filter>Gui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\skylander_dialog.h">
|
||||
<Filter>Gui\skylanders</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
|
@ -37,6 +37,7 @@
|
||||
save_data_list_dialog.cpp
|
||||
save_manager_dialog.cpp
|
||||
settings_dialog.cpp
|
||||
skylander_dialog.cpp
|
||||
syntax_highlighter.cpp
|
||||
trophy_manager_dialog.cpp
|
||||
trophy_notification_frame.cpp
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "about_dialog.h"
|
||||
#include "pad_settings_dialog.h"
|
||||
#include "progress_dialog.h"
|
||||
#include "skylander_dialog.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
@ -1271,6 +1272,12 @@ void main_window::CreateConnects()
|
||||
trop_manager->show();
|
||||
});
|
||||
|
||||
connect(ui->actionManage_Skylanders_Portal, &QAction::triggered, [=]
|
||||
{
|
||||
skylander_dialog* sky_diag = skylander_dialog::get_dlg(this);
|
||||
sky_diag->show();
|
||||
});
|
||||
|
||||
connect(ui->actionManage_Users, &QAction::triggered, [=]
|
||||
{
|
||||
user_manager_dialog user_manager(guiSettings, this);
|
||||
|
@ -227,6 +227,7 @@
|
||||
<addaction name="confSavedataManagerAct"/>
|
||||
<addaction name="actionManage_RAP_Licences"/>
|
||||
<addaction name="actionManage_Trophy_Data"/>
|
||||
<addaction name="actionManage_Skylanders_Portal"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuUtilities">
|
||||
<property name="title">
|
||||
@ -1008,6 +1009,11 @@
|
||||
<string>Remove Disk Cache</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionManage_Skylanders_Portal">
|
||||
<property name="text">
|
||||
<string>Skylanders Portal</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
|
556
rpcs3/rpcs3qt/skylander_dialog.cpp
Normal file
556
rpcs3/rpcs3qt/skylander_dialog.cpp
Normal file
@ -0,0 +1,556 @@
|
||||
#include <mutex>
|
||||
#include "Emu/System.h"
|
||||
#include "Utilities/File.h"
|
||||
#include "Crypto/md5.h"
|
||||
#include "Crypto/aes.h"
|
||||
#include "skylander_dialog.h"
|
||||
#include "Utilities/BEType.h"
|
||||
#include "Emu/Io/Skylander.h"
|
||||
|
||||
skylander_dialog* skylander_dialog::inst = nullptr;
|
||||
|
||||
const std::map<const u16, const std::string> list_skylanders = {
|
||||
{0, "Whirlwind"},
|
||||
{1, "Sonic Boom"},
|
||||
{2, "Warnado"},
|
||||
{3, "Lightning Rod"},
|
||||
{4, "Bash"},
|
||||
{5, "Terrafin"},
|
||||
{6, "Dino-Rang"},
|
||||
{7, "Prism Break"},
|
||||
{8, "Sunburn"},
|
||||
{9, "Eruptor"},
|
||||
{10, "Ignitor"},
|
||||
{11, "Flameslinger"},
|
||||
{12, "Zap"},
|
||||
{13, "Wham-Shell"},
|
||||
{14, "Gill Grunt"},
|
||||
{15, "Slam Bam"},
|
||||
{16, "Spyro"},
|
||||
{17, "Voodood"},
|
||||
{18, "Double Trouble"},
|
||||
{19, "Trigger Happy"},
|
||||
{20, "Drobot"},
|
||||
{21, "Drill Sergeant"},
|
||||
{22, "Boomer"},
|
||||
{23, "Wrecking Ball"},
|
||||
{24, "Camo"},
|
||||
{25, "Zook"},
|
||||
{26, "Stealth Elf"},
|
||||
{27, "Stump Smash"},
|
||||
{28, "Dark Spyro"},
|
||||
{29, "Hex"},
|
||||
{30, "Chop Chop"},
|
||||
{31, "Ghost Roaster"},
|
||||
{32, "Cynder"},
|
||||
{100, "Jet Vac"},
|
||||
{101, "Swarm"},
|
||||
{102, "Crusher"},
|
||||
{103, "Flashwing"},
|
||||
{104, "Hot Head"},
|
||||
{105, "Hot Dog"},
|
||||
{106, "Chill"},
|
||||
{107, "Thumpback"},
|
||||
{108, "Pop Fizz"},
|
||||
{109, "Ninjini"},
|
||||
{110, "Bouncer"},
|
||||
{111, "Sprocket"},
|
||||
{112, "Tree Rex"},
|
||||
{113, "Shroomboom"},
|
||||
{114, "Eye-Brawl"},
|
||||
{115, "Fright Rider"},
|
||||
{200, "Anvil Rain"},
|
||||
{201, "Treasure Chest"},
|
||||
{202, "Healing Elixer"},
|
||||
{203, "Ghost Swords"},
|
||||
{204, "Time Twister"},
|
||||
{205, "Sky-Iron Shield"},
|
||||
{206, "Winged Boots"},
|
||||
{207, "Sparx Dragonfly"},
|
||||
{208, "Dragonfire Cannon"},
|
||||
{209, "Scorpion Striker Catapult"},
|
||||
{230, "Hand Of Fate"},
|
||||
{231, "Piggy Bank"},
|
||||
{232, "Rocket Ram"},
|
||||
{233, "Tiki Speaky"},
|
||||
{300, "Dragons Peak"},
|
||||
{301, "Empire of Ice"},
|
||||
{302, "Pirate Seas"},
|
||||
{303, "Darklight Crypt"},
|
||||
{304, "Volcanic Vault"},
|
||||
{305, "Mirror Of Mystery"},
|
||||
{306, "Nightmare Express"},
|
||||
{307, "Sunscraper Spire"},
|
||||
{308, "Midnight Museum"},
|
||||
{404, "Bash"},
|
||||
{416, "Spyro"},
|
||||
{419, "Trigger Happy"},
|
||||
{430, "Chop Chop"},
|
||||
{450, "Gusto"},
|
||||
{451, "Thunderbolt"},
|
||||
{452, "Fling Kong"},
|
||||
{453, "Blades"},
|
||||
{454, "Wallop"},
|
||||
{455, "Head Rush"},
|
||||
{456, "Fist Bump"},
|
||||
{457, "Rocky Roll"},
|
||||
{458, "Wildfire"},
|
||||
{459, "Ka Boom"},
|
||||
{460, "Trail Blazer"},
|
||||
{461, "Torch"},
|
||||
{462, "Snap Shot"},
|
||||
{463, "Lob Star"},
|
||||
{464, "Flip Wreck"},
|
||||
{465, "Echo"},
|
||||
{466, "Blastermind"},
|
||||
{467, "Enigma"},
|
||||
{468, "Deja Vu"},
|
||||
{469, "Cobra Cadabra"},
|
||||
{470, "Jawbreaker"},
|
||||
{471, "Gearshift"},
|
||||
{472, "Chopper"},
|
||||
{473, "Tread Head"},
|
||||
{474, "Bushwhack"},
|
||||
{475, "Tuff Luck"},
|
||||
{476, "Food Fight"},
|
||||
{477, "High Five"},
|
||||
{478, "Krypt King"},
|
||||
{479, "Short Cut"},
|
||||
{480, "Bat Spin"},
|
||||
{481, "Funny Bone"},
|
||||
{482, "Knight light"},
|
||||
{483, "Spotlight"},
|
||||
{484, "Knight Mare"},
|
||||
{485, "Blackout"},
|
||||
{502, "Bop"},
|
||||
{503, "Spry"},
|
||||
{504, "Hijinx"},
|
||||
{505, "Terrabite"},
|
||||
{506, "Breeze"},
|
||||
{507, "Weeruptor"},
|
||||
{508, "Pet Vac"},
|
||||
{509, "Small Fry"},
|
||||
{510, "Drobit"},
|
||||
{514, "Gill Runt"},
|
||||
{519, "Trigger Snappy"},
|
||||
{526, "Whisper Elf"},
|
||||
{540, "Barkley"},
|
||||
{541, "Thumpling"},
|
||||
{542, "Mini Jini"},
|
||||
{543, "Eye Small"},
|
||||
{1004, "Blast Zone"},
|
||||
{1015, "Wash Buckler"},
|
||||
{2004, "Blast Zone (Head)"},
|
||||
{2015, "Wash Buckler (Head)"},
|
||||
{3000, "Scratch"},
|
||||
{3001, "Pop Thorn"},
|
||||
{3002, "Slobber Tooth"},
|
||||
{3003, "Scorp"},
|
||||
{3004, "Fryno"},
|
||||
{3005, "Smolderdash"},
|
||||
{3006, "Bumble Blast"},
|
||||
{3007, "Zoo Lou"},
|
||||
{3008, "Dune Bug"},
|
||||
{3009, "Star Strike"},
|
||||
{3010, "Countdown"},
|
||||
{3011, "Wind Up"},
|
||||
{3012, "Roller Brawl"},
|
||||
{3013, "Grim Creeper"},
|
||||
{3014, "Rip Tide"},
|
||||
{3015, "Punk Shock"},
|
||||
};
|
||||
|
||||
QString cur_sky_file_path;
|
||||
|
||||
skylander_dialog::skylander_dialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Skylanders Manager"));
|
||||
setObjectName("skylanders_manager");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setMinimumSize(QSize(700, 450));
|
||||
|
||||
QVBoxLayout* vbox_panel = new QVBoxLayout();
|
||||
|
||||
QHBoxLayout* hbox_buttons = new QHBoxLayout();
|
||||
QPushButton* button_new = new QPushButton(tr("New"), this);
|
||||
QPushButton* button_load = new QPushButton(tr("Load"), this);
|
||||
hbox_buttons->addWidget(button_new);
|
||||
hbox_buttons->addWidget(button_load);
|
||||
hbox_buttons->addStretch();
|
||||
vbox_panel->addLayout(hbox_buttons);
|
||||
|
||||
edit_curfile = new QLineEdit(cur_sky_file_path);
|
||||
edit_curfile->setEnabled(false);
|
||||
vbox_panel->addWidget(edit_curfile);
|
||||
|
||||
QGroupBox* group_skyinfo = new QGroupBox(tr("Skylander Info"));
|
||||
QVBoxLayout* vbox_group = new QVBoxLayout();
|
||||
combo_skylist = new QComboBox();
|
||||
for (auto& entry : list_skylanders)
|
||||
{
|
||||
combo_skylist->addItem(QString::fromStdString(entry.second), QVariant((int)entry.first));
|
||||
}
|
||||
|
||||
combo_skylist->addItem(tr("--Unknown--"), QVariant(0xFFFF));
|
||||
|
||||
QLabel* label_skyid = new QLabel(tr("Skylander ID:"));
|
||||
edit_skyid = new QLineEdit();
|
||||
QLabel* label_skyxp = new QLabel(tr("Skylander XP:"));
|
||||
edit_skyxp = new QLineEdit();
|
||||
QLabel* label_skymoney = new QLabel(tr("Skylander Money:"));
|
||||
edit_skymoney = new QLineEdit();
|
||||
|
||||
vbox_group->addWidget(combo_skylist);
|
||||
vbox_group->addWidget(label_skyid);
|
||||
vbox_group->addWidget(edit_skyid);
|
||||
vbox_group->addWidget(label_skyxp);
|
||||
vbox_group->addWidget(edit_skyxp);
|
||||
vbox_group->addWidget(label_skymoney);
|
||||
vbox_group->addWidget(edit_skymoney);
|
||||
|
||||
QHBoxLayout* sub_group = new QHBoxLayout();
|
||||
sub_group->addStretch();
|
||||
button_update = new QPushButton(tr("Update"), this);
|
||||
sub_group->addWidget(button_update);
|
||||
vbox_group->addLayout(sub_group);
|
||||
|
||||
vbox_group->addStretch();
|
||||
group_skyinfo->setLayout(vbox_group);
|
||||
|
||||
vbox_panel->addWidget(group_skyinfo);
|
||||
|
||||
setLayout(vbox_panel);
|
||||
|
||||
connect(button_new, &QAbstractButton::clicked, this, &skylander_dialog::new_skylander);
|
||||
connect(button_load, &QAbstractButton::clicked, this, &skylander_dialog::load_skylander);
|
||||
connect(button_update, &QAbstractButton::clicked, this, &skylander_dialog::process_edits);
|
||||
|
||||
update_edits();
|
||||
|
||||
// clang-format off
|
||||
connect(combo_skylist, &QComboBox::currentTextChanged, this, [&]()
|
||||
{
|
||||
{
|
||||
std::lock_guard lock(g_skylander.sky_mutex);
|
||||
u16 sky_id = combo_skylist->itemData(combo_skylist->currentIndex()).toInt();
|
||||
if (sky_id != 0xFFFF)
|
||||
{
|
||||
(le_t<u32>&)g_skylander.sky_dump[0] = (u16)combo_skylist->itemData(combo_skylist->currentIndex()).toInt();
|
||||
(le_t<u16>&)g_skylander.sky_dump[0x10] = (u16)combo_skylist->itemData(combo_skylist->currentIndex()).toInt();
|
||||
(le_t<u16>&)g_skylander.sky_dump[0x1E] = skylander_crc16(0xFFFF, g_skylander.sky_dump, 0x1E);
|
||||
|
||||
std::array<u8, 16> zero_array = {};
|
||||
for (u32 index = 8; index < 0x40; index++)
|
||||
{
|
||||
if ((index + 1) % 4)
|
||||
{
|
||||
set_block(index, zero_array);
|
||||
}
|
||||
}
|
||||
|
||||
set_checksums();
|
||||
|
||||
g_skylander.sky_reload = true;
|
||||
}
|
||||
}
|
||||
|
||||
g_skylander.sky_save();
|
||||
update_edits();
|
||||
});
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
skylander_dialog::~skylander_dialog()
|
||||
{
|
||||
inst = nullptr;
|
||||
}
|
||||
|
||||
skylander_dialog* skylander_dialog::get_dlg(QWidget* parent)
|
||||
{
|
||||
if (inst == nullptr)
|
||||
inst = new skylander_dialog(parent);
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
u16 skylander_dialog::skylander_crc16(u16 init_value, const u8* buffer, u32 size)
|
||||
{
|
||||
const unsigned short CRC_CCITT_TABLE[256] = {0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273,
|
||||
0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528,
|
||||
0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886,
|
||||
0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF,
|
||||
0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5,
|
||||
0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2,
|
||||
0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8,
|
||||
0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691,
|
||||
0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F,
|
||||
0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64,
|
||||
0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0};
|
||||
|
||||
u16 tmp;
|
||||
u16 crc = init_value;
|
||||
|
||||
for (u32 i = 0; i < size; i++)
|
||||
{
|
||||
tmp = (crc >> 8) ^ buffer[i];
|
||||
crc = (crc << 8) ^ CRC_CCITT_TABLE[tmp];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
void skylander_dialog::get_hash(u8 block, std::array<u8, 16>& res_hash)
|
||||
{
|
||||
const u8 hash_magic[0x35] = {0x20, 0x43, 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x43, 0x29, 0x20, 0x32, 0x30, 0x31, 0x30, 0x20, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x73, 0x69,
|
||||
0x6F, 0x6E, 0x2E, 0x20, 0x41, 0x6C, 0x6C, 0x20, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x2E, 0x20};
|
||||
|
||||
mbedtls_md5_context md5_ctx;
|
||||
|
||||
mbedtls_md5_init(&md5_ctx);
|
||||
mbedtls_md5_starts_ret(&md5_ctx);
|
||||
mbedtls_md5_update_ret(&md5_ctx, g_skylander.sky_dump, 0x20);
|
||||
mbedtls_md5_update_ret(&md5_ctx, &block, 1);
|
||||
mbedtls_md5_update_ret(&md5_ctx, hash_magic, 0x35);
|
||||
mbedtls_md5_finish_ret(&md5_ctx, res_hash.data());
|
||||
}
|
||||
|
||||
void skylander_dialog::set_block(const u8 block, const std::array<u8, 16>& to_encrypt)
|
||||
{
|
||||
std::array<u8, 16> hash_result;
|
||||
get_hash(block, hash_result);
|
||||
|
||||
aes_context aes_ctx;
|
||||
aes_setkey_enc(&aes_ctx, hash_result.data(), 128);
|
||||
|
||||
u8 res_buf[16];
|
||||
aes_crypt_ecb(&aes_ctx, AES_ENCRYPT, to_encrypt.data(), res_buf);
|
||||
|
||||
memcpy(g_skylander.sky_dump + (block * 16), res_buf, 16);
|
||||
}
|
||||
|
||||
void skylander_dialog::get_block(const u8 block, std::array<u8, 16>& decrypted)
|
||||
{
|
||||
std::array<u8, 16> hash_result;
|
||||
get_hash(block, hash_result);
|
||||
|
||||
aes_context aes_ctx;
|
||||
aes_setkey_dec(&aes_ctx, hash_result.data(), 128);
|
||||
|
||||
u8 res_buf[16];
|
||||
aes_crypt_ecb(&aes_ctx, AES_DECRYPT, g_skylander.sky_dump + (block * 16), res_buf);
|
||||
|
||||
memcpy(decrypted.data(), res_buf, 16);
|
||||
}
|
||||
|
||||
u8 skylander_dialog::get_active_block()
|
||||
{
|
||||
std::array<u8, 16> dec_0x08;
|
||||
std::array<u8, 16> dec_0x24;
|
||||
get_block(0x08, dec_0x08);
|
||||
get_block(0x24, dec_0x24);
|
||||
|
||||
return (dec_0x08[9] < dec_0x24[9]) ? 0x24 : 0x08;
|
||||
}
|
||||
|
||||
void skylander_dialog::set_checksums()
|
||||
{
|
||||
const std::array<u8, 2> sectors = {0x08, 0x24};
|
||||
|
||||
for (const auto& active : sectors)
|
||||
{
|
||||
// clang-format off
|
||||
// Decrypt and hash a bunch of blocks
|
||||
auto do_crc_blocks = [&](const u16 start_crc, const std::vector<u8>& blocks) -> u16
|
||||
{
|
||||
u16 cur_crc = start_crc;
|
||||
std::array<u8, 16> decrypted_block;
|
||||
for (const auto& b : blocks)
|
||||
{
|
||||
get_block(active + b, decrypted_block);
|
||||
cur_crc = skylander_crc16(cur_crc, decrypted_block.data(), 16);
|
||||
}
|
||||
return cur_crc;
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
std::array<u8, 16> decrypted_header, sub_header;
|
||||
get_block(active, decrypted_header);
|
||||
get_block(active + 9, sub_header);
|
||||
|
||||
// Type 4
|
||||
(le_t<u16>&)sub_header[0x0] = 0x0106;
|
||||
u16 res_crc = skylander_crc16(0xFFFF, sub_header.data(), 16);
|
||||
(le_t<u16>&)sub_header[0x0] = do_crc_blocks(res_crc, {10, 12, 13});
|
||||
|
||||
// Type 3
|
||||
std::array<u8, 16> zero_block = {};
|
||||
res_crc = do_crc_blocks(0xFFFF, {5, 6, 8});
|
||||
for (u32 index = 0; index < 0x0E; index++)
|
||||
{
|
||||
res_crc = skylander_crc16(res_crc, zero_block.data(), 16);
|
||||
}
|
||||
(le_t<u16>&)decrypted_header[0xA] = res_crc;
|
||||
|
||||
// Type 2
|
||||
res_crc = do_crc_blocks(0xFFFF, {1, 2, 4});
|
||||
(le_t<u16>&)decrypted_header[0xC] = res_crc;
|
||||
|
||||
// Type 1
|
||||
(le_t<u16>&)decrypted_header[0xE] = 5;
|
||||
(le_t<u16>&)decrypted_header[0xE] = skylander_crc16(0xFFFF, decrypted_header.data(), 16);
|
||||
|
||||
set_block(active, decrypted_header);
|
||||
set_block(active + 9, sub_header);
|
||||
}
|
||||
}
|
||||
|
||||
void skylander_dialog::new_skylander()
|
||||
{
|
||||
const QString file_path = QFileDialog::getSaveFileName(this, tr("Create Skylander File"), cur_sky_file_path, tr("Skylander Object (*.sky);;"));
|
||||
if (file_path.isEmpty())
|
||||
return;
|
||||
|
||||
if (g_skylander.sky_file)
|
||||
g_skylander.sky_file.close();
|
||||
|
||||
g_skylander.sky_file.open(file_path.toStdString(), fs::read + fs::write + fs::create);
|
||||
if (!g_skylander.sky_file)
|
||||
return;
|
||||
|
||||
cur_sky_file_path = file_path;
|
||||
|
||||
{
|
||||
std::lock_guard lock(g_skylander.sky_mutex);
|
||||
memset(g_skylander.sky_dump, 0, 0x40 * 0x10);
|
||||
|
||||
// Set the block permissions
|
||||
(le_t<u32>&)g_skylander.sky_dump[0x36] = 0x690F0F0F;
|
||||
for (u32 index = 1; index < 0x10; index++)
|
||||
{
|
||||
(le_t<u32>&)g_skylander.sky_dump[(index * 0x40) + 0x36] = 0x69080F7F;
|
||||
}
|
||||
|
||||
(le_t<u16>&)g_skylander.sky_dump[0x1E] = skylander_crc16(0xFFFF, g_skylander.sky_dump, 0x1E);
|
||||
|
||||
std::array<u8, 16> zero_array = {};
|
||||
for (u32 index = 8; index < 0x40; index++)
|
||||
{
|
||||
if ((index + 1) % 4)
|
||||
{
|
||||
set_block(index, zero_array);
|
||||
}
|
||||
}
|
||||
|
||||
set_checksums();
|
||||
|
||||
g_skylander.sky_reload = true;
|
||||
}
|
||||
|
||||
g_skylander.sky_save();
|
||||
|
||||
edit_curfile->setText(file_path);
|
||||
update_edits();
|
||||
}
|
||||
|
||||
void skylander_dialog::load_skylander()
|
||||
{
|
||||
const QString file_path = QFileDialog::getOpenFileName(this, tr("Select Skylander File"), cur_sky_file_path, tr("Skylander (*.sky);;"));
|
||||
if (file_path.isEmpty())
|
||||
return;
|
||||
|
||||
if (g_skylander.sky_file)
|
||||
g_skylander.sky_file.close();
|
||||
|
||||
g_skylander.sky_file.open(file_path.toStdString(), fs::read + fs::write);
|
||||
if (!g_skylander.sky_file)
|
||||
return;
|
||||
|
||||
cur_sky_file_path = file_path;
|
||||
|
||||
g_skylander.sky_load();
|
||||
|
||||
edit_curfile->setText(file_path);
|
||||
update_edits();
|
||||
}
|
||||
|
||||
void skylander_dialog::update_edits()
|
||||
{
|
||||
// clang-format off
|
||||
auto widget_enabler = [&](bool status)
|
||||
{
|
||||
combo_skylist->setEnabled(status);
|
||||
edit_skyid->setEnabled(status);
|
||||
edit_skyxp->setEnabled(status);
|
||||
edit_skymoney->setEnabled(status);
|
||||
button_update->setEnabled(status);
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
if (!g_skylander.sky_file)
|
||||
{
|
||||
widget_enabler(false);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
widget_enabler(true);
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard lock(g_skylander.sky_mutex);
|
||||
edit_skyid->setText(QString::number((le_t<u16>&)g_skylander.sky_dump[0x10]));
|
||||
|
||||
u8 active = get_active_block();
|
||||
|
||||
std::array<u8, 16> decrypted;
|
||||
get_block(active, decrypted);
|
||||
u32 xp = ((le_t<u32>&)decrypted.data()[0]) & 0xFFFFFF;
|
||||
edit_skyxp->setText(QString::number(xp));
|
||||
u16 money = (le_t<u16>&)decrypted[3];
|
||||
edit_skymoney->setText(QString::number(money));
|
||||
}
|
||||
}
|
||||
|
||||
void skylander_dialog::process_edits()
|
||||
{
|
||||
{
|
||||
std::lock_guard lock(g_skylander.sky_mutex);
|
||||
|
||||
bool cast_success = false;
|
||||
u16 skyID = edit_skyid->text().toInt(&cast_success);
|
||||
if (cast_success)
|
||||
{
|
||||
(le_t<u16>&)g_skylander.sky_dump[0x10] = skyID;
|
||||
(le_t<u16>&)g_skylander.sky_dump[0] = skyID;
|
||||
}
|
||||
|
||||
(le_t<u16>&)g_skylander.sky_dump[0x1E] = skylander_crc16(0xFFFF, g_skylander.sky_dump, 0x1E);
|
||||
|
||||
u8 active = get_active_block();
|
||||
|
||||
std::array<u8, 16> decrypted_header;
|
||||
get_block(active, decrypted_header);
|
||||
|
||||
u32 old_xp = ((le_t<u32>&)decrypted_header.data()[0]) & 0xFFFFFF;
|
||||
u16 old_money = (le_t<u16>&)decrypted_header[3];
|
||||
|
||||
u32 xp = edit_skyxp->text().toInt(&cast_success);
|
||||
if (!cast_success)
|
||||
xp = old_xp;
|
||||
u32 money = edit_skymoney->text().toInt(&cast_success);
|
||||
if (!cast_success)
|
||||
money = old_money;
|
||||
|
||||
memcpy(decrypted_header.data(), &xp, 3);
|
||||
(le_t<u16>&)decrypted_header[3] = money;
|
||||
|
||||
set_block(active, decrypted_header);
|
||||
|
||||
set_checksums();
|
||||
|
||||
g_skylander.sky_reload = true;
|
||||
}
|
||||
|
||||
g_skylander.sky_save();
|
||||
}
|
57
rpcs3/rpcs3qt/skylander_dialog.h
Normal file
57
rpcs3/rpcs3qt/skylander_dialog.h
Normal file
@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QGroupBox>
|
||||
#include <QComboBox>
|
||||
#include <QLineEdit>
|
||||
#include <QLabel>
|
||||
#include <QFileDialog>
|
||||
|
||||
class skylander_dialog : public QDialog
|
||||
{
|
||||
public:
|
||||
skylander_dialog(QWidget* parent);
|
||||
~skylander_dialog();
|
||||
static skylander_dialog* get_dlg(QWidget* parent);
|
||||
|
||||
skylander_dialog(skylander_dialog const&) = delete;
|
||||
void operator=(skylander_dialog const&) = delete;
|
||||
|
||||
protected:
|
||||
// Update the edits from skylander loaded in memory
|
||||
void update_edits();
|
||||
// Parse edits and apply them to skylander in memory
|
||||
void process_edits();
|
||||
|
||||
// Creates a new skylander
|
||||
void new_skylander();
|
||||
// Loads a skylander
|
||||
void load_skylander();
|
||||
|
||||
u16 skylander_crc16(u16 init_value, const u8* buffer, u32 size);
|
||||
// Get hash used for encryption of block
|
||||
void get_hash(u8 block, std::array<u8, 16>& res_hash);
|
||||
// encrypt a block to memory
|
||||
void set_block(const u8 block, const std::array<u8, 16>& to_encrypt);
|
||||
// decrypt a block in memory
|
||||
void get_block(const u8 block, std::array<u8, 16>& decrypted);
|
||||
|
||||
// get the active Area(0x08 or 0x24)
|
||||
u8 get_active_block();
|
||||
|
||||
void set_checksums();
|
||||
|
||||
protected:
|
||||
QLineEdit* edit_curfile = nullptr;
|
||||
QComboBox* combo_skylist = nullptr;
|
||||
QLineEdit* edit_skyid = nullptr;
|
||||
QLineEdit* edit_skyxp = nullptr;
|
||||
QLineEdit* edit_skymoney = nullptr;
|
||||
QPushButton* button_update = nullptr;
|
||||
|
||||
private:
|
||||
static skylander_dialog* inst;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user