2023-04-08 00:31:15 +02:00
|
|
|
#include "stdafx.h"
|
|
|
|
|
#include "Infinity.h"
|
|
|
|
|
|
|
|
|
|
#include <bit>
|
|
|
|
|
|
|
|
|
|
#include "Crypto/aes.h"
|
|
|
|
|
#include "Crypto/sha1.h"
|
|
|
|
|
|
|
|
|
|
LOG_CHANNEL(infinity_log, "infinity");
|
|
|
|
|
|
|
|
|
|
infinity_base g_infinitybase;
|
|
|
|
|
|
|
|
|
|
static constexpr std::array<u8, 32> SHA1_CONSTANT = {
|
|
|
|
|
0xAF, 0x62, 0xD2, 0xEC, 0x04, 0x91, 0x96, 0x8C, 0xC5, 0x2A, 0x1A, 0x71, 0x65, 0xF8, 0x65, 0xFE,
|
|
|
|
|
0x28, 0x63, 0x29, 0x20, 0x44, 0x69, 0x73, 0x6e, 0x65, 0x79, 0x20, 0x32, 0x30, 0x31, 0x33};
|
|
|
|
|
|
|
|
|
|
void infinity_figure::save()
|
|
|
|
|
{
|
|
|
|
|
if (!inf_file)
|
|
|
|
|
{
|
|
|
|
|
infinity_log.error("Tried to save infinity figure to file but no infinity figure is active!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
inf_file.seek(0, fs::seek_set);
|
|
|
|
|
inf_file.write(data.data(), 0x14 * 0x10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u8 infinity_base::generate_checksum(const std::array<u8, 32>& data, int num_of_bytes) const
|
|
|
|
|
{
|
|
|
|
|
int checksum = 0;
|
|
|
|
|
for (int i = 0; i < num_of_bytes; i++)
|
|
|
|
|
{
|
|
|
|
|
checksum += data[i];
|
|
|
|
|
}
|
|
|
|
|
return (checksum & 0xFF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void infinity_base::get_blank_response(u8 sequence, std::array<u8, 32>& reply_buf)
|
|
|
|
|
{
|
|
|
|
|
reply_buf[0] = 0xaa;
|
|
|
|
|
reply_buf[1] = 0x01;
|
|
|
|
|
reply_buf[2] = sequence;
|
|
|
|
|
reply_buf[3] = generate_checksum(reply_buf, 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void infinity_base::descramble_and_seed(u8* buf, u8 sequence, std::array<u8, 32>& reply_buf)
|
|
|
|
|
{
|
|
|
|
|
u64 value = u64(buf[4]) << 56 | u64(buf[5]) << 48 | u64(buf[6]) << 40 | u64(buf[7]) << 32 |
|
|
|
|
|
u64(buf[8]) << 24 | u64(buf[9]) << 16 | u64(buf[10]) << 8 | u64(buf[11]);
|
|
|
|
|
u32 seed = descramble(value);
|
|
|
|
|
generate_seed(seed);
|
|
|
|
|
get_blank_response(sequence, reply_buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void infinity_base::get_next_and_scramble(u8 sequence, std::array<u8, 32>& reply_buf)
|
|
|
|
|
{
|
|
|
|
|
const u32 next_random = get_next();
|
|
|
|
|
const u64 scrambled_next_random = scramble(next_random, 0);
|
|
|
|
|
reply_buf = {0xAA, 0x09, sequence};
|
|
|
|
|
reply_buf[3] = u8((scrambled_next_random >> 56) & 0xFF);
|
|
|
|
|
reply_buf[4] = u8((scrambled_next_random >> 48) & 0xFF);
|
|
|
|
|
reply_buf[5] = u8((scrambled_next_random >> 40) & 0xFF);
|
|
|
|
|
reply_buf[6] = u8((scrambled_next_random >> 32) & 0xFF);
|
|
|
|
|
reply_buf[7] = u8((scrambled_next_random >> 24) & 0xFF);
|
|
|
|
|
reply_buf[8] = u8((scrambled_next_random >> 16) & 0xFF);
|
|
|
|
|
reply_buf[9] = u8((scrambled_next_random >> 8) & 0xFF);
|
|
|
|
|
reply_buf[10] = u8(scrambled_next_random & 0xFF);
|
|
|
|
|
reply_buf[11] = generate_checksum(reply_buf, 11);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u32 infinity_base::descramble(u64 num_to_descramble)
|
|
|
|
|
{
|
|
|
|
|
u64 mask = 0x8E55AA1B3999E8AA;
|
|
|
|
|
u32 ret = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 64; i++)
|
|
|
|
|
{
|
|
|
|
|
if (mask & 0x8000000000000000)
|
|
|
|
|
{
|
|
|
|
|
ret = (ret << 1) | (num_to_descramble & 0x01);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
num_to_descramble >>= 1;
|
|
|
|
|
mask <<= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u64 infinity_base::scramble(u32 num_to_scramble, u32 garbage)
|
|
|
|
|
{
|
|
|
|
|
u64 mask = 0x8E55AA1B3999E8AA;
|
|
|
|
|
u64 ret = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 64; i++)
|
|
|
|
|
{
|
|
|
|
|
ret <<= 1;
|
|
|
|
|
|
|
|
|
|
if ((mask & 1) != 0)
|
|
|
|
|
{
|
|
|
|
|
ret |= (num_to_scramble & 1);
|
|
|
|
|
num_to_scramble >>= 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ret |= (garbage & 1);
|
|
|
|
|
garbage >>= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mask >>= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void infinity_base::generate_seed(u32 seed)
|
|
|
|
|
{
|
|
|
|
|
random_a = 0xF1EA5EED;
|
|
|
|
|
random_b = seed;
|
|
|
|
|
random_c = seed;
|
|
|
|
|
random_d = seed;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 23; i++)
|
|
|
|
|
{
|
|
|
|
|
get_next();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u32 infinity_base::get_next()
|
|
|
|
|
{
|
|
|
|
|
u32 a = random_a;
|
|
|
|
|
u32 b = random_b;
|
|
|
|
|
u32 c = random_c;
|
|
|
|
|
u32 ret = std::rotl(random_b, 27);
|
|
|
|
|
|
|
|
|
|
const u32 temp = (a + ((ret ^ 0xFFFFFFFF) + 1));
|
|
|
|
|
b ^= std::rotl(c, 17);
|
|
|
|
|
a = random_d;
|
|
|
|
|
c += a;
|
|
|
|
|
ret = b + temp;
|
|
|
|
|
a += temp;
|
|
|
|
|
|
|
|
|
|
random_c = a;
|
|
|
|
|
random_a = b;
|
|
|
|
|
random_b = c;
|
|
|
|
|
random_d = ret;
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void infinity_base::get_present_figures(u8 sequence, std::array<u8, 32>& reply_buf)
|
|
|
|
|
{
|
|
|
|
|
int x = 3;
|
|
|
|
|
for (u8 i = 0; i < figures.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
u8 slot = i == 0 ? 0x10 : (i < 4) ? 0x20 :
|
|
|
|
|
0x30;
|
|
|
|
|
if (figures[i].present)
|
|
|
|
|
{
|
|
|
|
|
reply_buf[x] = slot + figures[i].order_added;
|
|
|
|
|
reply_buf[x + 1] = 0x09;
|
|
|
|
|
x += 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
reply_buf[0] = 0xaa;
|
|
|
|
|
reply_buf[1] = x - 2;
|
|
|
|
|
reply_buf[2] = sequence;
|
|
|
|
|
reply_buf[x] = generate_checksum(reply_buf, x);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
infinity_figure& infinity_base::get_figure_by_order(u8 order_added)
|
|
|
|
|
{
|
|
|
|
|
for (u8 i = 0; i < figures.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
if (figures[i].order_added == order_added)
|
|
|
|
|
{
|
|
|
|
|
return figures[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return figures[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u8 infinity_base::derive_figure_position(u8 position)
|
|
|
|
|
{
|
2024-05-12 12:23:18 +02:00
|
|
|
switch (position)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
case 1:
|
|
|
|
|
case 2:
|
|
|
|
|
return 1;
|
|
|
|
|
case 3:
|
|
|
|
|
case 4:
|
|
|
|
|
case 5:
|
|
|
|
|
return 2;
|
|
|
|
|
case 6:
|
|
|
|
|
case 7:
|
|
|
|
|
case 8:
|
|
|
|
|
return 3;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2023-04-08 00:31:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void infinity_base::query_block(u8 fig_num, u8 block, std::array<u8, 32>& reply_buf, u8 sequence)
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(infinity_mutex);
|
|
|
|
|
|
|
|
|
|
infinity_figure& figure = get_figure_by_order(fig_num);
|
|
|
|
|
|
|
|
|
|
reply_buf[0] = 0xaa;
|
|
|
|
|
reply_buf[1] = 0x12;
|
|
|
|
|
reply_buf[2] = sequence;
|
|
|
|
|
reply_buf[3] = 0x00;
|
|
|
|
|
const u8 file_block = (block == 0) ? 1 : (block * 4);
|
|
|
|
|
if (figure.present && file_block < 20)
|
|
|
|
|
{
|
|
|
|
|
memcpy(&reply_buf[4], figure.data.data() + (16 * file_block), 16);
|
|
|
|
|
}
|
|
|
|
|
reply_buf[20] = generate_checksum(reply_buf, 20);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void infinity_base::write_block(u8 fig_num, u8 block, const u8* to_write_buf,
|
|
|
|
|
std::array<u8, 32>& reply_buf, u8 sequence)
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(infinity_mutex);
|
|
|
|
|
|
|
|
|
|
infinity_figure& figure = get_figure_by_order(fig_num);
|
|
|
|
|
|
|
|
|
|
reply_buf[0] = 0xaa;
|
|
|
|
|
reply_buf[1] = 0x02;
|
|
|
|
|
reply_buf[2] = sequence;
|
|
|
|
|
reply_buf[3] = 0x00;
|
|
|
|
|
const u8 file_block = (block == 0) ? 1 : (block * 4);
|
|
|
|
|
if (figure.present && file_block < 20)
|
|
|
|
|
{
|
|
|
|
|
memcpy(figure.data.data() + (file_block * 16), to_write_buf, 16);
|
|
|
|
|
figure.save();
|
|
|
|
|
}
|
|
|
|
|
reply_buf[4] = generate_checksum(reply_buf, 4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void infinity_base::get_figure_identifier(u8 fig_num, u8 sequence, std::array<u8, 32>& reply_buf)
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(infinity_mutex);
|
|
|
|
|
|
|
|
|
|
infinity_figure& figure = get_figure_by_order(fig_num);
|
|
|
|
|
|
|
|
|
|
reply_buf[0] = 0xaa;
|
|
|
|
|
reply_buf[1] = 0x09;
|
|
|
|
|
reply_buf[2] = sequence;
|
|
|
|
|
reply_buf[3] = 0x00;
|
|
|
|
|
|
|
|
|
|
if (figure.present)
|
|
|
|
|
{
|
|
|
|
|
memcpy(&reply_buf[4], figure.data.data(), 7);
|
|
|
|
|
}
|
|
|
|
|
reply_buf[11] = generate_checksum(reply_buf, 11);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool infinity_base::has_figure_been_added_removed() const
|
|
|
|
|
{
|
|
|
|
|
return !m_figure_added_removed_responses.empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::array<u8, 32> infinity_base::pop_added_removed_response()
|
|
|
|
|
{
|
|
|
|
|
std::array<u8, 32> response = m_figure_added_removed_responses.front();
|
|
|
|
|
m_figure_added_removed_responses.pop();
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool infinity_base::remove_figure(u8 position)
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(infinity_mutex);
|
|
|
|
|
infinity_figure& figure = figures[position];
|
|
|
|
|
|
2024-08-04 12:25:32 +02:00
|
|
|
if (!figure.present)
|
2023-04-08 00:31:15 +02:00
|
|
|
{
|
2024-08-04 12:25:32 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2024-05-12 12:23:18 +02:00
|
|
|
|
2024-08-04 12:25:32 +02:00
|
|
|
position = derive_figure_position(position);
|
|
|
|
|
if (position == 0)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-04-08 00:31:15 +02:00
|
|
|
|
2024-08-04 12:25:32 +02:00
|
|
|
figure.present = false;
|
2023-04-08 00:31:15 +02:00
|
|
|
|
2024-08-04 12:25:32 +02:00
|
|
|
std::array<u8, 32> figure_change_response = {0xab, 0x04, position, 0x09, figure.order_added, 0x01};
|
|
|
|
|
figure_change_response[6] = generate_checksum(figure_change_response, 6);
|
|
|
|
|
m_figure_added_removed_responses.push(figure_change_response);
|
|
|
|
|
|
|
|
|
|
figure.save();
|
|
|
|
|
figure.inf_file.close();
|
|
|
|
|
return true;
|
2023-04-08 00:31:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u32 infinity_base::load_figure(const std::array<u8, 0x14 * 0x10>& buf, fs::file in_file, u8 position)
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(infinity_mutex);
|
|
|
|
|
u8 order_added;
|
|
|
|
|
|
|
|
|
|
std::vector<u8> sha1_calc = {SHA1_CONSTANT.begin(), SHA1_CONSTANT.end() - 1};
|
|
|
|
|
for (int i = 0; i < 7; i++)
|
|
|
|
|
{
|
|
|
|
|
sha1_calc.push_back(buf[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sha1_context ctx;
|
|
|
|
|
u8 output[20];
|
|
|
|
|
|
|
|
|
|
sha1_starts(&ctx);
|
|
|
|
|
sha1_update(&ctx, sha1_calc.data(), sha1_calc.size());
|
|
|
|
|
sha1_finish(&ctx, output);
|
|
|
|
|
|
|
|
|
|
std::array<u8, 16> key{};
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
{
|
|
|
|
|
for (int x = 0; x < 4; x++)
|
|
|
|
|
{
|
|
|
|
|
key[x + (i * 4)] = output[(3 - x) + (i * 4)];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
aes_context aes;
|
|
|
|
|
aes_setkey_dec(&aes, key.data(), 128);
|
|
|
|
|
std::array<u8, 16> infinity_decrypted_block{};
|
|
|
|
|
aes_crypt_ecb(&aes, AES_DECRYPT, &buf[16], infinity_decrypted_block.data());
|
|
|
|
|
|
|
|
|
|
u32 number = u32(infinity_decrypted_block[1]) << 16 | u32(infinity_decrypted_block[2]) << 8 |
|
|
|
|
|
u32(infinity_decrypted_block[3]);
|
|
|
|
|
|
|
|
|
|
infinity_figure& figure = figures[position];
|
|
|
|
|
|
|
|
|
|
figure.inf_file = std::move(in_file);
|
|
|
|
|
memcpy(figure.data.data(), buf.data(), figure.data.size());
|
|
|
|
|
figure.present = true;
|
|
|
|
|
if (figure.order_added == 255)
|
|
|
|
|
{
|
|
|
|
|
figure.order_added = m_figure_order;
|
|
|
|
|
m_figure_order++;
|
|
|
|
|
}
|
|
|
|
|
order_added = figure.order_added;
|
|
|
|
|
|
|
|
|
|
position = derive_figure_position(position);
|
2024-05-12 12:23:18 +02:00
|
|
|
if (position == 0)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2023-04-08 00:31:15 +02:00
|
|
|
|
|
|
|
|
std::array<u8, 32> figure_change_response = {0xab, 0x04, position, 0x09, order_added, 0x00};
|
|
|
|
|
figure_change_response[6] = generate_checksum(figure_change_response, 6);
|
|
|
|
|
m_figure_added_removed_responses.push(figure_change_response);
|
|
|
|
|
|
|
|
|
|
return number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
usb_device_infinity::usb_device_infinity(const std::array<u8, 7>& location)
|
|
|
|
|
: usb_device_emulated(location)
|
|
|
|
|
{
|
|
|
|
|
device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{0x200, 0x0, 0x0, 0x0, 0x20, 0x0E6F, 0x0129, 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, 0x3, 0x20, 0x1}));
|
|
|
|
|
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x1, 0x3, 0x20, 0x1}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
usb_device_infinity::~usb_device_infinity()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-23 23:49:24 +01:00
|
|
|
std::shared_ptr<usb_device> usb_device_infinity::make_instance(u32, const std::array<u8, 7>& location)
|
|
|
|
|
{
|
|
|
|
|
return std::make_shared<usb_device_infinity>(location);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u16 usb_device_infinity::get_num_emu_devices()
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-08 00:31:15 +02:00
|
|
|
void usb_device_infinity::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_infinity::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;
|
|
|
|
|
|
|
|
|
|
if (endpoint == 0x81)
|
|
|
|
|
{
|
|
|
|
|
// Respond after FF command
|
|
|
|
|
transfer->expected_time = get_timestamp() + 1000;
|
|
|
|
|
if (g_infinitybase.has_figure_been_added_removed())
|
|
|
|
|
{
|
|
|
|
|
memcpy(buf, g_infinitybase.pop_added_removed_response().data(), 0x20);
|
|
|
|
|
}
|
|
|
|
|
else if (!m_queries.empty())
|
|
|
|
|
{
|
|
|
|
|
memcpy(buf, m_queries.front().data(), 0x20);
|
|
|
|
|
m_queries.pop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (endpoint == 0x01)
|
|
|
|
|
{
|
|
|
|
|
const u8 command = buf[2];
|
|
|
|
|
const u8 sequence = buf[3];
|
|
|
|
|
|
|
|
|
|
transfer->expected_time = get_timestamp() + 500;
|
|
|
|
|
std::array<u8, 32> q_result{};
|
|
|
|
|
|
|
|
|
|
switch (command)
|
|
|
|
|
{
|
|
|
|
|
case 0x80:
|
|
|
|
|
{
|
|
|
|
|
q_result = {0xaa, 0x15, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x03, 0x02, 0x09, 0x09, 0x43,
|
|
|
|
|
0x20, 0x32, 0x62, 0x36, 0x36, 0x4b, 0x34, 0x99, 0x67, 0x31, 0x93, 0x8c};
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 0x81:
|
|
|
|
|
{
|
|
|
|
|
// Initiate Challenge
|
|
|
|
|
g_infinitybase.descramble_and_seed(buf, sequence, q_result);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 0x83:
|
|
|
|
|
{
|
|
|
|
|
// Challenge Response
|
|
|
|
|
g_infinitybase.get_next_and_scramble(sequence, q_result);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 0x90:
|
|
|
|
|
case 0x92:
|
|
|
|
|
case 0x93:
|
|
|
|
|
case 0x95:
|
|
|
|
|
case 0x96:
|
|
|
|
|
{
|
|
|
|
|
// Color commands
|
|
|
|
|
g_infinitybase.get_blank_response(sequence, q_result);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 0xA1:
|
|
|
|
|
{
|
|
|
|
|
// Get Present Figures
|
|
|
|
|
g_infinitybase.get_present_figures(sequence, q_result);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 0xA2:
|
|
|
|
|
{
|
|
|
|
|
// Read Block from Figure
|
|
|
|
|
g_infinitybase.query_block(buf[4], buf[5], q_result, sequence);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 0xA3:
|
|
|
|
|
{
|
|
|
|
|
// Write block to figure
|
|
|
|
|
g_infinitybase.write_block(buf[4], buf[5], &buf[7], q_result, sequence);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 0xB4:
|
|
|
|
|
{
|
|
|
|
|
// Get figure ID
|
|
|
|
|
g_infinitybase.get_figure_identifier(buf[4], sequence, q_result);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 0xB5:
|
|
|
|
|
{
|
|
|
|
|
// Get status?
|
|
|
|
|
g_infinitybase.get_blank_response(sequence, q_result);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
infinity_log.error("Unhandled Query Type: 0x%02X", command);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_queries.push(q_result);
|
|
|
|
|
}
|
|
|
|
|
}
|