diff options
Diffstat (limited to 'src/raw')
| -rw-r--r-- | src/raw/parser.rs | 101 | ||||
| -rw-r--r-- | src/raw/types.rs | 24 | 
2 files changed, 100 insertions, 25 deletions
| diff --git a/src/raw/parser.rs b/src/raw/parser.rs index 2de867b..a3a1cd5 100644 --- a/src/raw/parser.rs +++ b/src/raw/parser.rs @@ -78,6 +78,8 @@ pub struct Header {      pub combat_id: u16,      /// Agent count.      pub agent_count: u32, +    /// Evtc revision +    pub revision: u8,  }  /// A completely parsed (raw) EVTC file. @@ -117,6 +119,10 @@ quick_error! {          MalformedHeader {              description("malformed header")          } +        UnknownRevision(rev: u8) { +            description("unknown revision") +            display("revision number not known: {}", rev) +        }          InvalidZip(err: ::zip::result::ZipError) {              from()              description("zip error") @@ -147,17 +153,16 @@ pub fn parse_header<T: Read>(input: &mut T) -> ParseResult<Header> {      input.read_exact(&mut arcdps_build)?;      let build_string = String::from_utf8(arcdps_build)?; -    // Read zero delimiter -    let mut zero = [0]; -    input.read_exact(&mut zero)?; -    if zero != [0] { -        return Err(ParseError::MalformedHeader); -    } +    // Read revision byte +    let mut revision = [0]; +    input.read_exact(&mut revision)?; +    let revision = revision[0];      // Read combat id.      let combat_id = input.read_u16::<LittleEndian>()?; -    // Read zero delimiter again. +    // Read zero delimiter. +    let mut zero = [0];      input.read_exact(&mut zero)?;      if zero != [0] {          return Err(ParseError::MalformedHeader); @@ -170,6 +175,7 @@ pub fn parse_header<T: Read>(input: &mut T) -> ParseResult<Header> {          arcdps_build: build_string,          combat_id,          agent_count, +        revision,      })  } @@ -249,10 +255,11 @@ pub fn parse_skill<T: Read>(input: &mut T) -> ParseResult<Skill> {  /// Parse all combat events.  ///  /// * `input` - Input stream. -pub fn parse_events<T: Read>(input: &mut T) -> ParseResult<Vec<CbtEvent>> { +/// * `parser` - The parse function to use. +pub fn parse_events<T: Read>(input: &mut T, parser: fn(&mut T) -> ParseResult<CbtEvent>) -> ParseResult<Vec<CbtEvent>> {      let mut result = Vec::new();      loop { -        let event = parse_event(input); +        let event = parser(input);          match event {              Ok(x) => result.push(x),              Err(ParseError::Io(ref e)) if e.kind() == ErrorKind::UnexpectedEof => return Ok(result), @@ -263,15 +270,17 @@ pub fn parse_events<T: Read>(input: &mut T) -> ParseResult<Vec<CbtEvent>> {  /// Parse a single combat event.  /// +/// This works for old combat events, i.e. files with revision == 0. +///  /// * `input` - Input stream. -pub fn parse_event<T: Read>(input: &mut T) -> ParseResult<CbtEvent> { +pub fn parse_event_rev0<T: Read>(input: &mut T) -> ParseResult<CbtEvent> {      let time = input.read_u64::<LittleEndian>()?;      let src_agent = input.read_u64::<LE>()?;      let dst_agent = input.read_u64::<LE>()?;      let value = input.read_i32::<LE>()?;      let buff_dmg = input.read_i32::<LE>()?; -    let overstack_value = input.read_u16::<LE>()?; -    let skillid = input.read_u16::<LE>()?; +    let overstack_value = input.read_u16::<LE>()? as u32; +    let skillid = input.read_u16::<LE>()? as u32;      let src_instid = input.read_u16::<LE>()?;      let dst_instid = input.read_u16::<LE>()?;      let src_master_instid = input.read_u16::<LE>()?; @@ -306,6 +315,7 @@ pub fn parse_event<T: Read>(input: &mut T) -> ParseResult<CbtEvent> {          src_instid,          dst_instid,          src_master_instid, +        dst_master_instid: 0,          iff,          buff,          result, @@ -317,9 +327,70 @@ pub fn parse_event<T: Read>(input: &mut T) -> ParseResult<CbtEvent> {          is_statechange,          is_flanking,          is_shields, +        is_offcycle: false,      })  } +/// Parse a single combat event. +/// +/// This works for new combat events, i.e. files with revision == 1. +/// +/// * `input` - Input stream. +pub fn parse_event_rev1<T: Read>(input: &mut T) -> ParseResult<CbtEvent> { +    let time = input.read_u64::<LittleEndian>()?; +    let src_agent = input.read_u64::<LE>()?; +    let dst_agent = input.read_u64::<LE>()?; +    let value = input.read_i32::<LE>()?; +    let buff_dmg = input.read_i32::<LE>()?; +    let overstack_value = input.read_u32::<LE>()?; +    let skillid = input.read_u32::<LE>()?; +    let src_instid = input.read_u16::<LE>()?; +    let dst_instid = input.read_u16::<LE>()?; +    let src_master_instid = input.read_u16::<LE>()?; +    let dst_master_instid = input.read_u16::<LE>()?; + +    let iff = IFF::from_u8(input.read_u8()?).unwrap_or(IFF::None); +    let buff = input.read_u8()?; +    let result = CbtResult::from_u8(input.read_u8()?).unwrap_or(CbtResult::None); +    let is_activation = CbtActivation::from_u8(input.read_u8()?).unwrap_or(CbtActivation::None); +    let is_buffremove = CbtBuffRemove::from_u8(input.read_u8()?).unwrap_or(CbtBuffRemove::None); +    let is_ninety = input.read_u8()? != 0; +    let is_fifty = input.read_u8()? != 0; +    let is_moving = input.read_u8()? != 0; +    let is_statechange = CbtStateChange::from_u8(input.read_u8()?)?; +    let is_flanking = input.read_u8()? != 0; +    let is_shields = input.read_u8()? != 0; +    let is_offcycle = input.read_u8()? != 0; + +    // Four more bytes of internal tracking garbage. +    input.read_u32::<LE>()?; + +    Ok(CbtEvent { +        time, +        src_agent, +        dst_agent, +        value, +        buff_dmg, +        overstack_value, +        skillid, +        src_instid, +        dst_instid, +        src_master_instid, +        dst_master_instid, +        iff, +        buff, +        result, +        is_activation, +        is_buffremove, +        is_ninety, +        is_fifty, +        is_moving, +        is_statechange, +        is_flanking, +        is_shields, +        is_offcycle, +    }) +}  /// Parse a complete EVTC file.  ///  /// * `input` - Input stream. @@ -328,7 +399,11 @@ pub fn parse_file<T: Read>(input: &mut T) -> ParseResult<Evtc> {      let agents = parse_agents(input, header.agent_count)?;      let skill_count = input.read_u32::<LittleEndian>()?;      let skills = parse_skills(input, skill_count)?; -    let events = parse_events(input)?; +    let events = match header.revision { +        0 => parse_events(input, parse_event_rev0)?, +        1 => parse_events(input, parse_event_rev1)?, +        x => return Err(ParseError::UnknownRevision(x)), +    };      Ok(Evtc {          header, diff --git a/src/raw/types.rs b/src/raw/types.rs index 5d4ee10..fb99708 100644 --- a/src/raw/types.rs +++ b/src/raw/types.rs @@ -1,7 +1,6 @@  use std::{self, fmt};  /// The "friend or foe" enum. -#[repr(C)]  #[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]  pub enum IFF {      /// Green vs green, red vs red. @@ -15,7 +14,6 @@ pub enum IFF {  }  /// Combat result (physical) -#[repr(C)]  #[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]  pub enum CbtResult {      /// Good physical hit @@ -43,7 +41,6 @@ pub enum CbtResult {  }  /// Combat activation -#[repr(C)]  #[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]  pub enum CbtActivation {      /// Field is not used in this kind of event. @@ -64,7 +61,6 @@ pub enum CbtActivation {  ///  /// The referenced fields are of the [`CbtEvent`](struct.CbtEvent.html)  /// struct. -#[repr(C)]  #[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]  pub enum CbtStateChange {      /// Field is not used in this kind of event. @@ -139,7 +135,6 @@ pub enum CbtStateChange {  }  /// Combat buff remove type -#[repr(C)]  #[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]  pub enum CbtBuffRemove {      /// Field is not used in this kind of event. @@ -157,7 +152,6 @@ pub enum CbtBuffRemove {  }  /// Custom skill ids -#[repr(C)]  #[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]  pub enum CbtCustomSkill {      /// Not custom but important and unnamed. @@ -169,7 +163,6 @@ pub enum CbtCustomSkill {  }  /// Language -#[repr(C)]  #[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]  pub enum Language {      /// English. @@ -183,7 +176,12 @@ pub enum Language {  }  /// A combat event. -#[repr(C)] +/// +/// This event combines both the old structure and the new structure. Fields not +/// present in the source are zeroed. When dealing with events, always make sure +/// to check the header.revision tag. +/// +/// For conflicting data types, the bigger one is chosen (e.g. u32 over u16).  #[derive(Clone, Debug, PartialEq, Eq)]  pub struct CbtEvent {      /// System time since Windows was started, in milliseconds. @@ -197,15 +195,17 @@ pub struct CbtEvent {      /// Estimated buff damage. Zero on application event.      pub buff_dmg: i32,      /// Estimated overwritten stack duration for buff application. -    pub overstack_value: u16, +    pub overstack_value: u32,      /// Skill id. -    pub skillid: u16, +    pub skillid: u32,      /// Agent map instance id.      pub src_instid: u16,      /// Agent map instance id.      pub dst_instid: u16,      /// Master source agent map instance id if source is a minion/pet.      pub src_master_instid: u16, +    /// Master destination agent map instance id if destination is a minion/pet. +    pub dst_master_instid: u16,      pub iff: IFF,      /// Buff application, removal or damage event.      pub buff: u8, @@ -223,10 +223,11 @@ pub struct CbtEvent {      pub is_flanking: bool,      /// All or part damage was vs. barrier/shield.      pub is_shields: bool, +    /// False if buff dmg happened during tick, true otherwise. +    pub is_offcycle: bool,  }  /// An agent. -#[repr(C)]  #[derive(Clone)]  pub struct Agent {      /// Agent id. @@ -290,7 +291,6 @@ impl fmt::Debug for Agent {  }  /// A skill. -#[repr(C)]  #[derive(Clone)]  pub struct Skill {      /// Skill id. | 
