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