aboutsummaryrefslogtreecommitdiff
path: root/src/raw/mod.rs
blob: 88b58ac4e383119b8ce8cc55c48ed287783afb46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//! This module defines raw types that correspond 1:1 to the C types used in
//! [arcdps](https://www.deltaconnected.com/arcdps/evtc/README.txt).
//!
//! It is not advised to use those types and functions, as dealing with all the
//! low-level details can be quite tedious. Instead, use the higher-level
//! functions whenever possible.
mod types;

use zip::ZipArchive;

pub use self::types::{
    Agent, CbtActivation, CbtBuffRemove, CbtCustomSkill, CbtEvent, CbtResult, CbtStateChange,
    Language, Skill, IFF,
};

pub mod parser;

pub use self::parser::{parse_file, Evtc, ParseError, ParseResult};

use std::ffi::CStr;
use std::io::{BufReader, Read, Seek};

/// Parse a complete log that was compressed as a zip file.
pub fn parse_zip<T: Read + Seek>(input: &mut T) -> ParseResult<Evtc> {
    let mut archive = ZipArchive::new(input)?;
    let mut file = BufReader::new(archive.by_index(0)?);
    parse_file(&mut file)
}

/// Return a [`CStr`][CStr] up to the first nul byte.
///
/// This is different to [`CStr::from_bytes_with_nul`][CStr::from_bytes_with_nul] in that it stops
/// at the first nul byte instead of raising an error.
///
/// If the slice does not end with a nul byte, this function returns `None`.
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()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_cstr_up_to_nul_terminated() {
        let bytes = b"foo\0";
        let cstr = cstr_up_to_nul(bytes).unwrap();
        assert_eq!(cstr.to_bytes(), b"foo");
    }

    #[test]
    fn test_cstr_up_to_nul_multiple() {
        let bytes = b"foo\0\0\0";
        let cstr = cstr_up_to_nul(bytes).unwrap();
        assert_eq!(cstr.to_bytes(), b"foo");

        let bytes = b"foo\0bar\0\0";
        let cstr = cstr_up_to_nul(bytes).unwrap();
        assert_eq!(cstr.to_bytes(), b"foo");
    }

    #[test]
    fn test_cstr_up_to_nul_unterminated() {
        let bytes = b"foo";
        let cstr = cstr_up_to_nul(bytes);
        assert!(cstr.is_none());
    }
}