use std::{
convert::TryFrom,
fs::File,
io::{BufReader, Read, Seek},
path::Path,
};
use super::{raw, Agent, Event, EvtcError, Log};
pub fn process(data: &raw::Evtc) -> Result<Log, EvtcError> {
let mut agents = setup_agents(data)?;
agents.sort_by_key(Agent::addr);
set_agent_awares(data, &mut agents)?;
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,
})
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Compression {
None,
Zip,
}
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)
}
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> {
data.agents.iter().map(Agent::try_from).collect()
}
fn get_agent_by_addr(agents: &mut [Agent], addr: u64) -> Option<&mut Agent> {
let pos = agents.binary_search_by_key(&addr, Agent::addr).ok()?;
Some(&mut agents[pos])
}
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.set_instance_id(event.src_instid);
if current_agent.first_aware() == 0 {
current_agent.set_first_aware(event.time);
}
current_agent.set_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.set_master_agent(Some(master_addr));
}
}
}
}
Ok(())
}