Skip to main content

fallout_core/
layout.rs

1use std::io;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub struct ByteRange {
5    pub start: usize,
6    pub end: usize,
7}
8
9impl ByteRange {
10    pub fn len(&self) -> usize {
11        self.end.saturating_sub(self.start)
12    }
13
14    pub fn is_empty(&self) -> bool {
15        self.start == self.end
16    }
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum SectionId {
21    Header,
22    Handler(u8),
23    Tail,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct SectionLayout {
28    pub id: SectionId,
29    pub range: ByteRange,
30}
31
32#[derive(Debug, Clone)]
33pub struct FileLayout {
34    pub file_len: usize,
35    pub sections: Vec<SectionLayout>,
36}
37
38impl FileLayout {
39    pub fn validate(&self) -> io::Result<()> {
40        let Some(first) = self.sections.first() else {
41            return Err(io::Error::new(
42                io::ErrorKind::InvalidData,
43                "file layout must contain at least one section",
44            ));
45        };
46
47        if first.range.start != 0 {
48            return Err(io::Error::new(
49                io::ErrorKind::InvalidData,
50                "layout does not start at byte 0",
51            ));
52        }
53
54        let mut expected = 0usize;
55        for section in &self.sections {
56            if section.range.start != expected {
57                return Err(io::Error::new(
58                    io::ErrorKind::InvalidData,
59                    format!(
60                        "layout gap/overlap around section {:?}: expected start {}, got {}",
61                        section.id, expected, section.range.start
62                    ),
63                ));
64            }
65            if section.range.end < section.range.start {
66                return Err(io::Error::new(
67                    io::ErrorKind::InvalidData,
68                    format!(
69                        "invalid section range {:?}: {}..{}",
70                        section.id, section.range.start, section.range.end
71                    ),
72                ));
73            }
74            expected = section.range.end;
75        }
76
77        if expected != self.file_len {
78            return Err(io::Error::new(
79                io::ErrorKind::InvalidData,
80                format!(
81                    "layout does not cover file: ended at {}, file length {}",
82                    expected, self.file_len
83                ),
84            ));
85        }
86
87        Ok(())
88    }
89}