plasma-prp 0.1.0

Read, write, inspect, and manipulate Plasma engine PRP files used by Myst Online: Uru Live
Documentation
//! hsBitVector — variable-length bit vector.
//!
//! Binary format: u32 count of u32 words, then that many u32 words.
//!
//! C++ ref: CoreLib/hsBitVector.h/.cpp

use std::io::Read;

use anyhow::Result;

use crate::resource::prp::PlasmaRead;

/// A variable-length bit vector, matching C++ hsBitVector.
#[derive(Debug, Clone, Default)]
pub struct BitVector {
    pub words: Vec<u32>,
}

impl BitVector {
    pub fn new() -> Self {
        Self { words: Vec::new() }
    }

    pub fn is_bit_set(&self, bit: usize) -> bool {
        let word_idx = bit / 32;
        let bit_idx = bit % 32;
        if word_idx >= self.words.len() {
            return false;
        }
        (self.words[word_idx] & (1 << bit_idx)) != 0
    }

    pub fn set_bit(&mut self, bit: usize, on: bool) {
        let word_idx = bit / 32;
        let bit_idx = bit % 32;
        while word_idx >= self.words.len() {
            self.words.push(0);
        }
        if on {
            self.words[word_idx] |= 1 << bit_idx;
        } else {
            self.words[word_idx] &= !(1 << bit_idx);
        }
    }

    pub fn read(reader: &mut impl Read) -> Result<Self> {
        let count = reader.read_u32()?;
        let mut words = Vec::with_capacity(count as usize);
        for _ in 0..count {
            words.push(reader.read_u32()?);
        }
        Ok(Self { words })
    }
}

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

    #[test]
    fn test_empty() {
        let bv = BitVector::new();
        assert!(!bv.is_bit_set(0));
        assert!(!bv.is_bit_set(100));
    }

    #[test]
    fn test_set_get() {
        let mut bv = BitVector::new();
        bv.set_bit(5, true);
        assert!(bv.is_bit_set(5));
        assert!(!bv.is_bit_set(4));
        bv.set_bit(33, true);
        assert!(bv.is_bit_set(33));
        assert_eq!(bv.words.len(), 2);
    }

    #[test]
    fn test_read() {
        use std::io::Cursor;
        // 1 word, value = 0x00000005 (bits 0 and 2 set)
        let data = [
            0x01, 0x00, 0x00, 0x00, // count = 1
            0x05, 0x00, 0x00, 0x00, // word = 5
        ];
        let mut cursor = Cursor::new(&data);
        let bv = BitVector::read(&mut cursor).unwrap();
        assert!(bv.is_bit_set(0));
        assert!(!bv.is_bit_set(1));
        assert!(bv.is_bit_set(2));
    }
}