diff options
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 157 |
1 files changed, 6 insertions, 151 deletions
@@ -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 { |