mirror of
https://github.com/Lorenzooone/cc3dsfs.git
synced 2026-03-22 02:06:22 -05:00
Some checks failed
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:linux32 flags:32 name:Linux GCC 32 os:ubuntu-latest]) (push) Has been cancelled
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:linux64 flags:64 name:Linux GCC x64 os:ubuntu-latest]) (push) Has been cancelled
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:linuxarm32 flags:arm32 name:Linux GCC ARM 32 os:ubuntu-latest]) (push) Has been cancelled
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:linuxarm64 flags:arm64 name:Linux GCC ARM 64 os:ubuntu-latest]) (push) Has been cancelled
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:macos name:macOS Apple Silicon os:macos-14]) (push) Has been cancelled
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:win32 flags:-A Win32 name:Windows VS2022 Win32 os:windows-2022]) (push) Has been cancelled
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:win64 flags:-A x64 name:Windows VS2022 x64 os:windows-2022]) (push) Has been cancelled
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:winarm64 flags:-A ARM64 name:Windows VS2022 ARM os:windows-2022]) (push) Has been cancelled
CD / Create Pi Mono Setup (push) Has been cancelled
CD / Publishing (push) Has been cancelled
1321 lines
50 KiB
C++
1321 lines
50 KiB
C++
#include "frontend.hpp"
|
|
#include "usb_is_device_communications.hpp"
|
|
#include "usb_is_device_libusb.hpp"
|
|
#include "usb_is_device_is_driver.hpp"
|
|
#include "is_twl_cap_init_seed_table.h"
|
|
#include "is_twl_cap_crc32_table.h"
|
|
#include "usb_generic.hpp"
|
|
|
|
#include <libusb.h>
|
|
#include <cstring>
|
|
#include <thread>
|
|
#include <chrono>
|
|
#include <iostream>
|
|
|
|
// Code based off of Gericom's sample code. Distributed under the MIT License. Copyright (c) 2024 Gericom
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
|
|
#define IS_TWL_SERIAL_NUMBER_SIZE 8
|
|
|
|
#define IS_NITRO_USB_PACKET_LIMIT 0x2000
|
|
#define IS_TWL_USB_PACKET_LIMIT 0x200000
|
|
|
|
#define REG_USB_DMA_CONTROL_2 0x0C000028
|
|
#define REG_USB_BIU_CONTROL_2 0x0C0000A4
|
|
|
|
#define DEFAULT_MAX_LENGTH_IS_TWL_VIDEO (5 * sizeof(ISTWLCaptureVideoInternalReceived))
|
|
#define DEFAULT_MAX_LENGTH_IS_TWL_AUDIO (5 * 5 * sizeof(ISTWLCaptureAudioReceived))
|
|
|
|
enum is_nitro_packet_emulator_dir {
|
|
IS_NITRO_PACKET_EMU_DIR_WRITE = 0x10,
|
|
IS_NITRO_PACKET_EMU_DIR_READ = 0x11
|
|
};
|
|
|
|
enum is_nitro_packet_capture_dir {
|
|
IS_NITRO_PACKET_CAP_DIR_WRITE = 0x00,
|
|
IS_NITRO_PACKET_CAP_DIR_READ = 0x01
|
|
};
|
|
|
|
enum is_twl_packet_capture_dir {
|
|
IS_TWL_PACKET_CAP_DIR_WRITE = 0x00,
|
|
IS_TWL_PACKET_CAP_DIR_READ = 0x01,
|
|
IS_TWL_PACKET_CAP_DIR_WRITE_READ = 0x02
|
|
};
|
|
|
|
enum is_device_packet_type {
|
|
IS_NITRO_PACKET_TYPE_COMMAND = 0,
|
|
IS_NITRO_PACKET_TYPE_EMU_MEMORY = 1,
|
|
IS_NITRO_EMULATOR_PACKET_TYPE_AGB_SRAM = 2,
|
|
IS_NITRO_CAPTURE_PACKET_TYPE_DMA_CONTROL = 2,
|
|
IS_NITRO_PACKET_TYPE_CAPTURE = 3,
|
|
IS_NITRO_PACKET_TYPE_AGB_CART_ROM = 4,
|
|
IS_NITRO_PACKET_TYPE_AGB_BUS2 = 5,
|
|
IS_TWL_PACKET_TYPE_FRAME = 7,
|
|
};
|
|
|
|
enum is_nitro_emulator_command {
|
|
IS_NITRO_EMU_CMD_GET_SERIAL = 0x13,
|
|
IS_NITRO_EMU_CMD_READ_NEC_MEM = 0x17,
|
|
IS_NITRO_EMU_CMD_WRITE_NEC_MEM = 0x24,
|
|
IS_NITRO_EMU_CMD_SET_READ_NEC_MEM = 0x25,
|
|
IS_NITRO_EMU_CMD_FULL_HARDWARE_RESET = 0x81,
|
|
IS_NITRO_EMU_CMD_CPU_RESET = 0x8A,
|
|
IS_NITRO_EMU_CMD_AD = 0xAD,
|
|
};
|
|
|
|
enum is_nitro_capture_command {
|
|
IS_NITRO_CAP_CMD_ENABLE_CAP = 0x00,
|
|
IS_NITRO_CAP_CMD_GET_SERIAL = 0x03,
|
|
IS_NITRO_CAP_CMD_SET_FWD_RATE = 0x12,
|
|
IS_NITRO_CAP_CMD_SET_FWD_MODE = 0x13,
|
|
IS_NITRO_CAP_CMD_SET_FWD_COLOURS = 0x14,
|
|
IS_NITRO_CAP_CMD_SET_FWD_FRAMES = 0x15,
|
|
IS_NITRO_CAP_CMD_GET_DEBUG_STATE = 0x18,
|
|
IS_NITRO_CAP_CMD_SET_RESTART_FULL = 0x18,
|
|
IS_NITRO_CAP_CMD_GET_LID_STATE = 0x19,
|
|
IS_NITRO_CAP_CMD_SET_FWD_RESTART = 0x1C,
|
|
IS_NITRO_CAP_CMD_SET_RESET_CPU_ON = 0x21,
|
|
IS_NITRO_CAP_CMD_SET_RESET_CPU_OFF = 0x22,
|
|
};
|
|
|
|
enum is_twl_capture_command {
|
|
IS_TWL_CAP_CMD_GET_CAP = 0x00,
|
|
IS_TWL_CAP_CMD_GET_ENC_DEC_SEEDS = 0x04,
|
|
IS_TWL_CAP_CMD_GET_SERIAL = 0x13,
|
|
IS_TWL_CAP_CMD_UNLOCK_COMMS = 0x1C,
|
|
IS_TWL_CAP_CMD_GENERIC_INFO = 0x80,
|
|
IS_TWL_CAP_CMD_MNTR = 0x81,
|
|
IS_TWL_CAP_CMD_POWER_ON_OFF = 0x82,
|
|
IS_TWL_CAP_CMD_ASK_CAPTURE_INFORMATION = 0x83,
|
|
IS_TWL_CAP_CMD_SCTG = 0x84,
|
|
IS_TWL_CAP_CMD_TCZC = 0x85,
|
|
IS_TWL_CAP_CMD_STOP_FRAME_READS = 0x88,
|
|
IS_TWL_CAP_CMD_SET_LAST_FRAME_INFORMATION = 0x8B,
|
|
IS_TWL_CAP_CMD_ACTS = 0x8C,
|
|
IS_TWL_CAP_CMD_SET_BATTERY_PERCENTAGE = 0x8D,
|
|
IS_TWL_CAP_CMD_SET_AC_ADAPTER_ON_OFF = 0x8E,
|
|
IS_TWL_CAP_CMD_EPAC = 0x92,
|
|
IS_TWL_CAP_CMD_POST_EPAC = 0x93,
|
|
IS_TWL_CAP_CMD_GET_AC_ADAPTER_ON_OFF = 0x96,
|
|
IS_TWL_CAP_CMD_GET_BATTERY_PERCENTAGE= 0x97,
|
|
};
|
|
|
|
enum is_nitro_emulator_forward_bits {
|
|
IS_NITRO_EMULATOR_FORWARD_ENABLE_BIT = 0,
|
|
IS_NITRO_EMULATOR_FORWARD_COUNTER_RESTART_BIT = 1,
|
|
};
|
|
|
|
enum is_nitro_capture_forward_bits {
|
|
IS_NITRO_CAPTURE_FORWARD_COUNTER_RESTART_BIT = 0,
|
|
};
|
|
|
|
#pragma pack(push, 1)
|
|
struct PACKED is_nitro_nec_packet_header {
|
|
uint8_t command;
|
|
uint8_t unit_size;
|
|
uint16_t count;
|
|
uint32_t address;
|
|
};
|
|
|
|
struct PACKED is_device_packet_header {
|
|
uint16_t command;
|
|
uint8_t direction;
|
|
uint8_t type;
|
|
uint32_t address;
|
|
uint32_t length;
|
|
uint32_t padding;
|
|
};
|
|
|
|
struct PACKED is_device_rw_packet_header {
|
|
uint16_t command;
|
|
uint8_t direction;
|
|
uint8_t type;
|
|
uint32_t address;
|
|
uint32_t length_w;
|
|
uint32_t length_r;
|
|
};
|
|
|
|
struct PACKED is_twl_ask_capture_information_packet {
|
|
uint32_t unk[2];
|
|
uint32_t max_length_video;
|
|
uint32_t unk2[2];
|
|
uint32_t max_length_audio;
|
|
uint32_t unk3[0x10];
|
|
};
|
|
// These two are probably the same struct, with different fields used for
|
|
// different packets...
|
|
// I say probably, that's why I keep them separate.
|
|
struct PACKED is_twl_capture_information_packet {
|
|
uint32_t available_video_length;
|
|
uint32_t video_start_address;
|
|
uint32_t unk;
|
|
uint32_t available_audio_length;
|
|
uint32_t audio_start_address;
|
|
uint32_t unk2;
|
|
uint32_t unk3[0x10];
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
static const is_device_usb_device usb_is_nitro_emu_rare_desc = {
|
|
.name = "ISNEr", .long_name = "IS Nitro Emulator(R)",
|
|
.vid = 0x0f6e, .pid = 0x0400,
|
|
.default_config = 1, .default_interface = 0,
|
|
.bulk_timeout = 500,
|
|
.ep_in = 2 | LIBUSB_ENDPOINT_IN, .ep_out = 1 | LIBUSB_ENDPOINT_OUT,
|
|
.write_pipe = "Pipe00", .read_pipe = "Pipe01",
|
|
.product_id = 2, .manufacturer_id = 1, .device_type = IS_NITRO_EMULATOR_DEVICE,
|
|
.video_data_type = VIDEO_DATA_BGR, .max_usb_packet_size = IS_NITRO_USB_PACKET_LIMIT,
|
|
.do_pipe_clear_reset = true, .audio_enabled = false, .max_audio_samples_size = 0
|
|
};
|
|
|
|
static const is_device_usb_device usb_is_nitro_emu_common_desc = {
|
|
.name = "ISNE", .long_name = "IS Nitro Emulator",
|
|
.vid = 0x0f6e, .pid = 0x0404,
|
|
.default_config = 1, .default_interface = 0,
|
|
.bulk_timeout = 500,
|
|
.ep_in = 2 | LIBUSB_ENDPOINT_IN, .ep_out = 1 | LIBUSB_ENDPOINT_OUT,
|
|
.write_pipe = "Pipe00", .read_pipe = "Pipe01",
|
|
.product_id = 2, .manufacturer_id = 1, .device_type = IS_NITRO_EMULATOR_DEVICE,
|
|
.video_data_type = VIDEO_DATA_BGR, .max_usb_packet_size = IS_NITRO_USB_PACKET_LIMIT,
|
|
.do_pipe_clear_reset = true, .audio_enabled = false, .max_audio_samples_size = 0
|
|
};
|
|
|
|
static const is_device_usb_device usb_is_nitro_cap_desc = {
|
|
.name = "ISNC", .long_name = "IS Nitro Capture",
|
|
.vid = 0x0f6e, .pid = 0x0403,
|
|
.default_config = 1, .default_interface = 0,
|
|
.bulk_timeout = 500,
|
|
.ep_in = 2 | LIBUSB_ENDPOINT_IN, .ep_out = 1 | LIBUSB_ENDPOINT_OUT,
|
|
.write_pipe = "Pipe00", .read_pipe = "Pipe01",
|
|
.product_id = 2, .manufacturer_id = 1, .device_type = IS_NITRO_CAPTURE_DEVICE,
|
|
.video_data_type = VIDEO_DATA_BGR, .max_usb_packet_size = IS_NITRO_USB_PACKET_LIMIT,
|
|
.do_pipe_clear_reset = true, .audio_enabled = false, .max_audio_samples_size = 0
|
|
};
|
|
|
|
static const is_device_usb_device usb_is_twl_cap_desc = {
|
|
.name = "ISTCD", .long_name = "IS TWL Capture (Dev)",
|
|
.vid = 0x0f6e, .pid = 0x0501,
|
|
.default_config = 1, .default_interface = 0,
|
|
.bulk_timeout = 500,
|
|
.ep_in = 6 | LIBUSB_ENDPOINT_IN, .ep_out = 2 | LIBUSB_ENDPOINT_OUT,
|
|
.write_pipe = "Pipe01", .read_pipe = "Pipe03",
|
|
.product_id = 2, .manufacturer_id = 1, .device_type = IS_TWL_CAPTURE_DEVICE,
|
|
.video_data_type = VIDEO_DATA_RGB, .max_usb_packet_size = IS_TWL_USB_PACKET_LIMIT,
|
|
.do_pipe_clear_reset = false, .audio_enabled = true, .max_audio_samples_size = sizeof(ISTWLCaptureSoundData) * TWL_CAPTURE_MAX_SAMPLES_CHUNK_NUM
|
|
};
|
|
|
|
static const is_device_usb_device usb_is_twl_cap_desc_2 = {
|
|
.name = "ISTCR", .long_name = "IS TWL Capture (Ret)",
|
|
.vid = 0x0f6e, .pid = 0x0502,
|
|
.default_config = 1, .default_interface = 0,
|
|
.bulk_timeout = 500,
|
|
.ep_in = 6 | LIBUSB_ENDPOINT_IN, .ep_out = 2 | LIBUSB_ENDPOINT_OUT,
|
|
.write_pipe = "Pipe01", .read_pipe = "Pipe03",
|
|
.product_id = 2, .manufacturer_id = 1, .device_type = IS_TWL_CAPTURE_DEVICE,
|
|
.video_data_type = VIDEO_DATA_RGB, .max_usb_packet_size = IS_TWL_USB_PACKET_LIMIT,
|
|
.do_pipe_clear_reset = false, .audio_enabled = true, .max_audio_samples_size = sizeof(ISTWLCaptureSoundData) * TWL_CAPTURE_MAX_SAMPLES_CHUNK_NUM
|
|
};
|
|
|
|
static const is_device_usb_device* all_usb_is_device_devices_desc[] = {
|
|
&usb_is_nitro_emu_rare_desc,
|
|
&usb_is_nitro_emu_common_desc,
|
|
&usb_is_nitro_cap_desc,
|
|
&usb_is_twl_cap_desc,
|
|
&usb_is_twl_cap_desc_2,
|
|
};
|
|
|
|
int GetNumISDeviceDesc() {
|
|
return sizeof(all_usb_is_device_devices_desc) / sizeof(all_usb_is_device_devices_desc[0]);
|
|
}
|
|
|
|
const is_device_usb_device* GetISDeviceDesc(int index) {
|
|
if((index < 0) || (index >= GetNumISDeviceDesc()))
|
|
index = 0;
|
|
return all_usb_is_device_devices_desc[index];
|
|
}
|
|
|
|
static void fix_endianness_header(is_device_packet_header* header) {
|
|
header->command = to_le(header->command);
|
|
header->address = to_le(header->address);
|
|
header->length = to_le(header->length);
|
|
}
|
|
|
|
static void fix_endianness_header(is_device_rw_packet_header* header) {
|
|
header->command = to_le(header->command);
|
|
header->address = to_le(header->address);
|
|
header->length_w = to_le(header->length_w);
|
|
header->length_r = to_le(header->length_r);
|
|
}
|
|
|
|
static void fix_endianness_header(is_nitro_nec_packet_header* header) {
|
|
header->count = to_le(header->count);
|
|
header->address = to_le(header->address);
|
|
}
|
|
|
|
static uint32_t get_crc32_data_comm(uint8_t* data, size_t size) {
|
|
uint32_t value = 0xFFFFFFFF;
|
|
for(size_t i = 0; i < size; i++) {
|
|
uint8_t inner_value = (value >> 24) ^ data[i];
|
|
value <<= 8;
|
|
value ^= read_le32(is_twl_cap_crc32_table, inner_value);
|
|
}
|
|
return ~value;
|
|
}
|
|
|
|
static void apply_enc_dec_action_value(uint32_t action_value, uint32_t rotating_value, uint8_t* data, size_t size_u16) {
|
|
int increment = 0;
|
|
bool is_rot_odd = (rotating_value & 1) == 1;
|
|
uint16_t first_val = 0;
|
|
uint16_t last_val = 0;
|
|
if((action_value & 0xF000) == 0x1000) {
|
|
if(is_rot_odd)
|
|
increment = 0xFFFF6494;
|
|
increment += 0xA597;
|
|
}
|
|
else if((action_value & 0xF000) == 0x2000) {
|
|
if(is_rot_odd)
|
|
increment = 0x9B6C;
|
|
increment += 0x5A69;
|
|
}
|
|
switch((action_value & 0xFF) - 1) {
|
|
case 0:
|
|
for(size_t j = 0; j < size_u16; j++)
|
|
write_be16(data, read_le16(data, j), j);
|
|
break;
|
|
case 1:
|
|
for(size_t j = 0; j < size_u16; j++) {
|
|
write_le16(data, read_le16(data, j) ^ rotating_value, j);
|
|
rotating_value += increment;
|
|
}
|
|
break;
|
|
case 2:
|
|
for(size_t j = 0; j < size_u16; j++) {
|
|
write_le16(data, read_le16(data, j) + rotating_value, j);
|
|
rotating_value += increment;
|
|
}
|
|
break;
|
|
case 3:
|
|
for(size_t j = 0; j < size_u16; j++) {
|
|
write_le16(data, read_le16(data, j) - rotating_value, j);
|
|
rotating_value += increment;
|
|
}
|
|
break;
|
|
case 4:
|
|
for(size_t j = 0; j < (size_u16 / 2); j++) {
|
|
first_val = read_le16(data, j);
|
|
write_le16(data, read_le16(data, size_u16 - 1 - j), j);
|
|
write_le16(data, first_val, size_u16 - 1 - j);
|
|
}
|
|
break;
|
|
case 5:
|
|
first_val = read_le16(data);
|
|
for(size_t j = 0; j < (size_u16 - 1); j++)
|
|
write_le16(data, read_le16(data, j + 1), j);
|
|
write_le16(data, first_val, size_u16 - 1);
|
|
break;
|
|
case 6:
|
|
last_val = read_le16(data, size_u16 - 1);
|
|
for(size_t j = 0; j < (size_u16 - 1); j++)
|
|
write_le16(data, read_le16(data, size_u16 - 1 - j - 1), size_u16 - 1 - j);
|
|
write_le16(data, last_val);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void enc_dec_table_add_usage(is_device_twl_enc_dec_table* table) {
|
|
table->rotating_value = rotate_bits_right(table->rotating_value);
|
|
table->num_iters_before_refresh -= 1;
|
|
if(table->num_iters_before_refresh > 0)
|
|
return;
|
|
table->num_iters_before_refresh = table->num_iters_full;
|
|
uint16_t base_table_value = table->action_values[0];
|
|
for(int i = 0; i < table->num_values - 1; i++)
|
|
table->action_values[i] = table->action_values[i + 1];
|
|
table->action_values[table->num_values - 1] = base_table_value;
|
|
}
|
|
|
|
static void table_print(uint8_t* data, size_t size) {
|
|
/*
|
|
for(size_t i = 0; i < size; i++) {
|
|
printf("%02x ", data[i]);
|
|
if((i % 0x10) == 0xF)
|
|
printf("\n");
|
|
}
|
|
printf("\n");
|
|
*/
|
|
}
|
|
|
|
static void enc_dec_table_apply_to_data(is_device_twl_enc_dec_table* table, uint8_t* data, size_t size, bool is_enc) {
|
|
table_print(data, size);
|
|
for(int i = 0; i < table->num_values; i++) {
|
|
int action_value = table->action_values[i];
|
|
if(!is_enc)
|
|
action_value = table->action_values[table->num_values - 1 - i];
|
|
apply_enc_dec_action_value(action_value, table->rotating_value, data, size / 2);
|
|
table_print(data, size);
|
|
}
|
|
enc_dec_table_add_usage(table);
|
|
}
|
|
|
|
// Write to bulk endpoint. Returns libusb error code
|
|
static int bulk_out(is_device_device_handlers* handlers, const is_device_usb_device* usb_device_desc, uint8_t *buf, int length, int *transferred) {
|
|
if(handlers->usb_handle)
|
|
return is_device_libusb_bulk_out(handlers, usb_device_desc, buf, length, transferred);
|
|
return is_driver_bulk_out(handlers, usb_device_desc, buf, length, transferred);
|
|
}
|
|
|
|
// Read from bulk endpoint. Returns libusb error code
|
|
static int bulk_in(is_device_device_handlers* handlers, const is_device_usb_device* usb_device_desc, uint8_t *buf, int length, int *transferred) {
|
|
if(handlers->usb_handle)
|
|
return is_device_libusb_bulk_in(handlers, usb_device_desc, buf, length, transferred);
|
|
return is_driver_bulk_in(handlers, usb_device_desc, buf, length, transferred);
|
|
}
|
|
|
|
// Read from ctrl endpoint. Returns libusb error code
|
|
static int ctrl_in(is_device_device_handlers* handlers, const is_device_usb_device* usb_device_desc, uint8_t *buf, int length, uint8_t request, uint16_t index, int *transferred) {
|
|
if(handlers->usb_handle)
|
|
return is_device_libusb_ctrl_in(handlers, usb_device_desc, buf, length, request, index, transferred);
|
|
return is_driver_ctrl_in(handlers, buf, length, request, index, transferred);
|
|
}
|
|
|
|
// Read from bulk endpoint. Returns libusb error code
|
|
static void bulk_in_async(is_device_device_handlers* handlers, const is_device_usb_device* usb_device_desc, uint8_t *buf, int length, isd_async_callback_data* cb_data) {
|
|
if(handlers->usb_handle)
|
|
return is_device_libusb_async_in_start(handlers, usb_device_desc, buf, length, cb_data);
|
|
is_drive_async_in_start(handlers, usb_device_desc, buf, length, cb_data);
|
|
}
|
|
|
|
// Prepare ctrl_in pipe, if needed...
|
|
static bool prepare_ctrl_in(is_device_device_handlers* handlers) {
|
|
if(handlers->usb_handle)
|
|
return true;
|
|
return is_driver_prepare_ctrl_in_handle(handlers);
|
|
}
|
|
|
|
// Close ctrl_in pipe, if needed...
|
|
static void close_ctrl_in(is_device_device_handlers* handlers) {
|
|
if(handlers->usb_handle)
|
|
return;
|
|
is_driver_close_ctrl_in_handle(handlers);
|
|
}
|
|
|
|
static int return_and_delete(uint8_t* ptr, int value) {
|
|
delete []ptr;
|
|
return value;
|
|
}
|
|
|
|
static uint8_t get_packet_direction(is_device_type device_type, size_t transfer_size_r, size_t transfer_size_w) {
|
|
if((transfer_size_r != 0) && (transfer_size_w != 0)) {
|
|
switch(device_type) {
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
return IS_TWL_PACKET_CAP_DIR_WRITE_READ;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
else if(transfer_size_r != 0) {
|
|
switch(device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
return IS_NITRO_PACKET_EMU_DIR_READ;
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
return IS_NITRO_PACKET_CAP_DIR_READ;
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
return IS_TWL_PACKET_CAP_DIR_READ;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
else {
|
|
switch(device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
return IS_NITRO_PACKET_EMU_DIR_WRITE;
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
return IS_NITRO_PACKET_CAP_DIR_WRITE;
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
return IS_TWL_PACKET_CAP_DIR_WRITE;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint8_t is_packet_direction_read(is_device_type device_type, uint8_t packet_direction) {
|
|
switch(device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
return packet_direction == IS_NITRO_PACKET_EMU_DIR_READ;
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
return packet_direction == IS_NITRO_PACKET_CAP_DIR_READ;
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
return (packet_direction == IS_TWL_PACKET_CAP_DIR_WRITE_READ) || (packet_direction == IS_TWL_PACKET_CAP_DIR_READ);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static uint8_t is_packet_direction_write(is_device_type device_type, uint8_t packet_direction) {
|
|
switch(device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
return packet_direction == IS_NITRO_PACKET_EMU_DIR_WRITE;
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
return packet_direction == IS_NITRO_PACKET_CAP_DIR_WRITE;
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
return (packet_direction == IS_TWL_PACKET_CAP_DIR_WRITE_READ) || (packet_direction == IS_TWL_PACKET_CAP_DIR_WRITE);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static int SendWritePacket(is_device_device_handlers* handlers, uint16_t command, is_device_packet_type type, uint32_t address, uint8_t* buf, int length, const is_device_usb_device* device_desc, bool expect_result = true) {
|
|
is_device_packet_header header;
|
|
bool append_mode = true;
|
|
if(device_desc->device_type == IS_NITRO_CAPTURE_DEVICE)
|
|
append_mode = false;
|
|
int single_packet_covered_size = (int)(device_desc->max_usb_packet_size - sizeof(header));
|
|
if(!append_mode)
|
|
single_packet_covered_size = (int)device_desc->max_usb_packet_size;
|
|
int num_iters = (length + single_packet_covered_size - 1) / single_packet_covered_size;
|
|
if(!num_iters)
|
|
num_iters = 1;
|
|
size_t max_size_packet = length;
|
|
if(length > single_packet_covered_size)
|
|
max_size_packet = single_packet_covered_size;
|
|
uint8_t* single_usb_packet = new uint8_t[max_size_packet + sizeof(header)];
|
|
for(int i = 0; i < num_iters; i++) {
|
|
int transfer_size = length - (i * single_packet_covered_size);
|
|
if(transfer_size > single_packet_covered_size)
|
|
transfer_size = single_packet_covered_size;
|
|
|
|
uint8_t packet_direction = get_packet_direction(device_desc->device_type, 0, transfer_size);
|
|
header.command = command;
|
|
header.direction = packet_direction;
|
|
header.type = type;
|
|
header.address = address;
|
|
header.length = transfer_size;
|
|
header.padding = 0;
|
|
fix_endianness_header(&header);
|
|
int ret = 0;
|
|
int num_bytes = 0;
|
|
for(size_t j = 0; j < sizeof(is_device_packet_header); j++)
|
|
single_usb_packet[j] = ((uint8_t*)&header)[j];
|
|
if(append_mode && (buf != NULL)) {
|
|
for(int j = 0; j < transfer_size; j++)
|
|
single_usb_packet[sizeof(is_device_packet_header) + j] = buf[(i * single_packet_covered_size) + j];
|
|
ret = bulk_out(handlers, device_desc, single_usb_packet, transfer_size + sizeof(is_device_packet_header), &num_bytes);
|
|
}
|
|
else {
|
|
int header_bytes = 0;
|
|
ret = bulk_out(handlers, device_desc, single_usb_packet, sizeof(is_device_packet_header), &header_bytes);
|
|
if(ret < 0)
|
|
return return_and_delete(single_usb_packet, ret);
|
|
if(header_bytes != sizeof(is_device_packet_header))
|
|
return return_and_delete(single_usb_packet, LIBUSB_ERROR_INTERRUPTED);
|
|
if((buf != NULL) && (transfer_size > 0))
|
|
ret = bulk_out(handlers, device_desc, &buf[(i * single_packet_covered_size)], transfer_size, &num_bytes);
|
|
num_bytes += header_bytes;
|
|
}
|
|
if(ret < 0)
|
|
return return_and_delete(single_usb_packet, ret);
|
|
if(num_bytes != ((int)(transfer_size + sizeof(is_device_packet_header))))
|
|
return return_and_delete(single_usb_packet, LIBUSB_ERROR_INTERRUPTED);
|
|
}
|
|
if((device_desc->device_type == IS_NITRO_CAPTURE_DEVICE) && expect_result) {
|
|
uint8_t status[16];
|
|
int status_bytes = 0;
|
|
int ret = bulk_in(handlers, device_desc, status, sizeof(status), &status_bytes);
|
|
if(ret < 0)
|
|
return return_and_delete(single_usb_packet, ret);
|
|
if(status_bytes != sizeof(status))
|
|
return return_and_delete(single_usb_packet, LIBUSB_ERROR_INTERRUPTED);
|
|
if(status[0] != 1)
|
|
return return_and_delete(single_usb_packet, LIBUSB_ERROR_OTHER);
|
|
}
|
|
return return_and_delete(single_usb_packet, LIBUSB_SUCCESS);
|
|
}
|
|
|
|
static int SendReadPacket(is_device_device_handlers* handlers, uint16_t command, is_device_packet_type type, uint32_t address, uint8_t* buf, int length, const is_device_usb_device* device_desc, bool expect_result = true) {
|
|
is_device_packet_header header;
|
|
int single_packet_covered_size = (int)device_desc->max_usb_packet_size;
|
|
int num_iters = (length + single_packet_covered_size - 1) / single_packet_covered_size;
|
|
if(num_iters == 0)
|
|
num_iters = 1;
|
|
for(int i = 0; i < num_iters; i++) {
|
|
int transfer_size = length - (i * single_packet_covered_size);
|
|
if(transfer_size > single_packet_covered_size)
|
|
transfer_size = single_packet_covered_size;
|
|
|
|
uint8_t packet_direction = get_packet_direction(device_desc->device_type, transfer_size, 0);
|
|
header.command = command;
|
|
header.direction = packet_direction;
|
|
header.type = type;
|
|
header.address = address;
|
|
header.length = transfer_size;
|
|
header.padding = 0;
|
|
fix_endianness_header(&header);
|
|
int num_bytes = 0;
|
|
int ret = bulk_out(handlers, device_desc, (uint8_t*)&header, sizeof(is_device_packet_header), &num_bytes);
|
|
if(ret < 0)
|
|
return ret;
|
|
if(num_bytes != sizeof(is_device_packet_header))
|
|
return LIBUSB_ERROR_INTERRUPTED;
|
|
if(buf != NULL) {
|
|
ret = bulk_in(handlers, device_desc, buf + (i * single_packet_covered_size), transfer_size, &num_bytes);
|
|
if(ret < 0)
|
|
return ret;
|
|
if(num_bytes != transfer_size)
|
|
return LIBUSB_ERROR_INTERRUPTED;
|
|
}
|
|
}
|
|
if((device_desc->device_type == IS_NITRO_CAPTURE_DEVICE) && expect_result) {
|
|
uint8_t status[16];
|
|
int status_bytes = 0;
|
|
int ret = bulk_in(handlers, device_desc, status, sizeof(status), &status_bytes);
|
|
if(ret < 0)
|
|
return ret;
|
|
if(status_bytes != sizeof(status))
|
|
return LIBUSB_ERROR_INTERRUPTED;
|
|
if(status[0] != 1)
|
|
return LIBUSB_ERROR_OTHER;
|
|
}
|
|
return LIBUSB_SUCCESS;
|
|
}
|
|
|
|
static int SendReadWritePacket(is_device_device_handlers* handlers, uint16_t command, is_device_packet_type type, uint32_t address, uint8_t* buf_w, int length_w, uint8_t* buf_r, int length_r, const is_device_usb_device* device_desc) {
|
|
if((device_desc->device_type == IS_NITRO_EMULATOR_DEVICE) || (device_desc->device_type == IS_NITRO_CAPTURE_DEVICE))
|
|
return LIBUSB_SUCCESS;
|
|
is_device_rw_packet_header header;
|
|
int single_packet_covered_size_w = (int)(device_desc->max_usb_packet_size - sizeof(header));
|
|
int num_iters_w = (length_w + single_packet_covered_size_w - 1) / single_packet_covered_size_w;
|
|
if(!num_iters_w)
|
|
num_iters_w = 1;
|
|
int single_packet_covered_size_r = (int)device_desc->max_usb_packet_size;
|
|
int num_iters_r = (length_r + single_packet_covered_size_r - 1) / single_packet_covered_size_r;
|
|
if(!num_iters_r)
|
|
num_iters_r = 1;
|
|
int num_iters = num_iters_w;
|
|
if(num_iters_r > num_iters)
|
|
num_iters = num_iters_r;
|
|
size_t max_size_packet_w = length_w;
|
|
if(length_w > single_packet_covered_size_w)
|
|
max_size_packet_w = single_packet_covered_size_w;
|
|
uint8_t* single_usb_packet = new uint8_t[max_size_packet_w + sizeof(header)];
|
|
for(int i = 0; i < num_iters; i++) {
|
|
int transfer_size_w = length_w - (i * single_packet_covered_size_w);
|
|
if(transfer_size_w > single_packet_covered_size_w)
|
|
transfer_size_w = single_packet_covered_size_w;
|
|
int transfer_size_r = length_r - (i * single_packet_covered_size_r);
|
|
if(transfer_size_r > single_packet_covered_size_r)
|
|
transfer_size_r = single_packet_covered_size_r;
|
|
|
|
uint8_t packet_direction = get_packet_direction(device_desc->device_type, transfer_size_r, transfer_size_w);
|
|
header.command = command;
|
|
header.direction = packet_direction;
|
|
header.type = type;
|
|
header.address = address;
|
|
header.length_w = transfer_size_w;
|
|
header.length_r = transfer_size_r;
|
|
fix_endianness_header(&header);
|
|
int ret = 0;
|
|
int num_bytes = 0;
|
|
if(is_packet_direction_write(device_desc->device_type, packet_direction)) {
|
|
for(size_t j = 0; j < sizeof(is_device_rw_packet_header); j++)
|
|
single_usb_packet[j] = ((uint8_t*)&header)[j];
|
|
if(buf_w != NULL) {
|
|
for(int j = 0; j < transfer_size_w; j++)
|
|
single_usb_packet[sizeof(is_device_rw_packet_header) + j] = buf_w[(i * single_packet_covered_size_w) + j];
|
|
ret = bulk_out(handlers, device_desc, single_usb_packet, transfer_size_w + sizeof(is_device_rw_packet_header), &num_bytes);
|
|
}
|
|
else {
|
|
ret = bulk_out(handlers, device_desc, single_usb_packet, sizeof(is_device_rw_packet_header), &num_bytes);
|
|
transfer_size_w = 0;
|
|
}
|
|
if(ret < 0)
|
|
return return_and_delete(single_usb_packet, ret);
|
|
if(num_bytes != ((int)(transfer_size_w + sizeof(is_device_rw_packet_header))))
|
|
return return_and_delete(single_usb_packet, LIBUSB_ERROR_INTERRUPTED);
|
|
}
|
|
else {
|
|
header.length_r = 0;
|
|
header.length_w = transfer_size_r;
|
|
ret = bulk_out(handlers, device_desc, (uint8_t*)&header, sizeof(is_device_rw_packet_header), &num_bytes);
|
|
if(ret < 0)
|
|
return return_and_delete(single_usb_packet, ret);
|
|
if(num_bytes != sizeof(is_device_packet_header))
|
|
return return_and_delete(single_usb_packet, LIBUSB_ERROR_INTERRUPTED);
|
|
}
|
|
num_bytes = 0;
|
|
if(is_packet_direction_read(device_desc->device_type, packet_direction)) {
|
|
if(buf_r != NULL) {
|
|
ret = bulk_in(handlers, device_desc, buf_r + (i * single_packet_covered_size_r), transfer_size_r, &num_bytes);
|
|
if(ret < 0)
|
|
return return_and_delete(single_usb_packet, ret);
|
|
if(num_bytes != transfer_size_r)
|
|
return return_and_delete(single_usb_packet, LIBUSB_ERROR_INTERRUPTED);
|
|
}
|
|
}
|
|
}
|
|
return return_and_delete(single_usb_packet, LIBUSB_SUCCESS);
|
|
}
|
|
|
|
int SendReadCommand(is_device_device_handlers* handlers, uint16_t command, uint8_t* buf, int length, const is_device_usb_device* device_desc) {
|
|
return SendReadPacket(handlers, command, IS_NITRO_PACKET_TYPE_COMMAND, 0, buf, length, device_desc);
|
|
}
|
|
|
|
int SendWriteCommand(is_device_device_handlers* handlers, uint16_t command, uint8_t* buf, int length, const is_device_usb_device* device_desc) {
|
|
return SendWritePacket(handlers, command, IS_NITRO_PACKET_TYPE_COMMAND, 0, buf, length, device_desc);
|
|
}
|
|
|
|
int SendReadWriteCommand(is_device_device_handlers* handlers, uint16_t command, uint8_t* out_buf, int out_length, uint8_t* in_buf, int in_length, const is_device_usb_device* device_desc) {
|
|
return SendReadWritePacket(handlers, command, IS_NITRO_PACKET_TYPE_COMMAND, 0, out_buf, out_length, in_buf, in_length, device_desc);
|
|
}
|
|
|
|
int SendReadCommandU32(is_device_device_handlers* handlers, uint16_t command, uint32_t* out, const is_device_usb_device* device_desc) {
|
|
uint32_t buffer;
|
|
int ret = SendReadCommand(handlers, command, (uint8_t*)&buffer, sizeof(uint32_t), device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
*out = from_le(buffer);
|
|
return 0;
|
|
}
|
|
|
|
int SendWriteCommandU32(is_device_device_handlers* handlers, uint16_t command, uint32_t value, const is_device_usb_device* device_desc) {
|
|
uint32_t buffer = to_le(value);
|
|
return SendWriteCommand(handlers, command, (uint8_t*)&buffer, sizeof(uint32_t), device_desc);
|
|
}
|
|
|
|
int SendReadWriteCommandU32(is_device_device_handlers* handlers, uint16_t command, uint32_t* out_value, uint32_t value, const is_device_usb_device* device_desc) {
|
|
uint32_t out_buf = to_le(value);
|
|
uint32_t in_buf = 0;
|
|
int ret = SendReadWriteCommand(handlers, command, (uint8_t*)&out_buf, sizeof(uint32_t), (uint8_t*)&in_buf, sizeof(uint32_t), device_desc);
|
|
*out_value = from_le(in_buf);
|
|
return ret;
|
|
}
|
|
|
|
static int AuthCtrlIn(is_device_device_handlers* handlers, const is_device_usb_device* device_desc) {
|
|
int ret = 0;
|
|
bool b_ret = true;
|
|
int num_bytes = 0;
|
|
uint8_t buffer[2];
|
|
uint16_t auth_val = 0;
|
|
switch(device_desc->device_type) {
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
b_ret = prepare_ctrl_in(handlers);
|
|
if(!b_ret)
|
|
return LIBUSB_ERROR_INTERRUPTED;
|
|
ret = ctrl_in(handlers, device_desc, buffer, 2, 0xDF, 0, &num_bytes);
|
|
if(ret < 0) {
|
|
close_ctrl_in(handlers);
|
|
return ret;
|
|
}
|
|
if(num_bytes < 2) {
|
|
close_ctrl_in(handlers);
|
|
return LIBUSB_ERROR_INTERRUPTED;
|
|
}
|
|
auth_val = read_le16(buffer) - 0x644C;
|
|
ret = ctrl_in(handlers, device_desc, buffer, 1, 0xE3, ((auth_val >> 3) & 0x1C92) ^ auth_val, &num_bytes);
|
|
close_ctrl_in(handlers);
|
|
if(ret < 0)
|
|
return ret;
|
|
if((num_bytes < 1) || (buffer[0] != 0))
|
|
return LIBUSB_ERROR_INTERRUPTED;
|
|
return LIBUSB_SUCCESS;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int GetDeviceSerial(is_device_device_handlers* handlers, uint8_t* buf, const is_device_usb_device* device_desc) {
|
|
int ret = 0;
|
|
uint32_t value = 0;
|
|
switch(device_desc->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
return SendReadCommand(handlers, IS_NITRO_EMU_CMD_GET_SERIAL, buf, IS_DEVICE_REAL_SERIAL_NUMBER_SIZE, device_desc);
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
return SendReadCommand(handlers, IS_NITRO_CAP_CMD_GET_SERIAL, buf, IS_DEVICE_REAL_SERIAL_NUMBER_SIZE, device_desc);
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
ret = AuthCtrlIn(handlers, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
ret = SendReadCommandU32(handlers, IS_TWL_CAP_CMD_UNLOCK_COMMS, &value, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
ret = SendReadCommand(handlers, IS_TWL_CAP_CMD_GET_SERIAL, buf, IS_TWL_SERIAL_NUMBER_SIZE, device_desc);
|
|
buf[IS_TWL_SERIAL_NUMBER_SIZE] = '\0';
|
|
return ret;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int ReadNecMem(is_device_device_handlers* handlers, uint32_t address, uint8_t unit_size, uint8_t* buf, int count, const is_device_usb_device* device_desc) {
|
|
is_nitro_nec_packet_header header;
|
|
header.command = IS_NITRO_EMU_CMD_SET_READ_NEC_MEM;
|
|
header.unit_size = unit_size;
|
|
header.count = count;
|
|
header.address = address;
|
|
fix_endianness_header(&header);
|
|
int ret = SendWriteCommand(handlers, header.command, (uint8_t*)&header, sizeof(is_nitro_nec_packet_header), device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
return SendReadCommand(handlers, IS_NITRO_EMU_CMD_READ_NEC_MEM, buf, count * unit_size, device_desc);
|
|
}
|
|
|
|
int ReadNecMemU16(is_device_device_handlers* handlers, uint32_t address, uint16_t* out, const is_device_usb_device* device_desc) {
|
|
uint16_t buffer;
|
|
int ret = ReadNecMem(handlers, address, 2, (uint8_t*)&buffer, 1, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
*out = from_le(buffer);
|
|
return 0;
|
|
}
|
|
|
|
int ReadNecMemU32(is_device_device_handlers* handlers, uint32_t address, uint32_t* out, const is_device_usb_device* device_desc) {
|
|
uint32_t buffer;
|
|
int ret = ReadNecMem(handlers, address, 2, (uint8_t*)&buffer, 2, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
*out = from_le(buffer);
|
|
return 0;
|
|
}
|
|
|
|
int WriteNecMem(is_device_device_handlers* handlers, uint32_t address, uint8_t unit_size, uint8_t* buf, int count, const is_device_usb_device* device_desc) {
|
|
uint8_t* buffer = new uint8_t[(count * unit_size) + sizeof(is_nitro_nec_packet_header)];
|
|
is_nitro_nec_packet_header header;
|
|
header.command = IS_NITRO_EMU_CMD_WRITE_NEC_MEM;
|
|
header.unit_size = unit_size;
|
|
header.count = count;
|
|
header.address = address;
|
|
fix_endianness_header(&header);
|
|
for(size_t i = 0; i < sizeof(is_nitro_nec_packet_header); i++)
|
|
buffer[i] = ((uint8_t*)&header)[i];
|
|
for(int i = 0; i < (count * unit_size); i++)
|
|
buffer[i + sizeof(is_nitro_nec_packet_header)] = buf[i];
|
|
int ret = SendWriteCommand(handlers, header.command, buffer, (count * unit_size) + sizeof(is_nitro_nec_packet_header), device_desc);
|
|
delete []buffer;
|
|
return ret;
|
|
}
|
|
|
|
int WriteNecMemU16(is_device_device_handlers* handlers, uint32_t address, uint16_t value, const is_device_usb_device* device_desc) {
|
|
uint16_t buffer = to_le(value);
|
|
return WriteNecMem(handlers, address, 2, (uint8_t*)&buffer, 1, device_desc);
|
|
}
|
|
|
|
int WriteNecMemU32(is_device_device_handlers* handlers, uint32_t address, uint32_t value, const is_device_usb_device* device_desc) {
|
|
uint32_t buffer = to_le(value);
|
|
return WriteNecMem(handlers, address, 2, (uint8_t*)&buffer, 2, device_desc);
|
|
}
|
|
|
|
int DisableLca2(is_device_device_handlers* handlers, const is_device_usb_device* device_desc) {
|
|
if(device_desc->device_type != IS_NITRO_EMULATOR_DEVICE)
|
|
return LIBUSB_SUCCESS;
|
|
int ret = WriteNecMemU16(handlers, 0x00805180, 0, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
ret = WriteNecMemU16(handlers, 0x0F84000A, 1, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
//SleepBetweenTransfers(handlers, 2000);
|
|
return WriteNecMemU16(handlers, 0x0F84000A, 0, device_desc);
|
|
}
|
|
|
|
static bool is_string_present_and_same(const std::string text, uint8_t* buffer, size_t text_pos = 0) {
|
|
if(text == "")
|
|
return true;
|
|
// Strings are pain.
|
|
// For some reason, under MSVC comparing strings doesn't work,
|
|
// but comparing the .c_str() representation does...
|
|
// Too bad that same method doesn't work when using GCC... :/
|
|
// Solution? Write the string to a buffer, and compare the two...
|
|
uint8_t* str_check = new uint8_t[text.size()];
|
|
write_string(str_check, text);
|
|
bool is_different = false;
|
|
for(size_t i = 0; i < text.size(); i++) {
|
|
if(str_check[i] != buffer[i + text_pos]) {
|
|
is_different = true;
|
|
break;
|
|
}
|
|
}
|
|
delete []str_check;
|
|
return !is_different;
|
|
}
|
|
|
|
static int data_dec_and_check(is_device_twl_enc_dec_table* dec_table, uint8_t* buffer, size_t total_size, const std::string text = "", size_t text_pos = 0) {
|
|
enc_dec_table_apply_to_data(dec_table, buffer, total_size, false);
|
|
uint32_t crc32 = get_crc32_data_comm(buffer, total_size - 4);
|
|
uint32_t read_crc32 = read_le32(buffer, (total_size - 4) / 4);
|
|
if((crc32 != read_crc32) || (!is_string_present_and_same(text, buffer, text_pos)))
|
|
return LIBUSB_ERROR_INTERRUPTED;
|
|
return LIBUSB_SUCCESS;
|
|
}
|
|
|
|
static void data_crc32_and_enc(is_device_twl_enc_dec_table* enc_table, uint8_t* buffer, size_t total_size, const std::string text = "", size_t text_pos = 0) {
|
|
if(text != "")
|
|
write_string(buffer + 4 + text_pos, text);
|
|
uint32_t crc32 = get_crc32_data_comm(buffer + 4, total_size - 4);
|
|
write_le32(buffer, crc32);
|
|
enc_dec_table_apply_to_data(enc_table, buffer, total_size, true);
|
|
}
|
|
|
|
int StartUsbCaptureDma(is_device_device_handlers* handlers, const is_device_usb_device* device_desc, bool enable_sound_capture, is_device_twl_enc_dec_table* enc_table, is_device_twl_enc_dec_table* dec_table) {
|
|
int ret = 0;
|
|
uint8_t buffer[0xB4];
|
|
uint32_t enabled_value = 0;
|
|
switch(device_desc->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
ret = WriteNecMemU16(handlers, REG_USB_DMA_CONTROL_2, 2, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
return WriteNecMemU16(handlers, REG_USB_BIU_CONTROL_2, 1, device_desc);
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
return SendReadPacket(handlers, IS_NITRO_CAP_CMD_ENABLE_CAP, IS_NITRO_CAPTURE_PACKET_TYPE_DMA_CONTROL, 0, NULL, 1, device_desc, false);
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
if((enc_table == NULL) || (dec_table == NULL))
|
|
return LIBUSB_ERROR_INTERRUPTED;
|
|
ret = SendWriteCommand(handlers, IS_TWL_CAP_CMD_ACTS, NULL, 0, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
memset(buffer, 0, 0xB4);
|
|
write_le16(buffer, 0xB0, 2 + 2);
|
|
write_le16(buffer, 6, 3 + 2);
|
|
data_crc32_and_enc(enc_table, buffer, 0xB4, "MNTR", 8);
|
|
ret = SendReadWriteCommand(handlers, IS_TWL_CAP_CMD_MNTR, buffer, 0xB4, buffer, 0xB4, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
ret = data_dec_and_check(dec_table, buffer, 0xB4, "MNTR", 8);
|
|
if(ret < 0)
|
|
return ret;
|
|
ret = SendReadCommand(handlers, IS_TWL_CAP_CMD_EPAC, buffer, 8, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
ret = data_dec_and_check(dec_table, buffer, 8, "EPAC");
|
|
if(ret < 0)
|
|
return ret;
|
|
ret = SendReadCommand(handlers, IS_TWL_CAP_CMD_POST_EPAC, buffer, 8, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
ret = data_dec_and_check(dec_table, buffer, 8);
|
|
if(ret < 0)
|
|
return ret;
|
|
if(read_le32(buffer) != 0)
|
|
return LIBUSB_ERROR_INTERRUPTED;
|
|
memset(buffer, 0, 0x10);
|
|
data_crc32_and_enc(enc_table, buffer, 0x10, "TCZC");
|
|
ret = SendWriteCommand(handlers, IS_TWL_CAP_CMD_TCZC, buffer, 0x10, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
ret = SendReadCommand(handlers, IS_TWL_CAP_CMD_SCTG, buffer, 0x10, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
ret = data_dec_and_check(dec_table, buffer, 0x10, "SCTG");
|
|
if(ret < 0)
|
|
return ret;
|
|
memset(buffer, 0, 0x10);
|
|
enabled_value = 1;
|
|
if(enable_sound_capture)
|
|
enabled_value |= 0x10;
|
|
write_le32(buffer, enabled_value, 2);
|
|
// FPS. Seems to do nothing? Set to 60 / multiplier,
|
|
// but it has no effect...
|
|
write_le32(buffer, 60, 3);
|
|
data_crc32_and_enc(enc_table, buffer, 0x10, "ACTS");
|
|
ret = SendReadWriteCommand(handlers, IS_TWL_CAP_CMD_ACTS, buffer, 0x10, buffer, 8, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
ret = data_dec_and_check(dec_table, buffer, 8);
|
|
if(ret < 0)
|
|
return ret;
|
|
if(read_le32(buffer) != 0)
|
|
return LIBUSB_ERROR_INTERRUPTED;
|
|
return ret;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int StopUsbCaptureDma(is_device_device_handlers* handlers, const is_device_usb_device* device_desc) {
|
|
int ret = 0;
|
|
switch(device_desc->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
ret = WriteNecMemU16(handlers, REG_USB_DMA_CONTROL_2, 0, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
return WriteNecMemU16(handlers, REG_USB_BIU_CONTROL_2, 0, device_desc);
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
return SendReadPacket(handlers, IS_NITRO_CAP_CMD_ENABLE_CAP, IS_NITRO_CAPTURE_PACKET_TYPE_DMA_CONTROL, 0, NULL, 0, device_desc);
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
return SendWriteCommand(handlers, IS_TWL_CAP_CMD_STOP_FRAME_READS, NULL, 0, device_desc);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int SetForwardFrameCount(is_device_device_handlers* handlers, uint16_t count, const is_device_usb_device* device_desc) {
|
|
if(!count)
|
|
return LIBUSB_ERROR_INTERRUPTED;
|
|
count -= 1;
|
|
switch(device_desc->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
return WriteNecMemU32(handlers, 0x0800000C, (count >> 8) | ((count & 0xFF) << 16), device_desc);
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
return SendWriteCommandU32(handlers, IS_NITRO_CAP_CMD_SET_FWD_FRAMES, count, device_desc);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int SetForwardFramePermanent(is_device_device_handlers* handlers, const is_device_usb_device* device_desc) {
|
|
return SetForwardFrameCount(handlers, 0x4001, device_desc);
|
|
}
|
|
|
|
int GetFrameCounter(is_device_device_handlers* handlers, uint16_t* out, const is_device_usb_device* device_desc) {
|
|
int ret = 0;
|
|
uint32_t counter = 0;
|
|
switch(device_desc->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
ReadNecMemU32(handlers, 0x08000028, &counter, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
*out = (counter & 0xFF) | ((counter & 0xFF0000) >> 8);
|
|
return ret;
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
*out = 0;
|
|
return LIBUSB_SUCCESS;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int UpdateFrameForwardConfig(is_device_device_handlers* handlers, is_device_forward_config_values_colors colors, is_device_forward_config_values_screens screens, is_device_forward_config_values_rate rate, const is_device_usb_device* device_desc) {
|
|
int ret = 0;
|
|
switch(device_desc->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
return WriteNecMemU16(handlers, 0x0800000A, ((colors & 1) << 4) | ((screens & 3) << 2) | ((rate & 3) << 0), device_desc);
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
ret = SendWriteCommandU32(handlers, IS_NITRO_CAP_CMD_SET_FWD_RATE, rate & 3, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
ret = SendWriteCommandU32(handlers, IS_NITRO_CAP_CMD_SET_FWD_MODE, screens & 3, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
return SendWriteCommandU32(handlers, IS_NITRO_CAP_CMD_SET_FWD_COLOURS, colors & 1, device_desc);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int UpdateFrameForwardEnable(is_device_device_handlers* handlers, bool enable, bool restart, const is_device_usb_device* device_desc) {
|
|
uint32_t value = 0;
|
|
switch(device_desc->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
if(enable)
|
|
value |= (1 << IS_NITRO_EMULATOR_FORWARD_ENABLE_BIT);
|
|
if(restart)
|
|
value |= (1 << IS_NITRO_EMULATOR_FORWARD_COUNTER_RESTART_BIT);
|
|
return WriteNecMemU16(handlers, 0x08000008, value, device_desc);
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
if(restart)
|
|
value |= (1 << IS_NITRO_CAPTURE_FORWARD_COUNTER_RESTART_BIT);
|
|
return SendWriteCommandU32(handlers, IS_NITRO_CAP_CMD_SET_FWD_RESTART, value, device_desc);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int ReadLidState(is_device_device_handlers* handlers, bool* out, const is_device_usb_device* device_desc) {
|
|
uint32_t flags = 0;
|
|
int ret = 0;
|
|
switch(device_desc->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
ret = ReadNecMemU32(handlers, 0x08000000, &flags, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
*out = (flags & 2) ? true : false;
|
|
return ret;
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
ret = SendReadCommandU32(handlers, IS_NITRO_CAP_CMD_GET_LID_STATE, &flags, device_desc);
|
|
*out = (flags & 1) ? true : false;
|
|
return ret;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int ReadDebugButtonState(is_device_device_handlers* handlers, bool* out, const is_device_usb_device* device_desc) {
|
|
uint32_t flags = 0;
|
|
int ret = 0;
|
|
switch(device_desc->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
ret = ReadNecMemU32(handlers, 0x08000000, &flags, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
*out = (flags & 1) ? true : false;
|
|
return ret;
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
ret = SendReadCommandU32(handlers, IS_NITRO_CAP_CMD_GET_DEBUG_STATE, &flags, device_desc);
|
|
*out = (flags & 1) ? true : false;
|
|
return ret;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int ReadPowerButtonState(is_device_device_handlers* handlers, bool* out, const is_device_usb_device* device_desc) {
|
|
return ReadDebugButtonState(handlers, out, device_desc);
|
|
}
|
|
|
|
static int ResetCPUEmulatorGeneral(is_device_device_handlers* handlers, bool on, const is_device_usb_device* device_desc) {
|
|
uint8_t data[] = {IS_NITRO_EMU_CMD_CPU_RESET, 0, (uint8_t)(on ? 1 : 0), 0};
|
|
return SendWriteCommand(handlers, IS_NITRO_EMU_CMD_CPU_RESET, data, sizeof(data), device_desc);
|
|
}
|
|
|
|
int ResetCPUStart(is_device_device_handlers* handlers, const is_device_usb_device* device_desc) {
|
|
switch(device_desc->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
return ResetCPUEmulatorGeneral(handlers, true, device_desc);
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
return SendWriteCommand(handlers, IS_NITRO_CAP_CMD_SET_RESET_CPU_ON, NULL, 0, device_desc);
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
return SendWriteCommandU32(handlers, IS_TWL_CAP_CMD_POWER_ON_OFF, 0x00070000 | IS_TWL_CAP_CMD_POWER_ON_OFF, device_desc);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int ResetCPUEnd(is_device_device_handlers* handlers, const is_device_usb_device* device_desc) {
|
|
switch(device_desc->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
return ResetCPUEmulatorGeneral(handlers, false, device_desc);
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
return SendWriteCommand(handlers, IS_NITRO_CAP_CMD_SET_RESET_CPU_OFF, NULL, 0, device_desc);
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
return SendWriteCommandU32(handlers, IS_TWL_CAP_CMD_POWER_ON_OFF, 0x00000000 | IS_TWL_CAP_CMD_POWER_ON_OFF, device_desc);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int ResetFullHardware(is_device_device_handlers* handlers, const is_device_usb_device* device_desc) {
|
|
uint8_t isn_emu_rst_data[] = {IS_NITRO_EMU_CMD_FULL_HARDWARE_RESET, 0xF2};
|
|
switch(device_desc->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
return SendWriteCommand(handlers, IS_NITRO_EMU_CMD_FULL_HARDWARE_RESET, isn_emu_rst_data, sizeof(isn_emu_rst_data), device_desc);
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
return SendWriteCommand(handlers, IS_NITRO_CAP_CMD_SET_RESTART_FULL, NULL, 0, device_desc);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int SetBatteryPercentage(is_device_device_handlers* handlers, const is_device_usb_device* device_desc, int percentage) {
|
|
int ret = 0;
|
|
uint32_t out = 0;
|
|
if(percentage < 1)
|
|
percentage = 1;
|
|
if(percentage > 100)
|
|
percentage = 100;
|
|
switch(device_desc->device_type) {
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
// Careful - 1 resets the DSi continuously (but the official software uses it), so...
|
|
ret = SendReadWriteCommandU32(handlers, IS_TWL_CAP_CMD_SET_BATTERY_PERCENTAGE, &out, (percentage << 8) | IS_TWL_CAP_CMD_SET_BATTERY_PERCENTAGE, device_desc);
|
|
if(out != 1)
|
|
return -1;
|
|
return ret;
|
|
default:
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
int SetACAdapterConnected(is_device_device_handlers* handlers, const is_device_usb_device* device_desc, bool connected) {
|
|
int ret = 0;
|
|
uint32_t out = 0;
|
|
int value = connected ? 1 : 0;
|
|
switch(device_desc->device_type) {
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
ret = SendReadWriteCommandU32(handlers, IS_TWL_CAP_CMD_SET_AC_ADAPTER_ON_OFF, &out, (value << 8) | IS_TWL_CAP_CMD_SET_AC_ADAPTER_ON_OFF, device_desc);
|
|
if(out != 1)
|
|
return -1;
|
|
return ret;
|
|
default:
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
int GetBatteryPercentageValues(is_device_device_handlers* handlers, const is_device_usb_device* device_desc, int* percentage_one, int* percentage_two) {
|
|
int ret = 0;
|
|
uint32_t out = 0;
|
|
switch(device_desc->device_type) {
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
ret = SendReadCommandU32(handlers, IS_TWL_CAP_CMD_GET_BATTERY_PERCENTAGE, &out, device_desc);
|
|
*percentage_one = out & 0xFF;
|
|
*percentage_two = (out >> 8) & 0xFF;
|
|
return ret;
|
|
default:
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
int GetACAdapterConnectedValues(is_device_device_handlers* handlers, const is_device_usb_device* device_desc, bool* connected_one, bool* connected_two) {
|
|
int ret = 0;
|
|
uint32_t out = 0;
|
|
switch(device_desc->device_type) {
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
ret = SendReadCommandU32(handlers, IS_TWL_CAP_CMD_GET_AC_ADAPTER_ON_OFF, &out, device_desc);
|
|
*connected_one = (out & 0xFF) ? true : false;
|
|
*connected_two = ((out >> 8) & 0xFF) ? true : false;
|
|
return ret;
|
|
default:
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
int AskFrameLengthPos(is_device_device_handlers* handlers, uint32_t* video_address, uint32_t* video_length, bool video_enabled, uint32_t* audio_address, uint32_t* audio_length, bool audio_enabled, const is_device_usb_device* device_desc) {
|
|
if(device_desc->device_type != IS_TWL_CAPTURE_DEVICE)
|
|
return 0;
|
|
is_twl_ask_capture_information_packet out_packet;
|
|
is_twl_capture_information_packet in_packet;
|
|
memset(&out_packet, 0, sizeof(out_packet));
|
|
if(video_enabled)
|
|
out_packet.max_length_video = to_le((uint32_t)DEFAULT_MAX_LENGTH_IS_TWL_VIDEO);
|
|
if(audio_enabled)
|
|
out_packet.max_length_audio = to_le((uint32_t)DEFAULT_MAX_LENGTH_IS_TWL_AUDIO);
|
|
int ret = SendReadWriteCommand(handlers, IS_TWL_CAP_CMD_ASK_CAPTURE_INFORMATION, (uint8_t*)&out_packet, sizeof(out_packet), (uint8_t*)&in_packet, sizeof(in_packet), device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
*video_length = from_le(in_packet.available_video_length);
|
|
*video_address = from_le(in_packet.video_start_address);
|
|
*audio_length = from_le(in_packet.available_audio_length);
|
|
*audio_address = from_le(in_packet.audio_start_address);
|
|
return ret;
|
|
}
|
|
|
|
int SetLastFrameInfo(is_device_device_handlers* handlers, uint32_t video_address, uint32_t video_length, uint32_t audio_address, uint32_t audio_length, const is_device_usb_device* device_desc) {
|
|
if(device_desc->device_type != IS_TWL_CAPTURE_DEVICE)
|
|
return 0;
|
|
is_twl_capture_information_packet out_packet;
|
|
memset(&out_packet, 0, sizeof(out_packet));
|
|
out_packet.available_video_length = to_le(video_length);
|
|
out_packet.video_start_address = to_le(video_address);
|
|
out_packet.available_audio_length = to_le(audio_length);
|
|
out_packet.audio_start_address = to_le(audio_address);
|
|
return SendWriteCommand(handlers, IS_TWL_CAP_CMD_SET_LAST_FRAME_INFORMATION, (uint8_t*)&out_packet, sizeof(out_packet), device_desc);
|
|
}
|
|
|
|
int ReadFrame(is_device_device_handlers* handlers, uint8_t* buf, uint32_t address, uint32_t length, const is_device_usb_device* device_desc) {
|
|
if(device_desc->device_type != IS_TWL_CAPTURE_DEVICE)
|
|
return 0;
|
|
if(length == 0)
|
|
return 0;
|
|
return SendReadPacket(handlers, IS_TWL_CAP_CMD_GET_CAP, IS_TWL_PACKET_TYPE_FRAME, address, buf, length, device_desc);
|
|
}
|
|
|
|
int ReadFrame(is_device_device_handlers* handlers, uint8_t* buf, int length, const is_device_usb_device* device_desc) {
|
|
// Maybe making this async would be better for lower end hardware...
|
|
int num_bytes = 0;
|
|
if(length == 0)
|
|
return 0;
|
|
int ret = bulk_in(handlers, device_desc, buf, length, &num_bytes);
|
|
if(num_bytes != length)
|
|
return LIBUSB_ERROR_INTERRUPTED;
|
|
return ret;
|
|
}
|
|
|
|
void ReadFrameAsync(is_device_device_handlers* handlers, uint8_t* buf, int length, const is_device_usb_device* device_desc, isd_async_callback_data* cb_data) {
|
|
return bulk_in_async(handlers, device_desc, buf, length, cb_data);
|
|
}
|
|
|
|
void CloseAsyncRead(is_device_device_handlers* handlers, isd_async_callback_data* cb_data) {
|
|
if(handlers->usb_handle)
|
|
return is_device_libusb_cancell_callback(cb_data);
|
|
return is_device_is_driver_cancel_callback(cb_data);
|
|
}
|
|
|
|
int ResetUSBDevice(is_device_device_handlers* handlers) {
|
|
if(handlers->usb_handle)
|
|
return is_device_libusb_reset_device(handlers);
|
|
return is_device_is_driver_reset_device(handlers);
|
|
}
|
|
|
|
static void prepare_enc_dec_table_data(is_device_twl_enc_dec_table* table, uint32_t seed_1, uint32_t seed_2, uint8_t num_iters, uint8_t num_values, bool is_enc) {
|
|
uint32_t rotating_value = seed_1 ^ seed_2 ^ 0x3CF0F03C;
|
|
table->rotating_value = rotating_value;
|
|
table->num_values = num_values;
|
|
table->num_iters_full = num_iters;
|
|
table->num_iters_before_refresh = num_iters;
|
|
uint8_t buffer[8];
|
|
uint8_t values_buffer[8];
|
|
for(int i = 0; i < 7; i++)
|
|
buffer[i] = i + 1;
|
|
buffer[7] = 7;
|
|
values_buffer[0] = 0;
|
|
for(int i = 1; i < num_values; i++) {
|
|
int divisor = (7 - (i - 1));
|
|
int mod = rotating_value % divisor;
|
|
values_buffer[i] = buffer[mod];
|
|
for(int j = mod; j < 7; j++)
|
|
buffer[j] = buffer[j + 1];
|
|
rotating_value = rotate_bits_right(rotating_value);
|
|
}
|
|
int offset = 0;
|
|
if(!is_enc)
|
|
offset = 1;
|
|
for(int i = 0; i < num_values; i++)
|
|
table->action_values[i] = read_le16(is_twl_cap_init_seed_table, (values_buffer[i] * 2) + offset);
|
|
}
|
|
|
|
int PrepareEncDecTable(is_device_device_handlers* handlers, is_device_twl_enc_dec_table* enc_table, is_device_twl_enc_dec_table* dec_table, const is_device_usb_device* device_desc) {
|
|
int ret = 0;
|
|
uint8_t buffer[0xC];
|
|
uint32_t system_ms = ms_since_start();
|
|
uint32_t incoming_seed = 0;
|
|
switch(device_desc->device_type) {
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
ret = AuthCtrlIn(handlers, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
write_le32(buffer, IS_TWL_CAP_CMD_GET_ENC_DEC_SEEDS);
|
|
write_le32(buffer, system_ms, 1);
|
|
write_le32(buffer, system_ms ^ 0x84327472, 2);
|
|
ret = SendWriteCommand(handlers, IS_TWL_CAP_CMD_GET_ENC_DEC_SEEDS, buffer, sizeof(buffer), device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
ret = SendReadCommand(handlers, IS_TWL_CAP_CMD_GET_ENC_DEC_SEEDS, buffer, 4, device_desc);
|
|
if(ret < 0)
|
|
return ret;
|
|
incoming_seed = read_le32(buffer);
|
|
prepare_enc_dec_table_data(enc_table, system_ms, system_ms ^ 0x84327472 ^ 0xB0762D27, 0x10, 7, true);
|
|
prepare_enc_dec_table_data(dec_table, ~system_ms, incoming_seed ^ 0xD13F1EBB, 0x40, 3, false);
|
|
return ret;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void SetupISDeviceAsyncThread(is_device_device_handlers* handlers, void* user_data, std::thread* thread_ptr, bool* keep_going, ConsumerMutex* is_data_ready) {
|
|
if(handlers->usb_handle)
|
|
return libusb_register_to_event_thread();
|
|
return is_device_is_driver_start_thread(thread_ptr, keep_going, (ISDeviceCaptureReceivedData*)user_data, handlers, is_data_ready);
|
|
}
|
|
|
|
void EndISDeviceAsyncThread(is_device_device_handlers* handlers, void* user_data, std::thread* thread_ptr, bool* keep_going, ConsumerMutex* is_data_ready) {
|
|
if(handlers->usb_handle)
|
|
return libusb_unregister_from_event_thread();
|
|
return is_device_is_driver_close_thread(thread_ptr, keep_going, (ISDeviceCaptureReceivedData*)user_data);
|
|
}
|