Skip to main content

rgpre/import/rob/
index.rs

1use super::{RobHeader, RobSegment, parser};
2use crate::{Result, error::Error};
3use std::io::{Read, Seek, SeekFrom};
4
5/// ROB headers indexed without reading embedded model payloads.
6pub struct RobIndex {
7    pub header: RobHeader,
8    /// Segment metadata; payloads are loaded by `read_segment`.
9    pub segments: Vec<RobSegment>,
10    offsets: Vec<u64>,
11}
12impl RobIndex {
13    /// Reads headers and seeks over payloads, rejecting ranges beyond the file.
14    pub fn read(reader: &mut (impl Read + Seek)) -> Result<Self> {
15        let length = reader.seek(SeekFrom::End(0))?;
16        reader.seek(SeekFrom::Start(0))?;
17        let mut bytes = [0; 20];
18        reader.read_exact(&mut bytes)?;
19        let (remaining, header) = parser::parse_rob_header(&bytes)?;
20        let mut position = 20 - remaining.len() as u64;
21        if u64::from(header.num_segments) > length.saturating_sub(position) / 80 {
22            return Err(Error::Parse("ROB segment count exceeds file length".into()));
23        }
24        let mut segments = Vec::new();
25        let mut offsets = Vec::new();
26        for index in 0..header.num_segments {
27            reader.seek(SeekFrom::Start(position))?;
28            let mut bytes = [0; 80];
29            reader.read_exact(&mut bytes)?;
30            let (_, segment) = parser::parse_rob_segment_header(&bytes)?;
31            position += 80;
32            offsets.push(position);
33            if !segment.points_to_external_file() {
34                position += u64::from(segment.data_size);
35            }
36            if position > length {
37                return Err(Error::Parse(format!(
38                    "ROB segment {index}: payload exceeds file length"
39                )));
40            }
41            segments.push(segment);
42        }
43        Ok(Self {
44            header,
45            segments,
46            offsets,
47        })
48    }
49
50    /// Reads just one indexed model payload, preserving its segment metadata.
51    pub fn read_segment(
52        &self,
53        reader: &mut (impl Read + Seek),
54        index: usize,
55    ) -> Result<RobSegment> {
56        let mut segment = self
57            .segments
58            .get(index)
59            .ok_or_else(|| Error::Parse(format!("ROB segment {index} missing")))?
60            .clone();
61        if !segment.points_to_external_file() {
62            reader.seek(SeekFrom::Start(self.offsets[index]))?;
63            segment.data.resize(
64                usize::try_from(segment.data_size).map_err(|e| Error::Parse(e.to_string()))?,
65                0,
66            );
67            reader.read_exact(&mut segment.data)?;
68        }
69        Ok(segment)
70    }
71}