evtc_parse/
agent.rs
1use crate::{
2 util::{read_string_buffer, write_string_buffer, Endian},
3 Parse, ParseError, Save,
4};
5use byteorder::{ReadBytesExt, WriteBytesExt};
6use evtc::AgentKind;
7use std::io;
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
23#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
24pub struct Agent {
25 pub id: u64,
27
28 pub name: Vec<String>,
32
33 pub profession: u32,
35
36 pub is_elite: u32,
38
39 pub hitbox_width: u16,
41
42 pub hitbox_height: u16,
44
45 pub toughness: u16,
47
48 pub concentration: u16,
50
51 pub healing: u16,
53
54 pub condition: u16,
56}
57
58impl Agent {
59 pub const NAME_SIZE: usize = 64;
61
62 #[inline]
64 pub const fn kind(&self) -> AgentKind {
65 AgentKind::new(self.profession, self.is_elite)
66 }
67
68 fn parse_name(input: &mut impl io::Read) -> Result<Vec<String>, ParseError> {
70 let string = read_string_buffer::<{ Self::NAME_SIZE }>(input)?;
71 Ok(string
72 .split('\0')
73 .filter(|part| !part.is_empty())
74 .map(|part| part.to_string())
75 .collect())
76 }
77
78 fn save_name(&self, output: &mut impl io::Write) -> Result<(), io::Error> {
80 let string = self.name.join("\0");
81 write_string_buffer::<{ Self::NAME_SIZE }>(output, &string)
82 }
83}
84
85impl Parse for Agent {
86 type Error = ParseError;
87
88 fn parse(input: &mut impl io::Read) -> Result<Self, Self::Error> {
89 let id = input.read_u64::<Endian>()?;
90 let profession = input.read_u32::<Endian>()?;
91 let is_elite = input.read_u32::<Endian>()?;
92 let toughness = input.read_u16::<Endian>()?;
93 let concentration = input.read_u16::<Endian>()?;
94 let healing = input.read_u16::<Endian>()?;
95 let hitbox_width = input.read_u16::<Endian>()?;
96 let condition = input.read_u16::<Endian>()?;
97 let hitbox_height = input.read_u16::<Endian>()?;
98
99 let name = Self::parse_name(input)?;
100
101 input.read_u32::<Endian>()?;
103
104 Ok(Self {
105 name,
106 id,
107 profession,
108 is_elite,
109 hitbox_width,
110 hitbox_height,
111 toughness,
112 concentration,
113 healing,
114 condition,
115 })
116 }
117}
118
119impl Save for Agent {
120 type Error = io::Error;
121
122 fn save(&self, output: &mut impl io::Write) -> Result<(), Self::Error> {
123 output.write_u64::<Endian>(self.id)?;
124 output.write_u32::<Endian>(self.profession)?;
125 output.write_u32::<Endian>(self.is_elite)?;
126 output.write_u16::<Endian>(self.toughness)?;
127 output.write_u16::<Endian>(self.concentration)?;
128 output.write_u16::<Endian>(self.healing)?;
129 output.write_u16::<Endian>(self.hitbox_width)?;
130 output.write_u16::<Endian>(self.condition)?;
131 output.write_u16::<Endian>(self.hitbox_height)?;
132
133 self.save_name(output)?;
134
135 output.write_u32::<Endian>(0)
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn agent_name() {
146 let name: Vec<String> = vec!["Character".into(), ":Account.1234".into(), "1".into()];
147 let data: &[u8; Agent::NAME_SIZE] = b"Character\0:Account.1234\x001\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
148
149 let parsed = Agent::parse_name(io::Cursor::new(data.as_slice()).get_mut())
150 .expect("failed to parse agent name");
151 assert_eq!(name, parsed, "incorrect agent name");
152
153 let agent = Agent {
154 id: 0,
155 name,
156 profession: 0,
157 is_elite: 0,
158 hitbox_width: 0,
159 hitbox_height: 0,
160 toughness: 0,
161 concentration: 0,
162 healing: 0,
163 condition: 0,
164 };
165
166 let mut buffer = [123u8; Agent::NAME_SIZE];
167 agent
168 .save_name(&mut buffer.as_mut_slice())
169 .expect("failed to save agent");
170 assert_eq!(data, &buffer, "incorrect saved data");
171 }
172}