diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/event.rs | 5 | ||||
| -rw-r--r-- | src/lib.rs | 87 | ||||
| -rw-r--r-- | src/raw/mod.rs | 9 | ||||
| -rw-r--r-- | src/raw/parser.rs | 29 | ||||
| -rw-r--r-- | src/raw/types.rs | 3 | 
5 files changed, 123 insertions, 10 deletions
| diff --git a/src/event.rs b/src/event.rs index 0ccbced..6dc6591 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,3 +1,6 @@ +//! Event definitions. +//! +//! This module contains the different types of events in their high-level form.  use super::raw;  use std::convert::TryFrom; @@ -19,7 +22,7 @@ pub enum FromRawEventError {  /// A rusty enum for all possible combat events.  /// -/// This makes dealing with `CbtEvent` a bit saner (and safer). +/// This makes dealing with [`CbtEvent`][raw::CbtEvent] a bit saner (and safer).  #[derive(Clone, Debug, PartialEq)]  pub enum EventKind {      // State change events @@ -1,21 +1,58 @@  //! `evtclib` is a crate aiming to provide utility functions to parse and work  //! with `.evtc` reports generated by arcdps.  //! +//! # About evtc Files +//! +//! evtc files are files generated by the (inofficial) arcdps addon to Guild Wars 2. They contain +//! metadata about a fight in the game, such as the boss's name (if it was a raid or fractal boss), +//! the participants, and a stripped-down log of the complete fight. +//! +//! There are other programs (such as +//! [GW2-Elite-Insights-Parser](https://github.com/baaron4/GW2-Elite-Insights-Parser/)) and +//! websites (such as [dps.report](https://dps.report)) which allow you to generate reports from +//! evtc files. +//! +//! A common way to store and distribute evtc files is to zip them to either a `.evtc.zip` (old +//! way) or a `.zevtc` (new way). evtclib uses [`zip`](https://crates.io/crates/zip) to read them, +//! prodiving the [`raw::parse_zip`][raw::parse_zip] convenience function. +//! +//! # Crate Structure +//! +//! The crate consists of two main parts: The [`raw`][raw] parser, which is used to read structured +//! data from binary input streams, and the higher-level abstrations provided in the root and +//! [`event`][event] submodules. +//! +//! Additionally, there are some defintions (such as IDs for various game items) in the +//! [`gamedata`][gamedata] module. +//! +//! The main structs that you should be dealing with are the [`Log`][Log] and its components, such +//! as [`Event`][Event] and [`Agent`][Agent]. +//!  //! # Workflow  //! +//! Currently, there is no convenience function to turn a file into a [`Log`][Log] directly, so you +//! have to use the [`raw`][raw] submodule to obtain a low-level [`Evtc`][raw::Evtc], and then +//! convert it to the high-level [`Log`][Log]. +//!  //! ```no_run -//! # use std::fs::File; -//! // Open some file for processing -//! let mut file = File::open("my_log.evtc").unwrap(); +//! # fn main() -> Result<(), Box<dyn std::error::Error>> { +//! use std::fs::File; +//! // Open a file for processing +//! let mut file = File::open("my_log.evtc")?;  //! // Parse the raw content of the file -//! let raw_log = evtclib::raw::parse_file(&mut file).unwrap(); +//! let raw_log = evtclib::raw::parse_file(&mut file)?;  //! // Process the file to do the nitty-gritty low-level stuff done -//! let log = evtclib::process(&raw_log).unwrap(); +//! let log = evtclib::process(&raw_log)?;  //! // Do work on the log +//! for player in log.players() { +//!     println!("Player {} participated!", player.account_name()); +//! } +//! # Ok(()) +//! # }  //! ```  //! -//! (Look at the note on "Buffering" in the [parser -//! module](raw/parser/index.html#buffering)) +//! Make sure to take a look at the note on "Buffering" in the [parser +//! module](raw/parser/index.html#buffering) in order to increase the speed of your application.  use std::convert::TryFrom;  use std::marker::PhantomData; @@ -32,14 +69,23 @@ pub use event::{Event, EventKind};  pub mod gamedata;  pub use gamedata::{Boss, EliteSpec, Profession}; +/// Any error that can occur during the processing of evtc files.  #[derive(Error, Debug)]  pub enum EvtcError { +    /// Generic error for invalid data in the evtc file.      #[error("invalid data has been provided")]      InvalidData, +    /// The profession id is not known. +    /// +    /// The field contains the unknown profession id.      #[error("invalid profession id: {0}")]      InvalidProfession(u32), +    /// The elite specialization id is not known. +    /// +    /// The field contains the unknown elite specialization id.      #[error("invalid elite specialization id: {0}")]      InvalidEliteSpec(u32), +    /// The file contains invalid utf-8.      #[error("utf8 decoding error: {0}")]      Utf8Error(#[from] std::str::Utf8Error),  } @@ -95,12 +141,17 @@ impl Player {  /// [Entangle](https://wiki.guildwars2.com/wiki/Entangle) or the other objects in the arena.  #[derive(Debug, Clone, Hash, PartialEq, Eq, CopyGetters)]  pub struct Gadget { +    /// The id of the gadget. +    /// +    /// Note that gadgets do not have true ids and the id is generated "through a combination of +    /// gadget parameters".      #[get_copy = "pub"]      id: u16,      name: String,  }  impl Gadget { +    /// The name of the gadget.      pub fn name(&self) -> &str {          &self.name      } @@ -112,12 +163,14 @@ impl Gadget {  /// friendly characters like Mesmer's clones and illusions, Necromancer minions, and so on.  #[derive(Debug, Clone, Hash, PartialEq, Eq, CopyGetters)]  pub struct Character { +    /// The id of the character.      #[get_copy = "pub"]      id: u16,      name: String,  }  impl Character { +    /// The name of the character.      pub fn name(&self) -> &str {          &self.name      } @@ -144,8 +197,17 @@ impl Character {  /// ```  #[derive(Debug, Clone, Hash, PartialEq, Eq)]  pub enum AgentKind { +    /// The agent is a player. +    /// +    /// The player-specific data is in the included [`Player`][Player] struct.      Player(Player), +    /// The agent is a gadget. +    /// +    /// The gadget-specific data is in the included [`Gadget`][Gadget] struct.      Gadget(Gadget), +    /// The agent is a character. +    /// +    /// The character-specific data is in the included [`Character`][Character] struct.      Character(Character),  } @@ -753,6 +815,17 @@ 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)?; diff --git a/src/raw/mod.rs b/src/raw/mod.rs index 77a8571..0b17933 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -33,6 +33,15 @@ pub fn parse_zip<R: Read + Seek>(input: R) -> ParseResult<Evtc> {  /// at the first nul byte instead of raising an error.  ///  /// If the slice does not end with a nul byte, this function returns `None`. +/// +/// ``` +/// # use evtclib::raw::cstr_up_to_nul; +/// # fn doctest() -> Option<()> { +/// assert_eq!(cstr_up_to_nul(b"foo\0bar\0")?.to_bytes(), b"foo"); +/// # Some(()) +/// # } +/// # doctest().unwrap(); +/// ```  pub fn cstr_up_to_nul(bytes: &[u8]) -> Option<&CStr> {      let index = bytes.iter().position(|c| *c == 0)?;      CStr::from_bytes_with_nul(&bytes[..index + 1]).ok() diff --git a/src/raw/parser.rs b/src/raw/parser.rs index b7d6aad..eaf8e6b 100644 --- a/src/raw/parser.rs +++ b/src/raw/parser.rs @@ -62,6 +62,11 @@  //! buffered: cargo run --release  0.22s user 0.04s system 94% cpu 0.275 total  //! raw file: cargo run --release  0.79s user 1.47s system 98% cpu 2.279 total  //! ``` +//! +//! # Resources +//! +//! * [evtc readme](https://www.deltaconnected.com/arcdps/evtc/README.txt) +//! * [C++ output code](https://www.deltaconnected.com/arcdps/evtc/writeencounter.cpp)  use byteorder::{LittleEndian, ReadBytesExt, LE};  use num_traits::FromPrimitive; @@ -84,9 +89,12 @@ pub struct Header {  }  /// A completely parsed (raw) EVTC file. +/// +/// Note that this struct does not yet do any preprocessing of the events. It is simply a +/// representation of the input file as a structured object.  #[derive(Clone, Debug)]  pub struct Evtc { -    /// The file header values +    /// The file header values.      pub header: Header,      /// The skill count.      pub skill_count: u32, @@ -99,27 +107,44 @@ pub struct Evtc {  }  /// A partially-parsed EVTC file, containing everything but the events. -/// This can speed up parsing for applications which can work with the header. +/// +/// This can speed up parsing for applications which can work with the header, as the event stream +/// is the largest chunk of data that has to be parsed.  #[derive(Clone, Debug)]  pub struct PartialEvtc { +    /// The file header values.      pub header: Header, +    /// The skill count.      pub skill_count: u32, +    /// The actual agents.      pub agents: Vec<Agent>, +    /// The skills.      pub skills: Vec<Skill>,  } +/// Any error that can occur during parsing.  #[derive(Error, Debug)]  pub enum ParseError { +    /// The error stems from an underlying input/output error.      #[error("IO error: {0}")]      Io(#[from] io::Error), +    /// The error is caused by invalid UTF-8 data in the file. +    /// +    /// Names in the evtc are expected to be encoded with UTF-8.      #[error("utf8 decoding error: {0}")]      Utf8Error(#[from] std::string::FromUtf8Error), +    /// A generic error to signal invalid data has been encountered.      #[error("invalid data")]      InvalidData, +    /// The header is malformed. +    /// +    /// This is the error that you get when you try to parse a non-evtc file.      #[error("malformed header")]      MalformedHeader, +    /// The revision used by the file is not known.      #[error("unknown revision: {0}")]      UnknownRevision(u8), +    /// The given ZIP archive is invalid.      #[error("invalid archive: {0}")]      InvalidZip(#[from] zip::result::ZipError),  } diff --git a/src/raw/types.rs b/src/raw/types.rs index 6d72892..2e1958c 100644 --- a/src/raw/types.rs +++ b/src/raw/types.rs @@ -1,3 +1,6 @@ +//! Raw evtc structs. +//! +//! This module contains the translated definitions from arcdps's C structs.  use num_derive::FromPrimitive;  use std::{self, fmt}; | 
