1use crate::{Result, error, parts};
18
19#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct ConstantMapping {
22 pub buffer: u16,
24 pub start: u16,
26 pub count: u16,
28 pub target: u16,
30 pub conversion: [u8; 4],
32}
33
34#[derive(Clone, Debug, PartialEq, Eq)]
36pub struct ScalarMapping {
37 pub buffer: u16,
39 pub source: u16,
41 pub component: u16,
43 pub target: u16,
45}
46
47#[derive(Clone, Debug, PartialEq, Eq)]
49pub struct SamplerMapping {
50 pub resource: u8,
52 pub sampler: u8,
54 pub target: u8,
56}
57
58#[derive(Clone, Debug, PartialEq, Eq)]
60pub struct RuntimeConstant {
61 pub kind: u16,
63 pub target: u16,
65}
66
67#[derive(Clone, Debug, PartialEq, Eq)]
69pub struct Level9 {
70 pub version: u32,
72 pub program: Vec<u8>,
74 pub program_offset: u32,
76 pub constants: Vec<ConstantMapping>,
78 pub integers: Vec<ScalarMapping>,
80 pub booleans: Vec<ScalarMapping>,
82 pub samplers: Vec<SamplerMapping>,
84 pub runtime: Vec<RuntimeConstant>,
86}
87
88struct Part<'a> {
89 data: &'a [u8],
90 kind: &'static str,
92}
93
94impl Part<'_> {
95 fn u16(&self, at: usize) -> Result<u16> {
96 self.data
97 .get(at..at.saturating_add(2))
98 .and_then(|b| b.try_into().ok())
99 .map(u16::from_le_bytes)
100 .ok_or_else(|| error(at / 4, format!("the {} part ends before byte {at}", self.kind)))
101 }
102 fn u32(&self, at: usize) -> Result<u32> {
103 self.data
104 .get(at..at.saturating_add(4))
105 .and_then(|b| b.try_into().ok())
106 .map(u32::from_le_bytes)
107 .ok_or_else(|| error(at / 4, format!("the {} part ends before byte {at}", self.kind)))
108 }
109 fn byte(&self, at: usize) -> Result<u8> {
110 self.data
111 .get(at)
112 .copied()
113 .ok_or_else(|| error(at / 4, format!("the {} part ends before byte {at}", self.kind)))
114 }
115 fn rows(&self, at: usize, row: usize) -> Result<Vec<usize>> {
118 let count = usize::from(self.u16(at)?);
119 let offset = usize::from(self.u16(at.saturating_add(2))?);
120 let end = count.checked_mul(row).and_then(|n| n.checked_add(offset));
121 if end.is_none_or(|end| end > self.data.len()) {
122 return Err(error(
123 at / 4,
124 format!(
125 "a table of {count} rows of {row} bytes at byte {offset} runs past the {} part's {} bytes",
126 self.kind,
127 self.data.len()
128 ),
129 ));
130 }
131 Ok((0..count)
132 .map(|n| offset.saturating_add(n.saturating_mul(row)))
133 .collect())
134 }
135 fn scalars(&self, at: usize) -> Result<Vec<ScalarMapping>> {
136 self.rows(at, 8)?
137 .into_iter()
138 .map(|r| {
139 Ok(ScalarMapping {
140 buffer: self.u16(r)?,
141 source: self.u16(r.saturating_add(2))?,
142 component: self.u16(r.saturating_add(4))?,
143 target: self.u16(r.saturating_add(6))?,
144 })
145 })
146 .collect()
147 }
148}
149
150pub fn level9(bytes: &[u8]) -> Result<Option<Level9>> {
153 part(bytes, "Aon9")
154}
155
156pub(crate) fn part(bytes: &[u8], kind: &'static str) -> Result<Option<Level9>> {
158 let directory = parts(bytes)?;
159 let Some(chunk) = directory.iter().find(|c| c.kind == kind.as_bytes()) else {
160 return Ok(None);
161 };
162 let part = Part {
163 data: chunk.data,
164 kind,
165 };
166 let size = part.u32(0)?;
167 if usize::try_from(size).ok() != Some(part.data.len()) {
168 return Err(error(
169 0,
170 format!("the {kind} part states {size} bytes but has {}", part.data.len()),
171 ));
172 }
173 let length = usize::try_from(part.u32(8)?).unwrap_or(usize::MAX);
174 let program_offset = part.u32(12)?;
175 let offset = usize::try_from(program_offset).unwrap_or(usize::MAX);
176 let program = offset
177 .checked_add(length)
178 .and_then(|end| part.data.get(offset..end))
179 .ok_or_else(|| {
180 error(
181 2,
182 format!(
183 "a program of {length} bytes at byte {offset} runs past the {kind} part's {} bytes",
184 part.data.len()
185 ),
186 )
187 })?;
188 let constants = part
189 .rows(16, 12)?
190 .into_iter()
191 .map(|r| {
192 Ok(ConstantMapping {
193 buffer: part.u16(r)?,
194 start: part.u16(r.saturating_add(2))?,
195 count: part.u16(r.saturating_add(4))?,
196 target: part.u16(r.saturating_add(6))?,
197 conversion: [
198 part.byte(r.saturating_add(8))?,
199 part.byte(r.saturating_add(9))?,
200 part.byte(r.saturating_add(10))?,
201 part.byte(r.saturating_add(11))?,
202 ],
203 })
204 })
205 .collect::<Result<_>>()?;
206 let samplers = part
207 .rows(28, 4)?
208 .into_iter()
209 .map(|r| {
210 let fourth = part.byte(r.saturating_add(3))?;
211 if fourth != 0 {
212 return Err(error(
213 r / 4,
214 format!(
215 "byte {} of a sampler mapping is {fourth}, where every one seen is 0",
216 r.saturating_add(3)
217 ),
218 ));
219 }
220 Ok(SamplerMapping {
221 resource: part.byte(r)?,
222 sampler: part.byte(r.saturating_add(1))?,
223 target: part.byte(r.saturating_add(2))?,
224 })
225 })
226 .collect::<Result<_>>()?;
227 let runtime = part
228 .rows(32, 4)?
229 .into_iter()
230 .map(|r| {
231 Ok(RuntimeConstant {
232 kind: part.u16(r)?,
233 target: part.u16(r.saturating_add(2))?,
234 })
235 })
236 .collect::<Result<_>>()?;
237 Ok(Some(Level9 {
238 version: part.u32(4)?,
239 program: program.to_vec(),
240 program_offset,
241 constants,
242 integers: part.scalars(20)?,
243 booleans: part.scalars(24)?,
244 samplers,
245 runtime,
246 }))
247}