use crate::constants::SOE_PROTOCOL_VERSION;
use crate::error::Result;
use crate::io::{BinaryReader, BinaryWriter};
use crate::protocol::{DisconnectReason, OpCode};
const OP_CODE_SIZE: usize = 2;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SessionRequest {
pub soe_protocol_version: u32,
pub session_id: u32,
pub udp_length: u32,
pub application_protocol: String,
}
impl SessionRequest {
pub const MIN_SIZE: usize = OP_CODE_SIZE + 4 + 4 + 4 + 1;
pub fn size(&self) -> usize {
OP_CODE_SIZE + 4 + 4 + 4 + self.application_protocol.len() + 1
}
pub fn deserialize(buffer: &[u8], has_op_code: bool) -> Result<Self> {
let mut r = BinaryReader::new(buffer);
if has_op_code {
r.read_u16()?;
}
Ok(Self {
soe_protocol_version: r.read_u32()?,
session_id: r.read_u32()?,
udp_length: r.read_u32()?,
application_protocol: r.read_null_terminated_string()?,
})
}
pub fn serialize(&self, buffer: &mut [u8]) -> Result<usize> {
let mut w = BinaryWriter::new(buffer);
w.write_u16(OpCode::SessionRequest.as_u16())?;
w.write_u32(self.soe_protocol_version)?;
w.write_u32(self.session_id)?;
w.write_u32(self.udp_length)?;
w.write_null_terminated_string(&self.application_protocol)?;
Ok(w.offset())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SessionResponse {
pub session_id: u32,
pub crc_seed: u32,
pub crc_length: u8,
pub is_compression_enabled: bool,
pub unknown_value_1: u8,
pub udp_length: u32,
pub soe_protocol_version: u32,
}
impl SessionResponse {
pub const SIZE: usize = OP_CODE_SIZE + 4 + 4 + 1 + 1 + 1 + 4 + 4;
pub fn deserialize(buffer: &[u8], has_op_code: bool) -> Result<Self> {
let mut r = BinaryReader::new(buffer);
if has_op_code {
r.read_u16()?;
}
Ok(Self {
session_id: r.read_u32()?,
crc_seed: r.read_u32()?,
crc_length: r.read_u8()?,
is_compression_enabled: r.read_bool()?,
unknown_value_1: r.read_u8()?,
udp_length: r.read_u32()?,
soe_protocol_version: r.read_u32()?,
})
}
pub fn serialize(&self, buffer: &mut [u8]) -> Result<usize> {
let mut w = BinaryWriter::new(buffer);
w.write_u16(OpCode::SessionResponse.as_u16())?;
w.write_u32(self.session_id)?;
w.write_u32(self.crc_seed)?;
w.write_u8(self.crc_length)?;
w.write_bool(self.is_compression_enabled)?;
w.write_u8(self.unknown_value_1)?;
w.write_u32(self.udp_length)?;
w.write_u32(self.soe_protocol_version)?;
Ok(w.offset())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Disconnect {
pub session_id: u32,
pub reason: DisconnectReason,
}
impl Disconnect {
pub const SIZE: usize = 4 + 2;
pub fn new(session_id: u32, reason: DisconnectReason) -> Self {
Self { session_id, reason }
}
pub fn deserialize(buffer: &[u8]) -> Result<Self> {
let mut r = BinaryReader::new(buffer);
let session_id = r.read_u32()?;
let reason = DisconnectReason::from_u16(r.read_u16()?);
Ok(Self { session_id, reason })
}
pub fn serialize(&self, buffer: &mut [u8]) -> Result<usize> {
let mut w = BinaryWriter::new(buffer);
w.write_u32(self.session_id)?;
w.write_u16(self.reason.as_u16())?;
Ok(w.offset())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RemapConnection {
pub session_id: u32,
pub crc_seed: u32,
}
impl RemapConnection {
pub const SIZE: usize = OP_CODE_SIZE + 4 + 4;
pub fn deserialize(buffer: &[u8], has_op_code: bool) -> Result<Self> {
let mut r = BinaryReader::new(buffer);
if has_op_code {
r.read_u16()?;
}
Ok(Self {
session_id: r.read_u32()?,
crc_seed: r.read_u32()?,
})
}
pub fn serialize(&self, buffer: &mut [u8]) -> Result<usize> {
let mut w = BinaryWriter::new(buffer);
w.write_u16(OpCode::RemapConnection.as_u16())?;
w.write_u32(self.session_id)?;
w.write_u32(self.crc_seed)?;
Ok(w.offset())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Acknowledge {
pub sequence: u16,
}
impl Acknowledge {
pub const SIZE: usize = 2;
pub fn new(sequence: u16) -> Self {
Self { sequence }
}
pub fn deserialize(buffer: &[u8]) -> Result<Self> {
let mut r = BinaryReader::new(buffer);
Ok(Self {
sequence: r.read_u16()?,
})
}
pub fn serialize(&self, buffer: &mut [u8]) -> Result<usize> {
let mut w = BinaryWriter::new(buffer);
w.write_u16(self.sequence)?;
Ok(w.offset())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AcknowledgeAll {
pub sequence: u16,
}
impl AcknowledgeAll {
pub const SIZE: usize = 2;
pub fn new(sequence: u16) -> Self {
Self { sequence }
}
pub fn deserialize(buffer: &[u8]) -> Result<Self> {
let mut r = BinaryReader::new(buffer);
Ok(Self {
sequence: r.read_u16()?,
})
}
pub fn serialize(&self, buffer: &mut [u8]) -> Result<usize> {
let mut w = BinaryWriter::new(buffer);
w.write_u16(self.sequence)?;
Ok(w.offset())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UnknownSender;
impl UnknownSender {
pub const SIZE: usize = OP_CODE_SIZE;
pub fn serialize(buffer: &mut [u8]) -> Result<usize> {
let mut w = BinaryWriter::new(buffer);
w.write_u16(OpCode::UnknownSender.as_u16())?;
Ok(w.offset())
}
}
pub fn session_request(
session_id: u32,
udp_length: u32,
application_protocol: &str,
) -> SessionRequest {
SessionRequest {
soe_protocol_version: SOE_PROTOCOL_VERSION,
session_id,
udp_length,
application_protocol: application_protocol.to_owned(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn session_request_round_trip() {
let pkt = SessionRequest {
soe_protocol_version: 3,
session_id: 5_467_392,
udp_length: 512,
application_protocol: "TestProtocol".to_owned(),
};
let mut buf = vec![0u8; pkt.size()];
let n = pkt.serialize(&mut buf).unwrap();
assert_eq!(n, pkt.size());
assert_eq!(SessionRequest::deserialize(&buf, true).unwrap(), pkt);
}
#[test]
fn session_response_round_trip() {
let pkt = SessionResponse {
session_id: 531_633,
crc_seed: 34_322,
crc_length: 2,
is_compression_enabled: true,
unknown_value_1: 0,
udp_length: 512,
soe_protocol_version: 3,
};
let mut buf = [0u8; SessionResponse::SIZE];
let n = pkt.serialize(&mut buf).unwrap();
assert_eq!(n, SessionResponse::SIZE);
assert_eq!(SessionResponse::deserialize(&buf, true).unwrap(), pkt);
}
#[test]
fn disconnect_round_trip() {
let pkt = Disconnect::new(5, DisconnectReason::Application);
let mut buf = [0u8; Disconnect::SIZE];
pkt.serialize(&mut buf).unwrap();
assert_eq!(Disconnect::deserialize(&buf).unwrap(), pkt);
}
#[test]
fn remap_connection_round_trip() {
let pkt = RemapConnection {
session_id: 16,
crc_seed: 32,
};
let mut buf = [0u8; RemapConnection::SIZE];
pkt.serialize(&mut buf).unwrap();
assert_eq!(RemapConnection::deserialize(&buf, true).unwrap(), pkt);
}
#[test]
fn acknowledge_round_trip() {
let pkt = Acknowledge::new(2);
let mut buf = [0u8; Acknowledge::SIZE];
pkt.serialize(&mut buf).unwrap();
assert_eq!(Acknowledge::deserialize(&buf).unwrap(), pkt);
let all = AcknowledgeAll::new(2);
let mut buf = [0u8; AcknowledgeAll::SIZE];
all.serialize(&mut buf).unwrap();
assert_eq!(AcknowledgeAll::deserialize(&buf).unwrap(), all);
}
#[test]
fn unknown_sender_serializes_opcode() {
let mut buf = [0u8; UnknownSender::SIZE];
UnknownSender::serialize(&mut buf).unwrap();
assert_eq!(buf, [0x00, 0x1D]);
}
}