#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 &out, const std::vector &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 &out, uint8_t node, uint8_t tag, const std::vector &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(node * 3), tag, static_cast(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(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_; } }