Skip to main content

zipatch_rs/chunk/
aply.rs

1use binrw::{BinRead, BinResult, Endian};
2
3/// Which apply-time flag a `APLY` chunk toggles.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum ApplyOptionKind {
6    /// Set [`crate::ApplyContext::ignore_missing`].
7    IgnoreMissing,
8    /// Set [`crate::ApplyContext::ignore_old_mismatch`].
9    IgnoreOldMismatch,
10}
11
12fn read_apply_option_kind<R: std::io::Read + std::io::Seek>(
13    reader: &mut R,
14    endian: Endian,
15    (): (),
16) -> BinResult<ApplyOptionKind> {
17    let raw = <u32 as BinRead>::read_options(reader, endian, ())?;
18    match raw {
19        1 => Ok(ApplyOptionKind::IgnoreMissing),
20        2 => Ok(ApplyOptionKind::IgnoreOldMismatch),
21        _ => Err(binrw::Error::Custom {
22            pos: 0,
23            err: Box::new(std::io::Error::new(
24                std::io::ErrorKind::InvalidData,
25                "unknown ApplyOption kind",
26            )),
27        }),
28    }
29}
30
31/// `APLY` chunk: toggles a boolean flag on the [`crate::ApplyContext`].
32#[derive(BinRead, Debug, Clone, PartialEq, Eq)]
33#[br(big)]
34pub struct ApplyOption {
35    /// Which flag this chunk targets.
36    #[br(parse_with = read_apply_option_kind)]
37    pub kind: ApplyOptionKind,
38    /// New value of the targeted flag.
39    #[br(pad_before = 4, map = |x: u32| x != 0)]
40    pub value: bool,
41}
42
43pub(crate) fn parse(body: &[u8]) -> crate::Result<ApplyOption> {
44    super::util::parse_be(body)
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    fn make_body(kind: u32, value: u32) -> Vec<u8> {
52        let mut body = Vec::new();
53        body.extend_from_slice(&kind.to_be_bytes());
54        body.extend_from_slice(&[0u8; 4]); // padding
55        body.extend_from_slice(&value.to_be_bytes());
56        body
57    }
58
59    #[test]
60    fn parses_apply_option_ignore_missing() {
61        let cmd = parse(&make_body(1, 0)).unwrap();
62        assert_eq!(cmd.kind, ApplyOptionKind::IgnoreMissing);
63        assert!(!cmd.value);
64    }
65
66    #[test]
67    fn parses_apply_option_ignore_old_mismatch() {
68        let cmd = parse(&make_body(2, 1)).unwrap();
69        assert_eq!(cmd.kind, ApplyOptionKind::IgnoreOldMismatch);
70        assert!(cmd.value);
71    }
72
73    #[test]
74    fn rejects_invalid_apply_option_kind() {
75        assert!(parse(&make_body(0, 0)).is_err());
76        assert!(parse(&make_body(3, 0)).is_err());
77    }
78}