rpcsx/rpcs3/Emu/Io/Dimensions.cpp

712 lines
22 KiB
C++

#include "stdafx.h"
#include "Dimensions.h"
#include <bit>
#include "cellos/sys_usbd.h"
LOG_CHANNEL(dimensions_log, "dimensions");
dimensions_toypad g_dimensionstoypad;
static constexpr std::array<u8, 16> COMMAND_KEY = {0x55, 0xFE, 0xF6, 0xB0, 0x62, 0xBF, 0x0B, 0x41,
0xC9, 0xB3, 0x7C, 0xB4, 0x97, 0x3E, 0x29, 0x7B};
static constexpr std::array<u8, 17> CHAR_CONSTANT = {0xB7, 0xD5, 0xD7, 0xE6, 0xE7, 0xBA, 0x3C,
0xA8, 0xD8, 0x75, 0x47, 0x68, 0xCF, 0x23, 0xE9, 0xFE, 0xAA};
static constexpr std::array<u8, 25> PWD_CONSTANT = {0x28, 0x63, 0x29, 0x20, 0x43, 0x6F, 0x70, 0x79,
0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x4C, 0x45, 0x47, 0x4F, 0x20, 0x32, 0x30, 0x31, 0x34, 0xAA, 0xAA};
void dimensions_figure::save()
{
if (!dim_file)
{
dimensions_log.error("Tried to save infinity figure to file but no infinity figure is active!");
return;
}
dim_file.seek(0, fs::seek_set);
dim_file.write(data.data(), 0x2D * 0x04);
}
u8 dimensions_toypad::generate_checksum(const std::array<u8, 32>& data, u32 num_of_bytes)
{
int checksum = 0;
ensure(num_of_bytes <= data.size());
for (u8 i = 0; i < num_of_bytes; i++)
{
checksum += data[i];
}
return (checksum & 0xFF);
}
void dimensions_toypad::get_blank_response(u8 type, u8 sequence, std::array<u8, 32>& reply_buf)
{
reply_buf[0] = 0x55;
reply_buf[1] = type;
reply_buf[2] = sequence;
reply_buf[3] = generate_checksum(reply_buf, 3);
}
void dimensions_toypad::generate_random_number(const u8* buf, u8 sequence, std::array<u8, 32>& reply_buf)
{
// Decrypt payload into an 8 byte array
const std::array<u8, 8> value = decrypt(buf, std::nullopt);
// Seed is the first 4 bytes (little endian) of the decrypted payload
const u32 seed = read_from_ptr<le_t<u32>>(value);
// Confirmation is the second 4 bytes (big endian) of the decrypted payload
const u32 conf = read_from_ptr<be_t<u32>>(value, 4);
// Initialize rng using the seed from decrypted payload
initialize_rng(seed);
std::array<u8, 8> value_to_encrypt = {};
// Encrypt 8 bytes, first 4 bytes is the decrypted confirmation from payload, 2nd 4 bytes are blank
write_to_ptr<be_t<u32>>(value_to_encrypt, conf);
const std::array<u8, 8> encrypted = encrypt(value_to_encrypt.data(), std::nullopt);
reply_buf[0] = 0x55;
reply_buf[1] = 0x09;
reply_buf[2] = sequence;
// Copy encrypted value to response data
std::memcpy(&reply_buf[3], encrypted.data(), encrypted.size());
reply_buf[11] = generate_checksum(reply_buf, 11);
}
void dimensions_toypad::initialize_rng(u32 seed)
{
m_random_a = 0xF1EA5EED;
m_random_b = seed;
m_random_c = seed;
m_random_d = seed;
for (int i = 0; i < 42; i++)
{
get_next();
}
}
u32 dimensions_toypad::get_next()
{
const u32 e = m_random_a - std::rotl(m_random_b, 21);
m_random_a = m_random_b ^ std::rotl(m_random_c, 19);
m_random_b = m_random_c + std::rotl(m_random_d, 6);
m_random_c = m_random_d + e;
m_random_d = e + m_random_a;
return m_random_d;
}
std::array<u8, 8> dimensions_toypad::decrypt(const u8* buf, std::optional<std::array<u8, 16>> key)
{
// Value to decrypt is separated in to two little endian 32 bit unsigned integers
u32 data_one = read_from_ptr<le_t<u32>>(buf);
u32 data_two = read_from_ptr<le_t<u32>>(buf, 4);
// Use the key as 4 32 bit little endian unsigned integers
u32 key_one;
u32 key_two;
u32 key_three;
u32 key_four;
if (key)
{
key_one = read_from_ptr<le_t<u32>>(key.value());
key_two = read_from_ptr<le_t<u32>>(key.value(), 4);
key_three = read_from_ptr<le_t<u32>>(key.value(), 8);
key_four = read_from_ptr<le_t<u32>>(key.value(), 12);
}
else
{
key_one = read_from_ptr<le_t<u32>>(COMMAND_KEY);
key_two = read_from_ptr<le_t<u32>>(COMMAND_KEY, 4);
key_three = read_from_ptr<le_t<u32>>(COMMAND_KEY, 8);
key_four = read_from_ptr<le_t<u32>>(COMMAND_KEY, 12);
}
u32 sum = 0xC6EF3720;
constexpr u32 delta = 0x9E3779B9;
for (int i = 0; i < 32; i++)
{
data_two -= (((data_one << 4) + key_three) ^ (data_one + sum) ^ ((data_one >> 5) + key_four));
data_one -= (((data_two << 4) + key_one) ^ (data_two + sum) ^ ((data_two >> 5) + key_two));
sum -= delta;
}
ensure(sum == 0, "Decryption failed, sum inequal to 0");
std::array<u8, 8> decrypted = {u8(data_one & 0xFF), u8((data_one >> 8) & 0xFF),
u8((data_one >> 16) & 0xFF), u8((data_one >> 24) & 0xFF),
u8(data_two & 0xFF), u8((data_two >> 8) & 0xFF),
u8((data_two >> 16) & 0xFF), u8((data_two >> 24) & 0xFF)};
return decrypted;
}
std::array<u8, 8> dimensions_toypad::encrypt(const u8* buf, std::optional<std::array<u8, 16>> key)
{
// Value to encrypt is separated in to two little endian 32 bit unsigned integers
u32 data_one = read_from_ptr<le_t<u32>>(buf);
u32 data_two = read_from_ptr<le_t<u32>>(buf, 4);
// Use the key as 4 32 bit little endian unsigned integers
u32 key_one;
u32 key_two;
u32 key_three;
u32 key_four;
if (key)
{
key_one = read_from_ptr<le_t<u32>>(key.value());
key_two = read_from_ptr<le_t<u32>>(key.value(), 4);
key_three = read_from_ptr<le_t<u32>>(key.value(), 8);
key_four = read_from_ptr<le_t<u32>>(key.value(), 12);
}
else
{
key_one = read_from_ptr<le_t<u32>>(COMMAND_KEY);
key_two = read_from_ptr<le_t<u32>>(COMMAND_KEY, 4);
key_three = read_from_ptr<le_t<u32>>(COMMAND_KEY, 8);
key_four = read_from_ptr<le_t<u32>>(COMMAND_KEY, 12);
}
u32 sum = 0;
constexpr u32 delta = 0x9E3779B9;
for (int i = 0; i < 32; i++)
{
sum += delta;
data_one += (((data_two << 4) + key_one) ^ (data_two + sum) ^ ((data_two >> 5) + key_two));
data_two += (((data_one << 4) + key_three) ^ (data_one + sum) ^ ((data_one >> 5) + key_four));
}
ensure(sum == 0xC6EF3720, "Encryption failed, sum inequal to 0xC6EF3720");
std::array<u8, 8> encrypted = {u8(data_one & 0xFF), u8((data_one >> 8) & 0xFF),
u8((data_one >> 16) & 0xFF), u8((data_one >> 24) & 0xFF),
u8(data_two & 0xFF), u8((data_two >> 8) & 0xFF),
u8((data_two >> 16) & 0xFF), u8((data_two >> 24) & 0xFF)};
return encrypted;
}
std::array<u8, 16> dimensions_toypad::generate_figure_key(const std::array<u8, 0x2D * 0x04>& buf)
{
std::array<u8, 7> uid = {buf[0], buf[1], buf[2], buf[4], buf[5], buf[6], buf[7]};
std::array<u8, 16> figure_key = {};
write_to_ptr<be_t<u32>>(figure_key, scramble(uid, 3));
write_to_ptr<be_t<u32>>(figure_key, 4, scramble(uid, 4));
write_to_ptr<be_t<u32>>(figure_key, 8, scramble(uid, 5));
write_to_ptr<be_t<u32>>(figure_key, 12, scramble(uid, 6));
return figure_key;
}
u32 dimensions_toypad::scramble(const std::array<u8, 7>& uid, u8 count)
{
std::vector<u8> to_scramble;
to_scramble.reserve(uid.size() + CHAR_CONSTANT.size());
for (u8 x : uid)
{
to_scramble.push_back(x);
}
for (u8 c : CHAR_CONSTANT)
{
to_scramble.push_back(c);
}
::at32(to_scramble, count * 4 - 1) = 0xaa;
return read_from_ptr<be_t<u32>>(dimensions_randomize(to_scramble, count).data());
}
std::array<u8, 4> dimensions_toypad::dimensions_randomize(const std::vector<u8>& key, u8 count)
{
u32 scrambled = 0;
for (u8 i = 0; i < count; i++)
{
const u32 v4 = std::rotr(scrambled, 25);
const u32 v5 = std::rotr(scrambled, 10);
const u32 b = read_from_ptr<le_t<u32>>(key, i * 4);
scrambled = b + v4 + v5 - scrambled;
}
return {u8(scrambled & 0xFF), u8(scrambled >> 8 & 0xFF), u8(scrambled >> 16 & 0xFF), u8(scrambled >> 24 & 0xFF)};
}
u32 dimensions_toypad::get_figure_id(const std::array<u8, 0x2D * 0x04>& buf)
{
const std::array<u8, 16> figure_key = generate_figure_key(buf);
const std::array<u8, 8> decrypted = decrypt(&buf[36 * 4], figure_key);
const u32 fig_num = read_from_ptr<le_t<u32>>(decrypted);
// Characters have their model number encrypted in page 36
if (fig_num < 1000)
{
return fig_num;
}
// Vehicles/Gadgets have their model number written as little endian in page 36
return read_from_ptr<le_t<u32>>(buf, 36 * 4);
}
dimensions_figure& dimensions_toypad::get_figure_by_index(u8 index)
{
return ::at32(m_figures, index);
}
void dimensions_toypad::random_uid(u8* uid_buffer)
{
uid_buffer[0] = 0x04;
uid_buffer[7] = 0x80;
for (u8 i = 1; i < 7; i++)
{
u8 random = rand() % 255;
uid_buffer[i] = random;
}
}
void dimensions_toypad::get_challenge_response(const u8* buf, u8 sequence, std::array<u8, 32>& reply_buf)
{
// Decrypt payload into an 8 byte array
const std::array<u8, 8> value = decrypt(buf, std::nullopt);
// Confirmation is the first 4 bytes of the decrypted payload
const u32 conf = read_from_ptr<be_t<u32>>(value);
// Generate next random number based on RNG
const u32 next_random = get_next();
std::array<u8, 8> value_to_encrypt = {};
// Encrypt an 8 byte array, first 4 bytes are the next random number (little endian)
// followed by the confirmation from the decrypted payload
write_to_ptr<le_t<u32>>(value_to_encrypt, next_random);
write_to_ptr<be_t<u32>>(value_to_encrypt, 4, conf);
const std::array<u8, 8> encrypted = encrypt(value_to_encrypt.data(), std::nullopt);
reply_buf[0] = 0x55;
reply_buf[1] = 0x09;
reply_buf[2] = sequence;
// Copy encrypted value to response data
std::memcpy(&reply_buf[3], encrypted.data(), encrypted.size());
reply_buf[11] = generate_checksum(reply_buf, 11);
}
void dimensions_toypad::query_block(u8 index, u8 page, std::array<u8, 32>& reply_buf, u8 sequence)
{
std::lock_guard lock(m_dimensions_mutex);
reply_buf[0] = 0x55;
reply_buf[1] = 0x12;
reply_buf[2] = sequence;
reply_buf[3] = 0x00;
// Index from game begins at 1 rather than 0, so minus 1 here
if (const u8 figure_index = index - 1; figure_index < dimensions_figure_count)
{
const dimensions_figure& figure = get_figure_by_index(figure_index);
// Query 4 pages of 4 bytes from the figure, copy this to the response
if (figure.index != 255 && (4 * page) < ((0x2D * 4) - 16))
{
std::memcpy(&reply_buf[4], figure.data.data() + (4 * page), 16);
}
}
reply_buf[20] = generate_checksum(reply_buf, 20);
}
void dimensions_toypad::write_block(u8 index, u8 page, const u8* to_write_buf, std::array<u8, 32>& reply_buf, u8 sequence)
{
std::lock_guard lock(m_dimensions_mutex);
reply_buf[0] = 0x55;
reply_buf[1] = 0x02;
reply_buf[2] = sequence;
reply_buf[3] = 0x00;
// Index from game begins at 1 rather than 0, so minus 1 here
if (const u8 figure_index = index - 1; figure_index < dimensions_figure_count)
{
dimensions_figure& figure = get_figure_by_index(figure_index);
// Copy 4 bytes to the page on the figure requested by the game
if (figure.index != 255 && page < 0x2D)
{
// Id is written to page 36
if (page == 36)
{
figure.id = read_from_ptr<le_t<u32>>(to_write_buf);
}
std::memcpy(figure.data.data() + (page * 4), to_write_buf, 4);
figure.save();
}
}
reply_buf[4] = generate_checksum(reply_buf, 4);
}
void dimensions_toypad::get_model(const u8* buf, u8 sequence, std::array<u8, 32>& reply_buf)
{
// Decrypt payload to 8 byte array, byte 1 is the index, 4-7 are the confirmation
const std::array<u8, 8> value = decrypt(buf, std::nullopt);
const u8 index = value[0];
const u32 conf = read_from_ptr<be_t<u32>>(value, 4);
std::array<u8, 8> value_to_encrypt = {};
// Response is the figure's id (little endian) followed by the confirmation from payload
// Index from game begins at 1 rather than 0, so minus 1 here
if (const u8 figure_index = index - 1; figure_index < dimensions_figure_count)
{
const dimensions_figure& figure = get_figure_by_index(figure_index);
write_to_ptr<le_t<u32>>(value_to_encrypt, figure.id);
}
write_to_ptr<be_t<u32>>(value_to_encrypt, 4, conf);
const std::array<u8, 8> encrypted = encrypt(value_to_encrypt.data(), std::nullopt);
reply_buf[0] = 0x55;
reply_buf[1] = 0x0a;
reply_buf[2] = sequence;
reply_buf[3] = 0x00;
// Copy encrypted message to response
std::memcpy(&reply_buf[4], encrypted.data(), encrypted.size());
reply_buf[12] = generate_checksum(reply_buf, 12);
}
u32 dimensions_toypad::load_figure(const std::array<u8, 0x2D * 0x04>& buf, fs::file in_file, u8 pad, u8 index, bool lock)
{
if (lock)
{
m_dimensions_mutex.lock();
}
const u32 id = get_figure_id(buf);
dimensions_figure& figure = get_figure_by_index(index);
figure.dim_file = std::move(in_file);
figure.id = id;
figure.pad = pad;
figure.index = index + 1;
std::memcpy(figure.data.data(), buf.data(), buf.size());
// When a figure is added to the toypad, respond to the game with the pad they were added to, their index,
// the direction (0x00 in byte 6 for added) and their UID
std::array<u8, 32> figure_change_response = {0x56, 0x0b, figure.pad, 0x00, figure.index, 0x00,
buf[0], buf[1], buf[2], buf[4], buf[5], buf[6], buf[7]};
figure_change_response[13] = generate_checksum(figure_change_response, 13);
m_figure_added_removed_responses.push(std::move(figure_change_response));
if (lock)
{
m_dimensions_mutex.unlock();
}
return id;
}
bool dimensions_toypad::remove_figure(u8 pad, u8 index, bool full_remove, bool lock)
{
dimensions_figure& figure = get_figure_by_index(index);
if (figure.index == 255)
{
return false;
}
if (lock)
{
m_dimensions_mutex.lock();
}
// When a figure is removed from the toypad, respond to the game with the pad they were removed from, their index,
// the direction (0x01 in byte 6 for removed) and their UID
if (full_remove)
{
std::array<u8, 32> figure_change_response = {0x56, 0x0b, pad, 0x00, figure.index, 0x01,
figure.data[0], figure.data[1], figure.data[2],
figure.data[4], figure.data[5], figure.data[6], figure.data[7]};
figure_change_response[13] = generate_checksum(figure_change_response, 13);
m_figure_added_removed_responses.push(std::move(figure_change_response));
figure.save();
figure.dim_file.close();
}
figure.index = 255;
figure.pad = 255;
figure.id = 0;
if (lock)
{
m_dimensions_mutex.unlock();
}
return true;
}
bool dimensions_toypad::temp_remove(u8 index)
{
std::lock_guard lock(m_dimensions_mutex);
const dimensions_figure& figure = get_figure_by_index(index);
if (figure.index == 255)
return false;
// Send a response to the game that the figure has been "Picked up" from existing slot,
// until either the movement is cancelled, or user chooses a space to move to
std::array<u8, 32> figure_change_response = {0x56, 0x0b, figure.pad, 0x00, figure.index, 0x01,
figure.data[0], figure.data[1], figure.data[2],
figure.data[4], figure.data[5], figure.data[6], figure.data[7]};
figure_change_response[13] = generate_checksum(figure_change_response, 13);
m_figure_added_removed_responses.push(std::move(figure_change_response));
return true;
}
bool dimensions_toypad::cancel_remove(u8 index)
{
std::lock_guard lock(m_dimensions_mutex);
dimensions_figure& figure = get_figure_by_index(index);
if (figure.index == 255)
return false;
// Cancel the previous movement of the figure
std::array<u8, 32> figure_change_response = {0x56, 0x0b, figure.pad, 0x00, figure.index, 0x00,
figure.data[0], figure.data[1], figure.data[2],
figure.data[4], figure.data[5], figure.data[6], figure.data[7]};
figure_change_response[13] = generate_checksum(figure_change_response, 13);
m_figure_added_removed_responses.push(std::move(figure_change_response));
return true;
}
bool dimensions_toypad::move_figure(u8 pad, u8 index, u8 old_pad, u8 old_index)
{
if (old_index == index)
{
// Don't bother removing and loading again, just send response to the game
cancel_remove(index);
return true;
}
std::lock_guard lock(m_dimensions_mutex);
// When moving figures between spaces on the toypad, remove any figure from the space they are moving to,
// then remove them from their current space, then load them to the space they are moving to
remove_figure(pad, index, true, false);
dimensions_figure& figure = get_figure_by_index(old_index);
const std::array<u8, 0x2D * 0x04> data = figure.data;
fs::file in_file = std::move(figure.dim_file);
remove_figure(old_pad, old_index, false, false);
load_figure(data, std::move(in_file), pad, index, false);
return true;
}
bool dimensions_toypad::create_blank_character(std::array<u8, 0x2D * 0x04>& buf, u16 id)
{
random_uid(buf.data());
buf[3] = id & 0xFF;
// Only characters are created with their ID encrypted and stored in pages 36 and 37,
// as well as a password stored in page 43. Blank tags have their information populated
// by the game when it calls the write_block command.
if (id != 0)
{
// LEGO Dimensions figures use NTAG213 tag types, and the UID for these is stored in
// bytes 0, 1, 2, 4, 5, 6 and 7 (out of 180 bytes)
std::array<u8, 7> uid = {buf[0], buf[1], buf[2], buf[4], buf[5], buf[6], buf[7]};
const std::array<u8, 16> figure_key = generate_figure_key(buf);
std::array<u8, 8> value_to_encrypt = {};
write_to_ptr<le_t<u32>>(value_to_encrypt, id);
write_to_ptr<le_t<u32>>(value_to_encrypt, 4, id);
std::array<u8, 8> encrypted = encrypt(value_to_encrypt.data(), figure_key);
std::memcpy(&buf[36 * 4], &encrypted[0], 4);
std::memcpy(&buf[37 * 4], &encrypted[4], 4);
std::memcpy(&buf[43 * 4], pwd_generate(uid).data(), 4);
}
else
{
// Page 38 is used as verification for blank tags
write_to_ptr<be_t<u16>>(buf.data(), 38 * 4, 1);
}
return true;
}
std::array<u8, 4> dimensions_toypad::pwd_generate(const std::array<u8, 7>& uid)
{
std::vector<u8> pwd_calc = {PWD_CONSTANT.begin(), PWD_CONSTANT.end() - 1};
for (u8 i = 0; i < uid.size(); i++)
{
pwd_calc.insert(pwd_calc.begin() + i, uid[i]);
}
return dimensions_randomize(pwd_calc, 8);
}
std::optional<std::array<u8, 32>> dimensions_toypad::pop_added_removed_response()
{
std::lock_guard lock(m_dimensions_mutex);
if (m_figure_added_removed_responses.empty())
{
return std::nullopt;
}
std::array<u8, 32> response = m_figure_added_removed_responses.front();
m_figure_added_removed_responses.pop();
return response;
}
usb_device_dimensions::usb_device_dimensions(const std::array<u8, 7>& location)
: usb_device_emulated(location)
{
device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{0x200, 0x0, 0x0, 0x0, 0x20, 0x0E6F, 0x0241, 0x200, 0x1, 0x2, 0x3, 0x1});
auto& config0 = device.add_node(UsbDescriptorNode(USB_DESCRIPTOR_CONFIG, UsbDeviceConfiguration{0x29, 0x1, 0x1, 0x0, 0x80, 0xFA}));
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, UsbDeviceInterface{0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0}));
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_HID, UsbDeviceHID{0x0111, 0x00, 0x01, 0x22, 0x001d}));
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x81, 0x03, 0x20, 0x1}));
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x01, 0x03, 0x20, 0x1}));
}
usb_device_dimensions::~usb_device_dimensions()
{
}
std::shared_ptr<usb_device> usb_device_dimensions::make_instance(u32, const std::array<u8, 7>& location)
{
return std::make_shared<usb_device_dimensions>(location);
}
u16 usb_device_dimensions::get_num_emu_devices()
{
return 1;
}
void usb_device_dimensions::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer)
{
usb_device_emulated::control_transfer(bmRequestType, bRequest, wValue, wIndex, wLength, buf_size, buf, transfer);
}
void usb_device_dimensions::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer)
{
ensure(buf_size == 0x20);
transfer->fake = true;
transfer->expected_count = buf_size;
transfer->expected_result = HC_CC_NOERR;
switch (endpoint)
{
case 0x81:
{
// Read Endpoint, if a request has not been sent via the write endpoint, set expected result as
// EHCI_CC_HALTED so the game doesn't report the Toypad as being disconnected.
std::lock_guard lock(m_query_mutex);
std::optional<std::array<u8, 32>> response = g_dimensionstoypad.pop_added_removed_response();
if (response)
{
std::memcpy(buf, response.value().data(), 0x20);
}
else if (!m_queries.empty())
{
std::memcpy(buf, m_queries.front().data(), 0x20);
m_queries.pop();
}
else
{
transfer->expected_count = 0;
transfer->expected_result = EHCI_CC_HALTED;
}
break;
}
case 0x01:
{
// Write endpoint, similar structure of request to the Infinity Base with a command for byte 3,
// sequence for byte 4, the payload after that, then a checksum for the final byte.
const u8 command = buf[2];
const u8 sequence = buf[3];
transfer->expected_time = get_timestamp() + 100;
std::array<u8, 32> q_result{};
switch (command)
{
case 0xB0: // Wake
{
// Consistent device response to the wake command
q_result = {0x55, 0x0e, 0x01, 0x28, 0x63, 0x29,
0x20, 0x4c, 0x45, 0x47, 0x4f, 0x20,
0x32, 0x30, 0x31, 0x34, 0x46};
break;
}
case 0xB1: // Seed
{
// Initialise a random number generator using the seed provided
g_dimensionstoypad.generate_random_number(&buf[4], sequence, q_result);
break;
}
case 0xB3: // Challenge
{
// Get the next number in the sequence based on the RNG from 0xB1 command
g_dimensionstoypad.get_challenge_response(&buf[4], sequence, q_result);
break;
}
case 0xC0: // Color
case 0xC1: // Get Pad Color
case 0xC2: // Fade
case 0xC3: // Flash
case 0xC4: // Fade Random
case 0xC6: // Fade All
case 0xC7: // Flash All
case 0xC8: // Color All
{
// Send a blank response to acknowledge color has been sent to toypad
g_dimensionstoypad.get_blank_response(0x01, sequence, q_result);
break;
}
case 0xD2: // Read
{
// Read 4 pages from the figure at index (buf[4]), starting with page buf[5]
g_dimensionstoypad.query_block(buf[4], buf[5], q_result, sequence);
break;
}
case 0xD3: // Write
{
// Write 4 bytes to page buf[5] to the figure at index buf[4]
g_dimensionstoypad.write_block(buf[4], buf[5], &buf[6], q_result, sequence);
break;
}
case 0xD4: // Model
{
// Get the model id of the figure at index buf[4]
g_dimensionstoypad.get_model(&buf[4], sequence, q_result);
break;
}
case 0xD0: // Tag List
case 0xE1: // PWD
case 0xE5: // Active
case 0xFF: // LEDS Query
{
// Further investigation required
dimensions_log.error("Unimplemented LD Function: 0x%x", command);
dimensions_log.error("Request: %s", fmt::buf_to_hexstring(buf, buf_size));
break;
}
default:
{
dimensions_log.error("Unknown LD Function: 0x%x", command);
dimensions_log.error("Request: %s", fmt::buf_to_hexstring(buf, buf_size));
break;
}
}
std::lock_guard lock(m_query_mutex);
m_queries.push(q_result);
break;
}
default:
break;
}
}
void usb_device_dimensions::isochronous_transfer(UsbTransfer* transfer)
{
usb_device_emulated::isochronous_transfer(transfer);
}