aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs157
-rw-r--r--src/processing.rs156
2 files changed, 162 insertions, 151 deletions
diff --git a/src/lib.rs b/src/lib.rs
index eb371bd..64c4ad6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -32,8 +32,9 @@
//!
//! `evtclib` provides two convenience functions to obtain a [`Log`][Log]:
//!
-//! If you have a stream (that is, something that is [`Read`][Read] + [`Seek`][Seek]), you can use
-//! [`process_stream`][process_stream] to obtain a [`Log`][Log] by reading from the stream.
+//! If you have a stream (that is, something that is [`Read`][std::io::Read] +
+//! [`Seek`][std::io::Seek]), you can use [`process_stream`][process_stream] to obtain a
+//! [`Log`][Log] by reading from the stream.
//!
//! If your evtc is saved in a file, you can use [`process_file`][process_file] to obtain a [`Log`]
//! from it. This will also ensure that the buffering is set up correctly, to avoid unnecessary
@@ -89,10 +90,7 @@
use std::collections::HashMap;
use std::convert::TryFrom;
-use std::fs::File;
-use std::io::{BufReader, Read, Seek};
use std::marker::PhantomData;
-use std::path::Path;
use getset::{CopyGetters, Getters};
use num_traits::FromPrimitive;
@@ -103,6 +101,9 @@ pub mod raw;
pub mod event;
pub use event::{Event, EventKind};
+mod processing;
+pub use processing::{Compression, process, process_file, process_stream};
+
pub mod gamedata;
use gamedata::CmTrigger;
pub use gamedata::{Boss, EliteSpec, Profession};
@@ -909,152 +910,6 @@ impl Log {
}
}
-/// Main function to turn a low-level [`Evtc`][raw::Evtc] to a high-level [`Log`][Log].
-///
-/// This function takes an [`Evtc`][raw::Evtc] and does the required type conversions and
-/// pre-processing to get a high-level [`Log`][Log]. This pre-processing includes
-///
-/// * Setting the correct aware times for the agents
-/// * Setting the master agents for each agent
-/// * Converting all events
-///
-/// Note that the structures are quite different, so this function does not consume the given
-/// [`Evtc`][raw::Evtc].
-pub fn process(data: &raw::Evtc) -> Result<Log, EvtcError> {
- // Prepare "augmented" agents
- let mut agents = setup_agents(data)?;
- // Do the first aware/last aware field
- set_agent_awares(data, &mut agents)?;
-
- // Set the master addr field
- set_agent_masters(data, &mut agents)?;
-
- let events = data
- .events
- .iter()
- .filter_map(|e| Event::try_from(e).ok())
- .collect();
-
- Ok(Log {
- agents,
- events,
- boss_id: data.header.combat_id,
- })
-}
-
-/// Indicates the given compression method for the file.
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub enum Compression {
- /// No compression was used.
- None,
- /// The file is wrapped in a zip archive.
- Zip,
-}
-
-/// Convenience function to process a given stream directly.
-///
-/// This is a shorthand for using [`raw::parse_file`][raw::parse_file] followed by
-/// [`process`][process].
-///
-/// The [`Seek`][Seek] bound is needed for zip compressed archives. If you have a reader that does
-/// not support seeking, you can use [`raw::parse_file`][raw::parse_file] directly instead.
-///
-/// ```no_run
-/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
-/// use std::io::Cursor;
-/// use evtclib::Compression;
-/// let data = Cursor::new(vec![]);
-/// let log = evtclib::process_stream(data, Compression::None)?;
-/// # Ok(()) }
-/// ```
-pub fn process_stream<R: Read + Seek>(
- input: R,
- compression: Compression,
-) -> Result<Log, EvtcError> {
- let evtc = match compression {
- Compression::None => raw::parse_file(input)?,
- Compression::Zip => raw::parse_zip(input)?,
- };
- process(&evtc)
-}
-
-/// Convenience function to process a given file directly.
-///
-/// This is a shorthand for opening the file and then using [`process_stream`][process_stream] with
-/// it. This function automatically wraps the raw file in a buffered reader, to ensure the bext
-/// reading performance.
-///
-/// If you need more fine-grained control, consider using [`process_stream`][process_stream] or
-/// [`raw::parse_file`][raw::parse_file] followed by [`process`][process] instead.
-///
-/// ```no_run
-/// # use evtclib::Compression;
-/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
-/// let log = evtclib::process_file("logfile.zevtc", Compression::Zip)?;
-/// # Ok(()) }
-/// ```
-pub fn process_file<P: AsRef<Path>>(path: P, compression: Compression) -> Result<Log, EvtcError> {
- let file = File::open(path).map_err(Into::<raw::ParseError>::into)?;
- let buffered = BufReader::new(file);
- process_stream(buffered, compression)
-}
-
-fn setup_agents(data: &raw::Evtc) -> Result<Vec<Agent>, EvtcError> {
- let mut agents = Vec::with_capacity(data.agents.len());
-
- for raw_agent in &data.agents {
- agents.push(Agent::try_from(raw_agent)?);
- }
- Ok(agents)
-}
-
-fn get_agent_by_addr(agents: &mut [Agent], addr: u64) -> Option<&mut Agent> {
- for agent in agents {
- if agent.addr == addr {
- return Some(agent);
- }
- }
- None
-}
-
-fn set_agent_awares(data: &raw::Evtc, agents: &mut [Agent]) -> Result<(), EvtcError> {
- for event in &data.events {
- if event.is_statechange == raw::CbtStateChange::None {
- if let Some(current_agent) = get_agent_by_addr(agents, event.src_agent) {
- current_agent.instance_id = event.src_instid;
- if current_agent.first_aware == 0 {
- current_agent.first_aware = event.time;
- }
- current_agent.last_aware = event.time;
- }
- }
- }
- Ok(())
-}
-
-fn set_agent_masters(data: &raw::Evtc, agents: &mut [Agent]) -> Result<(), EvtcError> {
- for event in &data.events {
- if event.src_master_instid != 0 {
- let mut master_addr = None;
- for agent in &*agents {
- if agent.instance_id == event.src_master_instid
- && agent.first_aware < event.time
- && event.time < agent.last_aware
- {
- master_addr = Some(agent.addr);
- break;
- }
- }
- if let Some(master_addr) = master_addr {
- if let Some(current_slave) = get_agent_by_addr(agents, event.src_agent) {
- current_slave.master_agent = Some(master_addr);
- }
- }
- }
- }
- Ok(())
-}
-
fn time_between_buffs(events: &[Event], wanted_buff_id: u32) -> u64 {
let mut time_maps: HashMap<u64, Vec<u64>> = HashMap::new();
for event in events {
diff --git a/src/processing.rs b/src/processing.rs
new file mode 100644
index 0000000..f35ab35
--- /dev/null
+++ b/src/processing.rs
@@ -0,0 +1,156 @@
+//! Private module to contain the processing functions.
+
+use std::{
+ convert::TryFrom,
+ fs::File,
+ io::{Read, Seek, BufReader},
+ path::Path,
+};
+
+use super::{Agent, Event, EvtcError, Log, raw};
+
+/// Main function to turn a low-level [`Evtc`][raw::Evtc] to a high-level [`Log`][Log].
+///
+/// This function takes an [`Evtc`][raw::Evtc] and does the required type conversions and
+/// pre-processing to get a high-level [`Log`][Log]. This pre-processing includes
+///
+/// * Setting the correct aware times for the agents
+/// * Setting the master agents for each agent
+/// * Converting all events
+///
+/// Note that the structures are quite different, so this function does not consume the given
+/// [`Evtc`][raw::Evtc].
+pub fn process(data: &raw::Evtc) -> Result<Log, EvtcError> {
+ // Prepare "augmented" agents
+ let mut agents = setup_agents(data)?;
+ // Do the first aware/last aware field
+ set_agent_awares(data, &mut agents)?;
+
+ // Set the master addr field
+ set_agent_masters(data, &mut agents)?;
+
+ let events = data
+ .events
+ .iter()
+ .filter_map(|e| Event::try_from(e).ok())
+ .collect();
+
+ Ok(Log {
+ agents,
+ events,
+ boss_id: data.header.combat_id,
+ })
+}
+
+/// Indicates the given compression method for the file.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum Compression {
+ /// No compression was used.
+ None,
+ /// The file is wrapped in a zip archive.
+ Zip,
+}
+
+/// Convenience function to process a given stream directly.
+///
+/// This is a shorthand for using [`raw::parse_file`][raw::parse_file] followed by
+/// [`process`][process].
+///
+/// The [`Seek`][Seek] bound is needed for zip compressed archives. If you have a reader that does
+/// not support seeking, you can use [`raw::parse_file`][raw::parse_file] directly instead.
+///
+/// ```no_run
+/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+/// use std::io::Cursor;
+/// use evtclib::Compression;
+/// let data = Cursor::new(vec![]);
+/// let log = evtclib::process_stream(data, Compression::None)?;
+/// # Ok(()) }
+/// ```
+pub fn process_stream<R: Read + Seek>(
+ input: R,
+ compression: Compression,
+) -> Result<Log, EvtcError> {
+ let evtc = match compression {
+ Compression::None => raw::parse_file(input)?,
+ Compression::Zip => raw::parse_zip(input)?,
+ };
+ process(&evtc)
+}
+
+/// Convenience function to process a given file directly.
+///
+/// This is a shorthand for opening the file and then using [`process_stream`][process_stream] with
+/// it. This function automatically wraps the raw file in a buffered reader, to ensure the bext
+/// reading performance.
+///
+/// If you need more fine-grained control, consider using [`process_stream`][process_stream] or
+/// [`raw::parse_file`][raw::parse_file] followed by [`process`][process] instead.
+///
+/// ```no_run
+/// # use evtclib::Compression;
+/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+/// let log = evtclib::process_file("logfile.zevtc", Compression::Zip)?;
+/// # Ok(()) }
+/// ```
+pub fn process_file<P: AsRef<Path>>(path: P, compression: Compression) -> Result<Log, EvtcError> {
+ let file = File::open(path).map_err(Into::<raw::ParseError>::into)?;
+ let buffered = BufReader::new(file);
+ process_stream(buffered, compression)
+}
+
+fn setup_agents(data: &raw::Evtc) -> Result<Vec<Agent>, EvtcError> {
+ let mut agents = Vec::with_capacity(data.agents.len());
+
+ for raw_agent in &data.agents {
+ agents.push(Agent::try_from(raw_agent)?);
+ }
+ Ok(agents)
+}
+
+fn get_agent_by_addr(agents: &mut [Agent], addr: u64) -> Option<&mut Agent> {
+ for agent in agents {
+ if agent.addr == addr {
+ return Some(agent);
+ }
+ }
+ None
+}
+
+fn set_agent_awares(data: &raw::Evtc, agents: &mut [Agent]) -> Result<(), EvtcError> {
+ for event in &data.events {
+ if event.is_statechange == raw::CbtStateChange::None {
+ if let Some(current_agent) = get_agent_by_addr(agents, event.src_agent) {
+ current_agent.instance_id = event.src_instid;
+ if current_agent.first_aware == 0 {
+ current_agent.first_aware = event.time;
+ }
+ current_agent.last_aware = event.time;
+ }
+ }
+ }
+ Ok(())
+}
+
+fn set_agent_masters(data: &raw::Evtc, agents: &mut [Agent]) -> Result<(), EvtcError> {
+ for event in &data.events {
+ if event.src_master_instid != 0 {
+ let mut master_addr = None;
+ for agent in &*agents {
+ if agent.instance_id == event.src_master_instid
+ && agent.first_aware < event.time
+ && event.time < agent.last_aware
+ {
+ master_addr = Some(agent.addr);
+ break;
+ }
+ }
+ if let Some(master_addr) = master_addr {
+ if let Some(current_slave) = get_agent_by_addr(agents, event.src_agent) {
+ current_slave.master_agent = Some(master_addr);
+ }
+ }
+ }
+ }
+ Ok(())
+}