evtc_parse/
log.rs

1use crate::{util::Endian, Agent, Header, LogTransformed, Parse, ParseError, Save, Skill};
2use byteorder::{ReadBytesExt, WriteBytesExt};
3use evtc::Event;
4use std::{fs::File, io, path::Path};
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9/// An EVTC log.
10#[derive(Debug, Clone)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub struct Log {
13    /// The log header with meta information.
14    pub header: Header,
15
16    /// Agents (entities) present in the log.
17    pub agents: Vec<Agent>,
18
19    /// Information about skills used in the log.
20    pub skills: Vec<Skill>,
21
22    /// Every [`Event`] occurring in the log.
23    ///
24    /// Some events may also hold meta information, for example [`StateChange::BuffFormula`](crate::StateChange::BuffFormula).
25    pub events: Vec<Event>,
26}
27
28impl Log {
29    /// Parses a [`Log`] from a given [`Path`] to a log file.
30    ///
31    /// With the `"zevtc"` or `"zip"` feature enabled this also supports compressed log files.
32    pub fn parse_file(path: impl AsRef<Path>) -> Result<Log, ParseError> {
33        let path = path.as_ref();
34        let mut file = io::BufReader::new(File::open(path)?);
35
36        #[cfg(feature = "zevtc")]
37        if let Some("zevtc" | "zip") = path.extension().and_then(|ext| ext.to_str()) {
38            return Self::parse_zevtc(file);
39        }
40
41        Log::parse(&mut file)
42    }
43
44    /// Returns the [`Agent`] with the given id.
45    #[inline]
46    pub fn agent(&self, id: u64) -> Option<&Agent> {
47        self.agents.iter().find(|agent| agent.id == id)
48    }
49
50    /// Returns a mutable reference to the [`Agent`] with the given id.
51    #[inline]
52    pub fn agent_mut(&mut self, id: u64) -> Option<&mut Agent> {
53        self.agents.iter_mut().find(|agent| agent.id == id)
54    }
55
56    /// Returns the name(s) of the [`Agent`] with the given id.
57    #[inline]
58    pub fn agent_name(&self, id: u64) -> Option<&[String]> {
59        self.agent(id).map(|agent| agent.name.as_slice())
60    }
61
62    /// Returns the [`Skill`] with the given id.
63    #[inline]
64    pub fn skill(&self, id: u32) -> Option<&Skill> {
65        self.skills.iter().find(|skill| skill.id == id)
66    }
67
68    /// Returns a mutable reference to the [`Skill`] with the given id.
69    #[inline]
70    pub fn skill_mut(&mut self, id: u32) -> Option<&mut Skill> {
71        self.skills.iter_mut().find(|skill| skill.id == id)
72    }
73
74    /// Returns the name of the [`Skill`] with the given id.
75    #[inline]
76    pub fn skill_name(&self, id: u32) -> Option<&str> {
77        self.skill(id).map(|skill| skill.name.as_str())
78    }
79
80    /// Converts the log into its [`LogTransformed`] equivalent.
81    #[inline]
82    pub fn into_transformed(self) -> LogTransformed {
83        self.into()
84    }
85}
86
87impl Parse for Log {
88    type Error = ParseError;
89
90    fn parse(input: &mut impl io::Read) -> Result<Self, Self::Error> {
91        let header = Header::parse(input)?;
92
93        // we only support current revision
94        if header.revision != 1 {
95            return Err(ParseError::UnsupportedRevision(header.revision));
96        }
97
98        let agent_count = input.read_u32::<Endian>()?;
99        let agents = Agent::parse_multi(input, agent_count as usize)?;
100
101        let skill_count = input.read_u32::<Endian>()?;
102        let skills = Skill::parse_multi(input, skill_count as usize)?;
103
104        let mut events = Vec::new();
105        while let Ok(event) = Event::parse(input) {
106            events.push(event);
107        }
108
109        Ok(Self {
110            header,
111            agents,
112            skills,
113            events,
114        })
115    }
116}
117
118impl Save for Log {
119    type Error = io::Error;
120
121    fn save(&self, output: &mut impl io::Write) -> Result<(), Self::Error> {
122        self.header.save(output)?;
123
124        output.write_u32::<Endian>(self.agents.len() as u32)?;
125        for agent in &self.agents {
126            agent.save(output)?;
127        }
128
129        output.write_u32::<Endian>(self.skills.len() as u32)?;
130        for skill in &self.skills {
131            skill.save(output)?;
132        }
133
134        for event in &self.events {
135            event.save(output)?;
136        }
137
138        Ok(())
139    }
140}