aboutsummaryrefslogtreecommitdiff
path: root/src/raw
diff options
context:
space:
mode:
Diffstat (limited to 'src/raw')
-rw-r--r--src/raw/parser.rs101
-rw-r--r--src/raw/types.rs24
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.