use crate::parser::{cstring, twint, uuid};
use uuid::Uuid;
use nom::IResult;
use nom::multi::count;
use nom::bytes::streaming::take;
use nom::Err::Failure;
use crate::error::ErrorKind;
fn player_input(input: &[u8]) -> IResult<&[u8], [i32; 10], ErrorKind> {
let (input, i0) = twint(input)?;
let (input, i1) = twint(input)?;
let (input, i2) = twint(input)?;
let (input, i3) = twint(input)?;
let (input, i4) = twint(input)?;
let (input, i5) = twint(input)?;
let (input, i6) = twint(input)?;
let (input, i7) = twint(input)?;
let (input, i8) = twint(input)?;
let (input, i9) = twint(input)?;
Ok((input, [i0, i1, i2, i3, i4, i5, i6, i7, i8, i9]))
}
#[derive(Debug, PartialEq)]
pub struct PlayerDiff {
pub cid: i32,
pub dx: i32,
pub dy: i32,
}
#[derive(Debug, PartialEq)]
pub struct TickSkip {
pub dt: i32,
}
#[derive(Debug, PartialEq)]
pub struct PlayerNew {
pub cid: i32,
pub x: i32,
pub y: i32,
}
#[derive(Debug, PartialEq)]
pub struct PlayerOld {
pub cid: i32,
}
#[derive(Debug, PartialEq)]
pub struct InputDiff {
pub cid: i32,
pub dinput: [i32; 10],
}
#[derive(Debug, PartialEq)]
pub struct InputNew {
pub cid: i32,
pub input: [i32; 10],
}
#[derive(Debug, PartialEq)]
pub struct NetMessage<'a> {
pub cid: i32,
pub msg_id: i32,
pub msg: &'a [u8],
}
pub enum NetMessages<'a> {
StartInfo(PlayerInfo<'a>),
ChangeInfo(PlayerInfo<'a>),
}
impl<'a> NetMessage<'a> {
fn chunk_player_info(input: &[u8]) -> IResult<&[u8], PlayerInfo, ErrorKind> {
let (input, name) = cstring(input)?;
let (input, clan) = cstring(input)?;
let (input, country) = twint(input)?;
let (input, skin) = cstring(input)?;
let (input, custom_color) = twint(input)?;
let custom_color = custom_color != 0;
let (input, color_body) = twint(input)?;
let (input, color_feet) = twint(input)?;
Ok((input, PlayerInfo { name, clan, country, skin, custom_color, color_body, color_feet }))
}
pub fn parse(self) -> Result<NetMessages<'a>, String> {
match self.msg_id {
40 => {
let (remaining, player_info) = NetMessage::chunk_player_info(self.msg)
.map_err(|err| err.to_string())?;
if remaining != b"" {
return Err(format!("{} trailing bytes parsing StartInfo", remaining.len()));
}
Ok(NetMessages::StartInfo(player_info))
},
42 => {
let (remaining, player_info) = NetMessage::chunk_player_info(self.msg)
.map_err(|err| err.to_string())?;
if remaining != b"" {
return Err(format!("{} trailing bytes parsing PlayerInfo", remaining.len()));
}
Ok(NetMessages::ChangeInfo(player_info))
},
_ => todo!()
}
}
}
#[derive(Debug, PartialEq)]
pub struct PlayerInfo<'a> {
pub name: &'a [u8],
pub clan: &'a [u8],
pub country: i32,
pub skin: &'a [u8],
pub custom_color: bool,
pub color_body: i32,
pub color_feet: i32,
}
#[derive(Debug, PartialEq)]
pub struct Join {
pub cid: i32,
}
#[derive(Debug, PartialEq)]
pub struct Drop<'a> {
pub cid: i32,
pub reason: &'a [u8],
}
#[derive(Debug, PartialEq)]
pub struct ConsoleCommand<'a> {
pub cid: i32,
pub flags: i32,
pub cmd: &'a [u8],
pub num_args: i32,
pub args: Vec<&'a [u8]>
}
#[derive(Debug, PartialEq)]
pub struct UnknownEx<'a> {
pub uuid: Uuid,
pub size: i32,
pub data: &'a [u8],
}
#[derive(Debug, PartialEq)]
pub struct DdnetVersionOld {
pub cid: i32,
pub version: i32,
}
#[derive(Debug, PartialEq)]
pub struct DdnetVersion<'a> {
pub cid: i32,
pub connection_id: Uuid,
pub version: i32,
pub version_str: &'a [u8],
}
#[derive(Debug, PartialEq)]
pub struct TeamSave<'a> {
pub team: i32,
pub save_id: Uuid,
pub save: &'a [u8],
}
#[derive(Debug, PartialEq)]
pub struct Team {
pub team: i32,
}
#[derive(Debug, PartialEq)]
pub struct Auth<'a> {
pub cid: i32,
pub level: i32,
pub auth_name: &'a [u8],
}
#[derive(Debug, PartialEq)]
pub struct AuthLogout {
pub cid: i32,
}
#[derive(Debug, PartialEq)]
pub struct PlayerId {
pub cid: i32,
}
#[derive(Debug, PartialEq)]
pub enum Chunk<'a> {
PlayerDiff(PlayerDiff),
Eos,
TickSkip(TickSkip),
PlayerNew(PlayerNew),
PlayerOld(PlayerOld),
InputDiff(InputDiff),
InputNew(InputNew),
NetMessage(NetMessage<'a>),
Join(Join),
Drop(Drop<'a>),
ConsoleCommand(ConsoleCommand<'a>),
UnknownEx(UnknownEx<'a>),
Test,
DdnetVersionOld(DdnetVersionOld),
DdnetVersion(DdnetVersion<'a>),
AuthInit(Auth<'a>),
AuthLogin(Auth<'a>),
AuthLogout(AuthLogout),
JoinVer6(PlayerId),
JoinVer7(PlayerId),
TeamSaveSuccess(TeamSave<'a>),
TeamSaveFailure(Team),
TeamLoadSuccess(TeamSave<'a>),
TeamLoadFailure(Team),
}
fn chunk_player_diff(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, dx) = twint(input)?;
let (input, dy) = twint(input)?;
Ok((input, Chunk::PlayerDiff(PlayerDiff {cid, dx, dy})))
}
fn chunk_tick_skip(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, dt) = twint(input)?;
Ok((input, Chunk::TickSkip(TickSkip { dt })))
}
fn chunk_player_new(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, x) = twint(input)?;
let (input, y) = twint(input)?;
Ok((input, Chunk::PlayerNew(PlayerNew { cid, x, y })))
}
fn chunk_player_old(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
Ok((input, Chunk::PlayerOld(PlayerOld { cid })))
}
fn chunk_input_diff(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, dinput) = player_input(input)?;
Ok((input, Chunk::InputDiff(InputDiff { cid, dinput })))
}
fn chunk_input_new(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, player_input) = player_input(input)?;
Ok((input, Chunk::InputNew(InputNew { cid, input: player_input })))
}
fn chunk_net_message(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, msg_size) = twint(input)?;
let (input, msg) = take(msg_size as usize)(input)?;
let (msg, msg_id) = twint(msg)?;
Ok((input, Chunk::NetMessage(NetMessage { cid, msg_id, msg })))
}
fn chunk_join(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
Ok((input, Chunk::Join(Join { cid })))
}
fn chunk_drop(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, reason) = cstring(input)?;
Ok((input, Chunk::Drop(Drop { cid, reason })))
}
fn chunk_console_command(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, flags) = twint(input)?;
let (input, cmd) = cstring(input)?;
let (input, num_args) = twint(input)?;
let (input, args) = count(cstring, num_args as usize)(input)?;
Ok((input, Chunk::ConsoleCommand(ConsoleCommand { cid, flags, cmd, num_args, args })))
}
const TH_TEST: Uuid = Uuid::from_u128(0x6bb8ba88_0f0b_382e_8dae_dbf4052b8b7d);
const TH_DDNETVER_OLD: Uuid = Uuid::from_u128(0x41b49541_f26f_325d_8715_9baf4b544ef9);
const TH_DDNETVER: Uuid = Uuid::from_u128(0x1397b63e_ee4e_3919_b86a_b058887fcaf5);
const TH_AUTH_INIT: Uuid = Uuid::from_u128(0x60daba5c_52c4_3aeb_b8ba_b2953fb55a17);
const TH_AUTH_LOGIN: Uuid = Uuid::from_u128(0x37ecd3b8_9218_3bb9_a71b_a935b86f6a81);
const TH_AUTH_LOGOUT: Uuid = Uuid::from_u128(0xd4f5abe8_edd2_3fb9_abd8_1c8bb84f4a63);
const TH_JOINVER6: Uuid = Uuid::from_u128(0x1899a382_71e3_36da_937d_c9de6bb95b1d);
const TH_JOINVER7: Uuid = Uuid::from_u128(0x59239b05_0540_318d_bea4_9aa1e80e7d2b);
const TH_SAVE_SUCCESS: Uuid = Uuid::from_u128(0x4560c756_da29_3036_81d4_90a50f0182cd);
const TH_SAVE_FAILURE: Uuid = Uuid::from_u128(0xb29901d5_1244_3bd0_bbde_23d04b1f7ba9);
const TH_LOAD_SUCCESS: Uuid = Uuid::from_u128(0xe05408d3_a313_33df_9eb3_ddb990ab954a);
const TH_LOAD_FAILURE: Uuid = Uuid::from_u128(0xef8905a2_c695_3591_a1cd_53d2015992dd);
fn extension_test(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
if input.len() != 0 {
Err(Failure(ErrorKind::TrailingBytes("EX_TEST", input.len() as u32)))
} else {
Ok((input, Chunk::Test))
}
}
fn extension_ddnetver_old(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, version) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes("EX_DDNETVER_OLD", input.len() as u32)))
} else {
Ok((input, Chunk::DdnetVersionOld(DdnetVersionOld { cid, version })))
}
}
fn extension_ddnetver(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, connection_id) = uuid(input)?;
let (input, version) = twint(input)?;
let (input, version_str) = cstring(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes("EX_DDNETVER", input.len() as u32)))
} else {
Ok((input, Chunk::DdnetVersion(DdnetVersion { cid, connection_id, version, version_str })))
}
}
fn extension_auth_init(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, level) = twint(input)?;
let (input, auth_name) = cstring(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes("EX_AUTH_INIT", input.len() as u32)))
} else {
Ok((input, Chunk::AuthInit(Auth { cid, level, auth_name })))
}
}
fn extension_auth_login(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, level) = twint(input)?;
let (input, auth_name) = cstring(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes("EX_AUTH_INIT", input.len() as u32)))
} else {
Ok((input, Chunk::AuthInit(Auth { cid, level, auth_name })))
}
}
fn extension_auth_logout(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes("EX_AUTH_LOGOUT", input.len() as u32)))
} else {
Ok((input, Chunk::AuthLogout(AuthLogout { cid })))
}
}
fn extension_join_ver6(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes("EX_JOINVER6", input.len() as u32)))
} else {
Ok((input, Chunk::JoinVer6(PlayerId { cid })))
}
}
fn extension_join_ver7(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes("EX_JOINVER7", input.len() as u32)))
} else {
Ok((input, Chunk::JoinVer7(PlayerId { cid })))
}
}
fn extension_save_success(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, team) = twint(input)?;
let (input, save_id) = uuid(input)?;
let (input, save) = cstring(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes("EX_SAVE_SUCCESS", input.len() as u32)))
} else {
Ok((input, Chunk::TeamSaveSuccess(TeamSave { team, save_id, save })))
}
}
fn extension_save_failure(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, team) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes("EX_SAVE_FAILURE", input.len() as u32)))
} else {
Ok((input, Chunk::TeamSaveFailure(Team { team })))
}
}
fn extension_load_success(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, team) = twint(input)?;
let (input, save_id) = uuid(input)?;
let (input, save) = cstring(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes("EX_LOAD_SUCCESS", input.len() as u32)))
} else {
Ok((input, Chunk::TeamLoadSuccess(TeamSave { team, save_id, save })))
}
}
fn extension_load_failure(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, team) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes("EX_LOAD_FAILURE", input.len() as u32)))
} else {
Ok((input, Chunk::TeamLoadFailure(Team { team })))
}
}
fn chunk_unknown_ex(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, uuid) = uuid(input)?;
let (input, size) = twint(input)?;
let (input, data) = take(size as usize)(input)?;
let (_, c) = match uuid {
TH_TEST => extension_test(data)?,
TH_DDNETVER_OLD => extension_ddnetver_old(data)?,
TH_DDNETVER => extension_ddnetver(data)?,
TH_AUTH_INIT => extension_auth_init(data)?,
TH_AUTH_LOGIN => extension_auth_login(data)?,
TH_AUTH_LOGOUT => extension_auth_logout(data)?,
TH_JOINVER6 => extension_join_ver6(data)?,
TH_JOINVER7 => extension_join_ver7(data)?,
TH_SAVE_SUCCESS => { extension_save_success(data) }?,
TH_SAVE_FAILURE => { extension_save_failure(data) }?,
TH_LOAD_SUCCESS => { extension_load_success(data) }?,
TH_LOAD_FAILURE => { extension_load_failure(data) }?,
_ => (input, Chunk::UnknownEx(UnknownEx { uuid, size, data }))
};
Ok((input, c))
}
pub fn chunk(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (i, tag) = twint(input)?;
match tag {
0..=63 => chunk_player_diff(input),
-1 => Ok((i, Chunk::Eos)),
-2 => chunk_tick_skip(i),
-3 => chunk_player_new(i),
-4 => chunk_player_old(i),
-5 => chunk_input_diff(i),
-6 => chunk_input_new(i),
-7 => chunk_net_message(i),
-8 => chunk_join(i),
-9 => chunk_drop(i),
-10 => chunk_console_command(i),
-11 => chunk_unknown_ex(i),
other => Err(Failure(ErrorKind::UnknownTag(other))),
}
}