Initial re-upload of spice2x-24-08-24
This commit is contained in:
67
acio2emu/firmware/bi2x.cpp
Normal file
67
acio2emu/firmware/bi2x.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "bi2x.h"
|
||||
|
||||
namespace acio2emu::firmware {
|
||||
bool BI2XNode::handle_packet(const acio2emu::Packet &in, std::vector<uint8_t> &out) {
|
||||
auto cur = in.payload.begin();
|
||||
while ((cur + 1) < in.payload.end()) {
|
||||
auto cmd = (cur[0] << 8) | cur[1];
|
||||
out.push_back(*cur++);
|
||||
out.push_back(*cur++);
|
||||
out.push_back(0);
|
||||
|
||||
switch (cmd) {
|
||||
case 2: // query firmware version
|
||||
read_firmware_version(out);
|
||||
cur = in.payload.end();
|
||||
break;
|
||||
|
||||
case 16:
|
||||
out.push_back(2);
|
||||
cur = in.payload.end();
|
||||
break;
|
||||
|
||||
case 800:
|
||||
case 802:
|
||||
case 19:
|
||||
cur = in.payload.end();
|
||||
break;
|
||||
|
||||
case 120:
|
||||
out.push_back(3);
|
||||
cur = in.payload.end();
|
||||
break;
|
||||
|
||||
case 801:
|
||||
out.push_back(33);
|
||||
out.push_back(0);
|
||||
cur = in.payload.end();
|
||||
break;
|
||||
|
||||
case 784: // poll input
|
||||
if (!read_input(out)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 785: { // write output
|
||||
auto count = write_output(std::span{&*cur, static_cast<size_t>(in.payload.end() - cur)});
|
||||
if (count < 0) {
|
||||
return false;
|
||||
}
|
||||
cur += count;
|
||||
break;
|
||||
}
|
||||
|
||||
case 786:
|
||||
cur += 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning("bi2x", "unknown command: {}", cmd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
22
acio2emu/firmware/bi2x.h
Normal file
22
acio2emu/firmware/bi2x.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <span>
|
||||
#include <cstdint>
|
||||
|
||||
#include "acio2emu/node.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace acio2emu::firmware {
|
||||
class BI2XNode : public Node {
|
||||
virtual void read_firmware_version(std::vector<uint8_t> &buffer) = 0;
|
||||
|
||||
virtual bool read_input(std::vector<uint8_t> &buffer) = 0;
|
||||
virtual int write_output(std::span<const uint8_t> buffer) = 0;
|
||||
|
||||
/*
|
||||
* acio2emu::Node
|
||||
*/
|
||||
bool handle_packet(const acio2emu::Packet &in, std::vector<uint8_t> &out) override;
|
||||
};
|
||||
}
|
||||
120
acio2emu/handle.cpp
Normal file
120
acio2emu/handle.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "handle.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h" // ws2s
|
||||
|
||||
namespace acio2emu {
|
||||
class MasterNode : public Node {
|
||||
private:
|
||||
const IOBHandle *iob_;
|
||||
|
||||
public:
|
||||
MasterNode(const IOBHandle *iob) : iob_(iob) { }
|
||||
|
||||
bool handle_packet(const Packet &in, std::vector<uint8_t> &out) {
|
||||
// were we sent a command?
|
||||
if (in.payload.size() >= 2) {
|
||||
if (in.payload[0] != 0 || in.payload[1] != 1) {
|
||||
// unknown command
|
||||
return false;
|
||||
}
|
||||
|
||||
// assign node ids
|
||||
out.push_back(0);
|
||||
out.push_back(1);
|
||||
for (int i = 0; i < iob_->number_of_nodes(); i++) {
|
||||
out.push_back(i * 16);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
IOBHandle::IOBHandle(std::wstring device) : device_(device) {
|
||||
nodes_[0] = std::make_unique<MasterNode>(this);
|
||||
}
|
||||
|
||||
bool IOBHandle::register_node(std::unique_ptr<Node> node) {
|
||||
if ((number_of_nodes_ - 1) >= 16) {
|
||||
// too many nodes
|
||||
return false;
|
||||
}
|
||||
|
||||
nodes_[number_of_nodes_++] = std::move(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
int IOBHandle::number_of_nodes() const {
|
||||
// don't include the master node
|
||||
return number_of_nodes_ - 1;
|
||||
}
|
||||
|
||||
void IOBHandle::forward_packet_(const Packet &packet) {
|
||||
// clear the output queue
|
||||
output_ = {};
|
||||
|
||||
auto node = packet.node / 2;
|
||||
if (node >= number_of_nodes_) {
|
||||
log_warning("acio2emu", "cannot forward packet: node out of range: {} >= {}", node, number_of_nodes_);
|
||||
return;
|
||||
}
|
||||
|
||||
// forward the packet to the node
|
||||
std::vector<uint8_t> payload;
|
||||
if (!nodes_[node]->handle_packet(packet, payload)) {
|
||||
// error in handler
|
||||
return;
|
||||
}
|
||||
|
||||
// encode the response
|
||||
encode_packet(output_, node, packet.tag, payload);
|
||||
}
|
||||
|
||||
/*
|
||||
* CustomHandle
|
||||
*/
|
||||
|
||||
bool IOBHandle::open(LPCWSTR lpFileName) {
|
||||
if (device_ != lpFileName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("acio2emu", "Opened {} (ACIO2)", ws2s(device_));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IOBHandle::close() {
|
||||
log_info("acio2emu", "Closed {} (ACIO2)", ws2s(device_));
|
||||
return true;
|
||||
}
|
||||
|
||||
int IOBHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) {
|
||||
auto buffer = reinterpret_cast<uint8_t *>(lpBuffer);
|
||||
DWORD i = 0;
|
||||
|
||||
while (!output_.empty() && i < nNumberOfBytesToRead) {
|
||||
buffer[i++] = output_.front();
|
||||
output_.pop();
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int IOBHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) {
|
||||
auto buffer = reinterpret_cast<const uint8_t *>(lpBuffer);
|
||||
|
||||
for (DWORD i = 0; i < nNumberOfBytesToWrite; i++) {
|
||||
if (decoder_.update(buffer[i])) {
|
||||
// forward the packet to a node
|
||||
forward_packet_(decoder_.packet());
|
||||
}
|
||||
}
|
||||
|
||||
return nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
int IOBHandle::device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
44
acio2emu/handle.h
Normal file
44
acio2emu/handle.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <queue>
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <cstdint>
|
||||
|
||||
#include "acio2emu/packet.h"
|
||||
#include "acio2emu/node.h"
|
||||
|
||||
#include "hooks/devicehook.h"
|
||||
|
||||
namespace acio2emu {
|
||||
class IOBHandle : public CustomHandle {
|
||||
private:
|
||||
std::wstring device_;
|
||||
|
||||
std::array<std::unique_ptr<Node>, 17> nodes_;
|
||||
// the first node is reserved for the "master" node
|
||||
int number_of_nodes_ = 1;
|
||||
|
||||
PacketDecoder decoder_;
|
||||
std::queue<uint8_t> output_;
|
||||
|
||||
void forward_packet_(const Packet &packet);
|
||||
|
||||
public:
|
||||
IOBHandle(std::wstring device);
|
||||
|
||||
bool register_node(std::unique_ptr<Node> node);
|
||||
int number_of_nodes() const;
|
||||
|
||||
/*
|
||||
* CustomHandle
|
||||
*/
|
||||
|
||||
bool open(LPCWSTR lpFileName) override;
|
||||
int read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) override;
|
||||
int write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) override;
|
||||
int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize) override;
|
||||
bool close() override;
|
||||
};
|
||||
}
|
||||
40
acio2emu/internal/crc.h
Normal file
40
acio2emu/internal/crc.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace acio2emu::detail {
|
||||
inline uint8_t crc4_lgp_c(uint8_t crc, const uint8_t *data, size_t len) {
|
||||
static constexpr uint8_t tbl[] = {
|
||||
0x00, 0x0D, 0x03, 0x0E,
|
||||
0x06, 0x0B, 0x05, 0x08,
|
||||
0x0C, 0x01, 0x0F, 0x02,
|
||||
0x0A, 0x07, 0x09, 0x04,
|
||||
};
|
||||
|
||||
crc &= 15;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
auto b = data[i];
|
||||
crc = (((crc >> 4) ^ (tbl[(b ^ crc) & 0x0F])) >> 4) ^ tbl[(((crc >> 4) ^ (tbl[(b ^ crc) & 0x0F])) ^ (b >> 4)) & 0x0F];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
inline uint8_t crc7_lgp_48(uint8_t crc, const uint8_t *data, size_t len) {
|
||||
static constexpr uint8_t tbl[] = {
|
||||
0x00, 0x09, 0x12, 0x1B,
|
||||
0x24, 0x2D, 0x36, 0x3F,
|
||||
0x48, 0x41, 0x5A, 0x53,
|
||||
0x6C, 0x65, 0x7E, 0x77
|
||||
};
|
||||
|
||||
crc &= 127;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
auto b = data[i];
|
||||
crc = (((crc >> 4) ^ (tbl[(b ^ crc) & 0x0F])) >> 4) ^ tbl[(((crc >> 4) ^ (tbl[(b ^ crc) & 0x0F])) ^ (b >> 4)) & 0x0F];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
}
|
||||
140
acio2emu/internal/lz.h
Normal file
140
acio2emu/internal/lz.h
Normal file
@@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <cstdint>
|
||||
|
||||
namespace acio2emu::detail {
|
||||
class InflateTransformer {
|
||||
private:
|
||||
std::queue<uint8_t> output_;
|
||||
|
||||
uint8_t flags_ = 0, flag_shift_ = 0;
|
||||
|
||||
uint8_t window_[85] = {};
|
||||
int window_offset_ = 81;
|
||||
|
||||
enum class inflateStep {
|
||||
readFlags,
|
||||
processFlags,
|
||||
copyStored,
|
||||
copyFromWindow,
|
||||
} step_ = inflateStep::readFlags;
|
||||
|
||||
void window_put_(uint8_t b) {
|
||||
window_[window_offset_++] = b;
|
||||
window_offset_ %= sizeof(window_);
|
||||
}
|
||||
|
||||
uint8_t window_get_(int offset) {
|
||||
return window_[offset % sizeof(window_)];
|
||||
}
|
||||
|
||||
public:
|
||||
void put(uint8_t b) {
|
||||
auto consumed = false;
|
||||
|
||||
while (true) {
|
||||
switch (step_) {
|
||||
case inflateStep::readFlags:
|
||||
if (consumed) {
|
||||
// need more data
|
||||
return;
|
||||
}
|
||||
consumed = true;
|
||||
|
||||
flags_ = b;
|
||||
flag_shift_ = 0;
|
||||
|
||||
step_ = inflateStep::processFlags;
|
||||
break;
|
||||
|
||||
case inflateStep::processFlags:
|
||||
// have we processed every flag?
|
||||
if (flag_shift_ > 6) {
|
||||
step_ = inflateStep::readFlags;
|
||||
break;
|
||||
}
|
||||
|
||||
if (flags_ & (1 << flag_shift_)) {
|
||||
flag_shift_++;
|
||||
|
||||
if (flags_ & (1 << flag_shift_)) {
|
||||
// emit 0xAA when both bits are set
|
||||
output_.push(0xAA);
|
||||
}
|
||||
else {
|
||||
// copy from the window when only the lower bit is set
|
||||
step_ = inflateStep::copyFromWindow;
|
||||
}
|
||||
}
|
||||
else {
|
||||
step_ = inflateStep::copyStored;
|
||||
}
|
||||
flag_shift_++;
|
||||
|
||||
break;
|
||||
|
||||
case inflateStep::copyFromWindow: {
|
||||
if (consumed) {
|
||||
// need more data
|
||||
return;
|
||||
}
|
||||
consumed = true;
|
||||
|
||||
// determine the match size, default is 2-bytes
|
||||
auto offset = b;
|
||||
auto size = 2;
|
||||
|
||||
if (offset >= 0xAA) {
|
||||
// 4-byte match
|
||||
size = 4;
|
||||
offset -= 0xAB;
|
||||
}
|
||||
else if (offset >= 0x55) {
|
||||
// 3-byte match
|
||||
size = 3;
|
||||
offset -= 0x55;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < size; i ++) {
|
||||
auto cur = window_get_(offset + i);
|
||||
|
||||
window_put_(cur);
|
||||
output_.push(cur);
|
||||
}
|
||||
|
||||
// continue processing flags
|
||||
step_ = inflateStep::processFlags;
|
||||
break;
|
||||
}
|
||||
|
||||
case inflateStep::copyStored:
|
||||
if (consumed) {
|
||||
// need more data
|
||||
return;
|
||||
}
|
||||
consumed = true;
|
||||
|
||||
window_put_(b);
|
||||
output_.push(b);
|
||||
|
||||
// continue processing flags
|
||||
step_ = inflateStep::processFlags;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int get() {
|
||||
if (output_.empty()) {
|
||||
// output queue is empty
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto b = output_.front();
|
||||
output_.pop();
|
||||
|
||||
return b;
|
||||
}
|
||||
};
|
||||
}
|
||||
14
acio2emu/node.h
Normal file
14
acio2emu/node.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "acio2emu/packet.h"
|
||||
|
||||
namespace acio2emu {
|
||||
class Node {
|
||||
public:
|
||||
virtual ~Node() {}
|
||||
|
||||
virtual bool handle_packet(const Packet &in, std::vector<uint8_t> &out) = 0;
|
||||
};
|
||||
}
|
||||
259
acio2emu/packet.cpp
Normal file
259
acio2emu/packet.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
#include "packet.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
|
||||
#include "acio2emu/internal/crc.h"
|
||||
|
||||
namespace acio2emu {
|
||||
static constexpr uint8_t SOF = 0xAA;
|
||||
static constexpr uint8_t ESC = 0xFF;
|
||||
|
||||
static void encode_payload_(std::queue<uint8_t> &out, const std::vector<uint8_t> &payload) {
|
||||
for (auto b : payload) {
|
||||
if (b == SOF || b == ESC) {
|
||||
out.push(ESC);
|
||||
b = ~b;
|
||||
}
|
||||
out.push(b);
|
||||
}
|
||||
// compute and write the payload's CRC
|
||||
out.push(detail::crc7_lgp_48(0x7F, payload.data(), payload.size()) ^ 0x7F);
|
||||
}
|
||||
|
||||
bool encode_packet(std::queue<uint8_t> &out, uint8_t node, uint8_t tag, const std::vector<uint8_t> &payload) {
|
||||
auto size = payload.size();
|
||||
if (size > 127) {
|
||||
log_warning("acio2emu", "cannot encode packet: payload too large: {} > 127", payload.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
// build the header
|
||||
uint8_t header[5] = {
|
||||
SOF,
|
||||
static_cast<uint8_t>(node * 3),
|
||||
tag,
|
||||
static_cast<uint8_t>(size),
|
||||
0,
|
||||
};
|
||||
// compute the header's CRC
|
||||
header[4] = detail::crc4_lgp_c(0x0F, &header[1], sizeof(header) - 1) ^ 0x0F;
|
||||
// push the header to the output queue
|
||||
for (size_t i = 0; i < sizeof(header); i++) {
|
||||
out.push(header[i]);
|
||||
}
|
||||
|
||||
encode_payload_(out, payload);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PacketDecoder::set_step_(readStep s) {
|
||||
#ifndef NDEBUG
|
||||
auto valid = true;
|
||||
switch (s) {
|
||||
case readStep::idle:
|
||||
case readStep::readNode:
|
||||
// transition from any step/state allowed
|
||||
break;
|
||||
|
||||
case readStep::readTag:
|
||||
if (step_ != readStep::readNode) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readPayloadSize:
|
||||
if (step_ != readStep::readTag) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readPayloadFlags:
|
||||
if (step_ != readStep::readPayloadSize) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readReplacementByte:
|
||||
if (step_ != readStep::readPayloadFlags) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readPayload:
|
||||
if (step_ != readStep::readPayloadFlags &&
|
||||
step_ != readStep::readReplacementByte &&
|
||||
step_ != readStep::readEscaped
|
||||
) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readEscaped:
|
||||
if (step_ != readStep::readPayload) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
log_fatal("acio2emu", "cannot set step: unknown value: {}", s);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
log_fatal("acio2emu", "illegal transition detected: {} -> {}", step_, s);
|
||||
}
|
||||
#endif
|
||||
step_ = s;
|
||||
}
|
||||
|
||||
int PacketDecoder::update_payload_size_(uint8_t b) {
|
||||
if ((b & 0x80) == 0) {
|
||||
payload_size_ = (payload_size_ << 7) | (b & 0x7F);
|
||||
// finished
|
||||
return 0;
|
||||
}
|
||||
else if ((b & 0x40) != 0 && payload_size_count_ < 5) {
|
||||
payload_size_count_++;
|
||||
payload_size_count_ = (payload_size_count_ << 6) | (b & 0x3F);
|
||||
// continuation required
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
// invalid value or invalid state
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t PacketDecoder::deobfuscate_(uint8_t b) {
|
||||
if ((b ^ 0xAA) == 0) {
|
||||
return b;
|
||||
}
|
||||
|
||||
auto mask = 0x55;
|
||||
if ((b & 0x80) == 0) {
|
||||
mask = 0x7F;
|
||||
}
|
||||
|
||||
return (b ^ lcg_()) & mask;
|
||||
}
|
||||
|
||||
void PacketDecoder::reset_(readStep s) {
|
||||
set_step_(s);
|
||||
packet_ = {};
|
||||
payload_size_ = 0;
|
||||
payload_size_count_ = 0;
|
||||
}
|
||||
|
||||
bool PacketDecoder::update(uint8_t b) {
|
||||
// is this the start of a packet?
|
||||
if (b == SOF) {
|
||||
reset_(readStep::readNode);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (step_) {
|
||||
case readStep::readNode:
|
||||
packet_.node = b;
|
||||
|
||||
set_step_(readStep::readTag);
|
||||
break;
|
||||
|
||||
case readStep::readTag:
|
||||
packet_.tag = b;
|
||||
|
||||
set_step_(readStep::readPayloadSize);
|
||||
break;
|
||||
|
||||
case readStep::readPayloadSize: {
|
||||
auto status = update_payload_size_(b);
|
||||
if (status == 0) {
|
||||
// finished reading payload size
|
||||
packet_.payload.reserve(payload_size_);
|
||||
set_step_(readStep::readPayloadFlags);
|
||||
}
|
||||
else if (status == -1) {
|
||||
// reset on error
|
||||
reset_(readStep::idle);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case readStep::readPayloadFlags:
|
||||
obfuscated_ = (b & (1 << 4)) != 0;
|
||||
encoding_ = static_cast<payloadEncoding>(b >> 5);
|
||||
|
||||
if (obfuscated_) {
|
||||
lcg_.seed(packet_.tag ^ 0x55);
|
||||
}
|
||||
|
||||
if (encoding_ == payloadEncoding::replace) {
|
||||
set_step_(readStep::readReplacementByte);
|
||||
}
|
||||
else {
|
||||
set_step_(readStep::readPayload);
|
||||
|
||||
if (encoding_ == payloadEncoding::lz) {
|
||||
// reset the InflateTransformer
|
||||
inflate_ = {};
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readReplacementByte:
|
||||
substitute_ = b;
|
||||
|
||||
set_step_(readStep::readPayload);
|
||||
break;
|
||||
|
||||
case readStep::readPayload:
|
||||
// do we need to deobfuscate?
|
||||
if (obfuscated_) {
|
||||
b = deobfuscate_(b);
|
||||
}
|
||||
|
||||
if (encoding_ == payloadEncoding::lz) {
|
||||
inflate_.put(b);
|
||||
for (int i = inflate_.get(); i >= 0; i = inflate_.get()) {
|
||||
packet_.payload.push_back(i);
|
||||
}
|
||||
}
|
||||
else if (encoding_ == payloadEncoding::replace && b == substitute_) {
|
||||
packet_.payload.push_back(SOF);
|
||||
}
|
||||
else if (encoding_ == payloadEncoding::byteStuffing && b == ESC) {
|
||||
set_step_(readStep::readEscaped);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
packet_.payload.push_back(b);
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readEscaped:
|
||||
b = ~b;
|
||||
if (obfuscated_) {
|
||||
b = deobfuscate_(b);
|
||||
}
|
||||
packet_.payload.push_back(b);
|
||||
|
||||
set_step_(readStep::readPayload);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ((step_ == readStep::readPayload || step_ == readStep::readPayloadFlags) &&
|
||||
(packet_.payload.size() >= payload_size_)) {
|
||||
set_step_(readStep::idle);
|
||||
// finished reading packet
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const Packet &PacketDecoder::packet() {
|
||||
return packet_;
|
||||
}
|
||||
}
|
||||
65
acio2emu/packet.h
Normal file
65
acio2emu/packet.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <random> // std::linear_congruential_engine
|
||||
#include <cstdint>
|
||||
|
||||
#include "acio2emu/internal/lz.h"
|
||||
|
||||
namespace acio2emu {
|
||||
struct Packet {
|
||||
uint8_t node;
|
||||
uint8_t tag;
|
||||
std::vector<uint8_t> payload;
|
||||
};
|
||||
|
||||
class PacketDecoder {
|
||||
private:
|
||||
Packet packet_ = {};
|
||||
|
||||
// order matters, don't change this enum!
|
||||
enum payloadEncoding {
|
||||
byteStuffing,
|
||||
raw,
|
||||
unknown,
|
||||
replace,
|
||||
lz,
|
||||
} encoding_;
|
||||
|
||||
uint32_t payload_size_ = 0, payload_size_count_ = 0;
|
||||
|
||||
// payloadEncoding::replace state
|
||||
uint8_t substitute_;
|
||||
|
||||
// payloadEncoding::lz state
|
||||
detail::InflateTransformer inflate_;
|
||||
|
||||
// deobfuscation state
|
||||
bool obfuscated_;
|
||||
std::linear_congruential_engine<uint32_t, 1103515245, 12345, 0> lcg_;
|
||||
|
||||
enum class readStep {
|
||||
idle,
|
||||
readNode,
|
||||
readTag,
|
||||
readPayloadSize,
|
||||
readPayloadFlags,
|
||||
readReplacementByte,
|
||||
readPayload,
|
||||
readEscaped,
|
||||
} step_ = readStep::idle;
|
||||
|
||||
void set_step_(readStep s);
|
||||
void reset_(readStep s);
|
||||
|
||||
int update_payload_size_(uint8_t b);
|
||||
uint8_t deobfuscate_(uint8_t b);
|
||||
|
||||
public:
|
||||
bool update(uint8_t b);
|
||||
const Packet &packet();
|
||||
};
|
||||
|
||||
bool encode_packet(std::queue<uint8_t> &out, uint8_t node, uint8_t tag, const std::vector<uint8_t> &payload);
|
||||
}
|
||||
Reference in New Issue
Block a user