source2-demo 0.5.1

Dota 2 / Deadlock / CS2 replay parser written in Rust
Documentation
use crate::error::ParserError;
use crate::reader::SliceReader;
use crate::writer::{BitsWriter, BitstreamWriter};
use bitter::BitReader;

pub(crate) fn bit_position(reader: &SliceReader<'_>) -> usize {
    let current_bits = (reader.source_buffer.len() - reader.source_offset) * 8;
    let remaining_bits = reader.bit_reader.bits_remaining().unwrap_or_default();
    reader.source_offset * 8 + current_bits.saturating_sub(remaining_bits)
}

pub(crate) fn copy_original_bits(
    source: &[u8],
    start_bit: usize,
    bit_len: usize,
    writer: &mut BitstreamWriter<'_>,
) -> Result<(), ParserError> {
    let end_bit = start_bit + bit_len;
    let aligned_start = ((start_bit + 7) & !7).min(end_bit);
    for bit_index in start_bit..aligned_start {
        let byte = source[bit_index / 8];
        let bit = ((byte >> (bit_index % 8)) & 1) != 0;
        writer.write_bit(bit)?;
    }

    let aligned_end = end_bit & !7;
    let mut byte_index = aligned_start / 8;
    let end_byte = aligned_end / 8;
    while byte_index + 8 <= end_byte {
        let value = u64::from_le_bytes(source[byte_index..byte_index + 8].try_into().unwrap());
        writer.write_bits(64, value)?;
        byte_index += 8;
    }
    while byte_index < end_byte {
        writer.write_bits(8, source[byte_index] as u64)?;
        byte_index += 1;
    }

    for bit_index in aligned_end.max(aligned_start)..end_bit {
        let byte = source[bit_index / 8];
        let bit = ((byte >> (bit_index % 8)) & 1) != 0;
        writer.write_bit(bit)?;
    }
    Ok(())
}

pub(crate) fn copy_remaining_bits(
    reader: &mut SliceReader<'_>,
    writer: &mut BitstreamWriter<'_>,
) -> Result<(), ParserError> {
    let Some(bits_left) = reader.bit_reader.bits_remaining() else {
        return Ok(());
    };

    for _ in 0..bits_left {
        let bit = reader
            .bit_reader
            .read_bit()
            .ok_or_else(|| ParserError::IoError("unexpected end of entity data".to_string()))?;
        writer.write_bit(bit)?;
    }
    Ok(())
}