use crate::network::Frame;
use serde::ser::{SerializeMap, SerializeSeq, SerializeStruct};
use serde::{Serialize, Serializer};
#[derive(Serialize, PartialEq, Debug, Clone)]
pub struct Replay {
pub header_size: i32,
pub header_crc: u32,
pub major_version: i32,
pub minor_version: i32,
pub net_version: Option<i32>,
pub game_type: String,
#[serde(serialize_with = "pair_vec")]
pub properties: Vec<(String, HeaderProp)>,
pub content_size: i32,
pub content_crc: u32,
pub network_frames: Option<NetworkFrames>,
pub levels: Vec<String>,
pub keyframes: Vec<KeyFrame>,
pub debug_info: Vec<DebugInfo>,
pub tick_marks: Vec<TickMark>,
pub packages: Vec<String>,
pub objects: Vec<String>,
pub names: Vec<String>,
pub class_indices: Vec<ClassIndex>,
pub net_cache: Vec<ClassNetCache>,
}
#[derive(Serialize, PartialEq, Debug, Clone)]
pub struct NetworkFrames {
pub frames: Vec<Frame>,
}
#[derive(Serialize, PartialEq, Debug, Clone)]
pub struct TickMark {
pub description: String,
pub frame: i32,
}
#[derive(Serialize, PartialEq, Debug, Clone, Copy)]
pub struct KeyFrame {
pub time: f32,
pub frame: i32,
pub position: i32,
}
#[derive(PartialEq, Debug, Clone)]
pub enum HeaderProp {
Array(Vec<Vec<(String, HeaderProp)>>),
Bool(bool),
Byte {
kind: String,
value: Option<String>,
},
Float(f32),
Int(i32),
Name(String),
QWord(u64),
Str(String),
Struct {
name: String,
fields: Vec<(String, HeaderProp)>,
},
}
impl HeaderProp {
pub fn as_array(&self) -> Option<&Vec<Vec<(String, HeaderProp)>>> {
if let HeaderProp::Array(arr) = self {
Some(arr)
} else {
None
}
}
pub fn as_bool(&self) -> Option<bool> {
if let HeaderProp::Bool(val) = self {
Some(*val)
} else {
None
}
}
pub fn as_float(&self) -> Option<f32> {
if let HeaderProp::Float(val) = self {
Some(*val)
} else {
None
}
}
pub fn as_u64(&self) -> Option<u64> {
if let HeaderProp::QWord(val) = self {
Some(*val)
} else {
None
}
}
pub fn as_i32(&self) -> Option<i32> {
if let HeaderProp::Int(val) = self {
Some(*val)
} else {
None
}
}
pub fn as_string(&self) -> Option<&str> {
match self {
HeaderProp::Name(val) => Some(val.as_str()),
HeaderProp::Str(val) => Some(val.as_str()),
_ => None,
}
}
pub fn is_byte(&self) -> bool {
matches!(self, HeaderProp::Byte { .. })
}
}
#[derive(Serialize, PartialEq, Debug, Clone)]
pub struct DebugInfo {
pub frame: i32,
pub user: String,
pub text: String,
}
#[derive(Serialize, PartialEq, Debug, Clone)]
pub struct ClassIndex {
pub class: String,
pub index: i32,
}
#[derive(Serialize, PartialEq, Debug, Clone, Copy)]
pub struct CacheProp {
pub object_ind: i32,
pub stream_id: i32,
}
#[derive(Serialize, PartialEq, Debug, Clone)]
pub struct ClassNetCache {
pub object_ind: i32,
pub parent_id: i32,
pub cache_id: i32,
pub properties: Vec<CacheProp>,
}
fn pair_vec<K, V, S>(inp: &[(K, V)], serializer: S) -> Result<S::Ok, S::Error>
where
K: Serialize,
V: Serialize,
S: Serializer,
{
let mut state = serializer.serialize_map(Some(inp.len()))?;
for (key, val) in inp.iter() {
state.serialize_key(key)?;
state.serialize_value(val)?;
}
state.end()
}
impl Serialize for HeaderProp {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
HeaderProp::Array(ref x) => {
#[derive(Serialize)]
struct Elem<'a>(#[serde(serialize_with = "pair_vec")] &'a [(String, HeaderProp)]);
let mut state = serializer.serialize_seq(Some(x.len()))?;
for inner in x {
state.serialize_element(&Elem(inner.as_slice()))?;
}
state.end()
}
HeaderProp::Struct {
ref name,
ref fields,
} => {
#[derive(Serialize)]
struct Elem<'a>(#[serde(serialize_with = "pair_vec")] &'a [(String, HeaderProp)]);
let mut st = serializer.serialize_struct("Struct", 2)?;
st.serialize_field("name", name)?;
st.serialize_field("fields", &Elem(fields.as_slice()))?;
st.end()
}
HeaderProp::Bool(ref x) => serializer.serialize_bool(*x),
HeaderProp::Byte {
ref kind,
ref value,
} => {
let mut byte = serializer.serialize_struct("Byte", 2)?;
byte.serialize_field("kind", kind)?;
byte.serialize_field("value", value)?;
byte.end()
}
HeaderProp::Float(ref x) => serializer.serialize_f32(*x),
HeaderProp::Int(ref x) => serializer.serialize_i32(*x),
HeaderProp::QWord(ref x) => serializer.collect_str(x),
HeaderProp::Name(ref x) | HeaderProp::Str(ref x) => serializer.serialize_str(x),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn to_json<T: serde::Serialize>(input: &T) -> std::string::String {
serde_json::to_string(input).unwrap()
}
#[test]
fn serialize_header_array() {
let data = vec![
vec![
(String::from("frame"), HeaderProp::Int(441)),
(
String::from("PlayerName"),
HeaderProp::Str(String::from("rust is awesome")),
),
],
vec![
(String::from("frame"), HeaderProp::Int(1738)),
(
String::from("PlayerName"),
HeaderProp::Str(String::from("rusty")),
),
],
];
let actual = to_json(&HeaderProp::Array(data));
assert!(actual.contains("\"PlayerName\":\"rust is awesome\""));
assert!(actual.contains("\"PlayerName\":\"rusty\""));
assert!(actual.contains("\"frame\":441"));
assert!(actual.contains("\"frame\":1738"));
}
#[test]
fn serialize_header_bool() {
assert_eq!(to_json(&HeaderProp::Bool(false)), "false");
assert_eq!(to_json(&HeaderProp::Bool(true)), "true");
}
#[test]
fn serialize_header_numbers() {
assert_eq!(
to_json(&HeaderProp::Byte {
kind: String::from("a"),
value: Some(String::from("B"))
}),
r#"{"kind":"a","value":"B"}"#
);
assert_eq!(to_json(&HeaderProp::QWord(10)), "\"10\"");
assert_eq!(to_json(&HeaderProp::Float(10.2)), "10.2");
assert_eq!(to_json(&HeaderProp::Int(11)), "11");
}
#[test]
fn serialize_header_str() {
let val = "hello world";
assert_eq!(
to_json(&HeaderProp::Str(String::from(val))),
"\"hello world\""
);
assert_eq!(
to_json(&HeaderProp::Name(String::from(val))),
"\"hello world\""
);
}
}