1use crate::reader::Reader;
12use serde::Serialize;
13
14use crate::{Result, invalid};
15
16#[derive(Clone, Debug, PartialEq, Serialize)]
21pub struct UnityUrpSettings {
22 pub main_light_mode: i64,
24 pub main_light_shadows: bool,
26 pub additional_lights_mode: i64,
28 pub additional_light_shadows: bool,
30 pub reflection_probe_blending: bool,
32 pub reflection_probe_box_projection: bool,
34 pub reflection_probe_atlas: bool,
36 pub shadow_distance: f32,
38 pub shadow_cascades: u8,
40 pub soft_shadows: bool,
42 pub mixed_lighting: bool,
44 pub light_cookies: bool,
46 pub light_layers: bool,
48 pub lod_cross_fade: bool,
50 pub sh_eval_mode: i64,
52 pub light_probe_system: i64,
54}
55
56const ASSET_VERSION: i32 = 13;
58
59pub fn read_urp_settings(bytes: &[u8]) -> Result<UnityUrpSettings> {
61 let mut r = Reader::new(bytes, false);
62 let flag = |r: &mut Reader| -> Result<bool> {
63 let b = r.bool()?;
64 r.align(4)?;
65 Ok(b)
66 };
67 let version = r.i32()?;
68 if version != ASSET_VERSION {
69 return Err(invalid(format!(
70 "Universal Render Pipeline asset version {version}: only version {ASSET_VERSION} has a verified layout"
71 )));
72 }
73 r.i32()?; r.i32()?; r.take(12)?; let renderers = r.u32()?;
77 let renderers_len = usize::try_from(renderers)
78 .ok()
79 .and_then(|n| n.checked_mul(12))
80 .ok_or_else(|| invalid(format!("renderer list of {renderers} entries too long")))?;
81 r.take(renderers_len)?;
82 r.i32()?; r.take(8)?; r.i32()?; r.take(8)?; r.i32()?; r.i32()?; r.f32()?; r.i32()?; r.take(4)?; r.f32()?; let lod_cross_fade = flag(&mut r)?;
93 r.i32()?; let sh_eval_mode = r.i32()?;
95 let light_probe_system = r.i32()?;
96 r.i32()?; r.i32()?; r.take(16)?; r.i32()?; let main_light_mode = r.i32()?;
101 let main_light_shadows = flag(&mut r)?;
102 r.i32()?; let additional_lights_mode = r.i32()?;
104 r.i32()?; let additional_light_shadows = flag(&mut r)?;
106 r.take(16)?; let reflection_probe_blending = flag(&mut r)?;
108 let reflection_probe_box_projection = flag(&mut r)?;
109 let reflection_probe_atlas = flag(&mut r)?;
110 let shadow_distance = r.f32()?;
111 let shadow_cascades = r.i32()?;
112 r.take(4 + 8 + 12 + 4 + 4 + 4)?; let soft_shadows = flag(&mut r)?;
114 r.take(4)?; r.i32()?; r.i32()?; r.i32()?; r.i32()?; r.take(8)?; let mixed_lighting = flag(&mut r)?;
121 let light_cookies = flag(&mut r)?;
122 let light_layers = flag(&mut r)?;
123 let in_range = |what: &str, ok: bool| -> Result<()> {
125 ok.then_some(()).ok_or_else(|| {
126 invalid(format!(
127 "Universal Render Pipeline asset: {what} out of range (an unverified layout)"
128 ))
129 })
130 };
131 in_range("m_ShadowCascadeCount", (1..=4).contains(&shadow_cascades))?;
132 in_range(
133 "m_ShadowDistance",
134 shadow_distance.is_finite() && shadow_distance >= 0.0,
135 )?;
136 in_range("m_MainLightRenderingMode", (0..=2).contains(&main_light_mode))?;
137 in_range(
138 "m_AdditionalLightsRenderingMode",
139 (0..=2).contains(&additional_lights_mode),
140 )?;
141 in_range("m_ShEvalMode", (0..=3).contains(&sh_eval_mode))?;
142 Ok(UnityUrpSettings {
143 main_light_mode: i64::from(main_light_mode),
144 main_light_shadows,
145 additional_lights_mode: i64::from(additional_lights_mode),
146 additional_light_shadows,
147 reflection_probe_blending,
148 reflection_probe_box_projection,
149 reflection_probe_atlas,
150 shadow_distance,
151 shadow_cascades: u8::try_from(shadow_cascades).map_err(|_| {
152 invalid(format!(
153 "Universal Render Pipeline asset: m_ShadowCascadeCount {shadow_cascades} out of range"
154 ))
155 })?,
156 soft_shadows,
157 mixed_lighting,
158 light_cookies,
159 light_layers,
160 lod_cross_fade,
161 sh_eval_mode: i64::from(sh_eval_mode),
162 light_probe_system: i64::from(light_probe_system),
163 })
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 fn asset(cascades: i32) -> Vec<u8> {
172 let mut b: Vec<u8> = Vec::new();
173 let int = |b: &mut Vec<u8>, v: i32| b.extend(v.to_le_bytes());
174 let float = |b: &mut Vec<u8>, v: f32| b.extend(v.to_le_bytes());
175 let bool4 = |b: &mut Vec<u8>, v: bool| b.extend(i32::from(v).to_le_bytes());
176 int(&mut b, 13); int(&mut b, 13);
178 int(&mut b, 1); b.extend([0; 12]); int(&mut b, 1); b.extend([0; 12]);
182 int(&mut b, 0); bool4(&mut b, true); bool4(&mut b, false); int(&mut b, 1);
186 bool4(&mut b, false); bool4(&mut b, true); int(&mut b, 0);
189 int(&mut b, 4); float(&mut b, 1.0);
191 int(&mut b, 0); bool4(&mut b, false);
193 float(&mut b, 0.92);
194 bool4(&mut b, true); int(&mut b, 1);
196 int(&mut b, 2); int(&mut b, 0); int(&mut b, 1024);
199 int(&mut b, 256);
200 for _ in 0..4 {
201 bool4(&mut b, false);
202 }
203 int(&mut b, 1); int(&mut b, 1); bool4(&mut b, true); int(&mut b, 4096);
207 int(&mut b, 1); int(&mut b, 8);
209 bool4(&mut b, true); for v in [1024, 256, 512, 1024] {
211 int(&mut b, v);
212 }
213 bool4(&mut b, true); bool4(&mut b, false); bool4(&mut b, true); float(&mut b, 400.0);
217 int(&mut b, cascades);
218 float(&mut b, 0.25);
219 for v in [0.1, 0.3, 0.123, 0.29, 0.54, 0.11, 0.1, 0.5] {
220 float(&mut b, v);
221 }
222 bool4(&mut b, true); bool4(&mut b, true); int(&mut b, 64);
225 int(&mut b, 3);
226 int(&mut b, 2048);
227 int(&mut b, 3);
228 bool4(&mut b, true);
229 bool4(&mut b, false);
230 bool4(&mut b, true); bool4(&mut b, false); bool4(&mut b, true); b
234 }
235
236 #[test]
239 fn a_urp_assets_settings_are_read_by_the_verified_layout() {
240 let s = read_urp_settings(&asset(4)).unwrap();
241 assert_eq!((s.shadow_cascades, s.shadow_distance), (4, 400.0));
242 assert!(s.main_light_shadows && s.soft_shadows && s.additional_light_shadows);
243 assert!(
244 s.reflection_probe_blending && !s.reflection_probe_box_projection && s.reflection_probe_atlas
245 );
246 assert!(s.mixed_lighting && !s.light_cookies && s.light_layers && s.lod_cross_fade);
247 assert_eq!(
248 (s.main_light_mode, s.additional_lights_mode, s.sh_eval_mode),
249 (1, 1, 2)
250 );
251 let mut other = asset(4);
253 other[0] = 12;
254 assert!(read_urp_settings(&other).is_err());
255 assert!(read_urp_settings(&asset(7)).is_err());
256 assert!(read_urp_settings(&asset(4)[..100]).is_err());
257 }
258}