wild_container/
lib.rs

1#[cfg(test)]
2mod test;
3
4use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
5use std::io::{self, Cursor, Read, Seek, SeekFrom, Write};
6use std::string::FromUtf8Error;
7use thiserror::Error;
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12const IDENTIFIER: u32 = 0x57494C44;
13
14#[derive(Clone, Ord, PartialOrd, PartialEq, Eq, Debug, Default, Hash)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16pub struct Section {
17	pub id: u8,
18	pub flags: u32,
19	pub checksum: u32,
20	pub body: Vec<u8>,
21}
22
23#[derive(Debug, Error)]
24pub enum Error {
25	#[error("invalid identifier: {0:#X}. required: 0x57494C44")]
26	InvalidIdentifier(u32),
27	#[error("read error occurred")]
28	Io(#[from] io::Error),
29	#[error("string was not UTF-8")]
30	NotUtf8(#[from] FromUtf8Error),
31}
32
33const HEADER_IDENTIFIER: usize = 4;
34const HEADER_SECTIONS_LEN: usize = 1;
35const HEADER_SECTIONS: usize = 1 + 8;
36
37const SECTION_FLAGS: usize = 4;
38const SECTION_CHECKSUM: usize = 4;
39const SECTION_BODY_LEN: usize = 8;
40
41pub fn encode(writer: &mut impl Write, sections: Vec<Section>) -> Result<(), Error> {
42	writer.write_u32::<BigEndian>(IDENTIFIER)?;
43
44	let sections_len = sections.len() as u8;
45	writer.write_u8(sections_len)?;
46
47	let header_offset =
48		HEADER_IDENTIFIER + HEADER_SECTIONS_LEN + (sections.len() * HEADER_SECTIONS);
49
50	let mut encoded_sections = Cursor::new(vec![]);
51
52	for idx in 0..sections_len {
53		let section = &sections[idx as usize];
54		let section_offset =
55			SECTION_FLAGS + SECTION_CHECKSUM + SECTION_BODY_LEN + section.body.len();
56		let offset = header_offset + (section_offset * idx as usize);
57
58		writer.write_u8(section.id)?;
59		writer.write_u64::<BigEndian>(offset as u64)?;
60
61		encoded_sections.write_u32::<BigEndian>(section.flags)?;
62		encoded_sections.write_u32::<BigEndian>(section.checksum)?;
63		encoded_sections.write_u64::<BigEndian>(section.body.len() as u64)?;
64		encoded_sections.write_all(&section.body)?;
65	}
66
67	writer.write_all(&encoded_sections.into_inner())?;
68
69	Ok(())
70}
71
72#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Default, Debug, Hash)]
73pub struct ContainerDecoder<R: Read> {
74	reader: R,
75	idx: usize,
76	pub sections: Vec<(u8, u64)>,
77}
78
79impl<R: Read> ContainerDecoder<R> {
80	pub fn new(mut reader: R) -> Result<Self, Error> {
81		let identifier = reader.read_u32::<BigEndian>()?;
82		if identifier != IDENTIFIER {
83			return Err(Error::InvalidIdentifier(identifier));
84		}
85
86		let sections_len = reader.read_u8()?;
87		let mut sections = vec![];
88
89		for _ in 0..sections_len {
90			let id = reader.read_u8()?;
91			let addr = reader.read_u64::<BigEndian>()?;
92			sections.push((id, addr));
93		}
94
95		Ok(Self {
96			reader,
97			idx: 0,
98			sections,
99		})
100	}
101}
102
103impl<R: Read + Seek> Iterator for ContainerDecoder<R> {
104	type Item = Section;
105
106	fn next(&mut self) -> Option<Self::Item> {
107		if self.idx == self.sections.len() {
108			return None;
109		}
110
111		let (id, addr) = self.sections[self.idx];
112		self.idx += 1;
113
114		self.reader.seek(SeekFrom::Start(addr)).ok()?;
115
116		let flags = self.reader.read_u32::<BigEndian>().ok()?;
117		let checksum = self.reader.read_u32::<BigEndian>().ok()?;
118		let body_len = self.reader.read_u64::<BigEndian>().ok()?;
119
120		let mut body = vec![0; body_len as usize];
121		self.reader.read_exact(&mut body).ok()?;
122
123		Some(Section {
124			id,
125			flags,
126			checksum,
127			body,
128		})
129	}
130}