1use crate::player::PPtr;
26use crate::reader::Reader;
27use crate::version::UnityVersion;
28use serde::Serialize;
29
30use crate::{Result, invalid};
31
32#[derive(Clone, Debug, PartialEq, Serialize)]
38pub struct UnityUrpSettings {
39 pub renderers: Vec<PPtr>,
42 pub default_renderer: i64,
45 pub require_depth_texture: bool,
48 pub require_opaque_texture: bool,
50 pub msaa: i64,
52 pub main_light_mode: i64,
54 pub main_light_shadows: bool,
56 pub additional_lights_mode: i64,
58 pub additional_light_shadows: bool,
60 pub reflection_probe_blending: bool,
62 pub reflection_probe_box_projection: bool,
64 pub reflection_probe_atlas: bool,
66 pub shadow_distance: f32,
68 pub shadow_cascades: u8,
70 pub soft_shadows: bool,
72 pub mixed_lighting: bool,
74 pub light_cookies: bool,
76 pub light_layers: bool,
78 pub lod_cross_fade: bool,
80 pub sh_eval_mode: i64,
82 pub light_probe_system: i64,
84}
85
86const ASSET_VERSION: i32 = 13;
88
89#[derive(Clone, Copy, Debug, PartialEq, Eq)]
92#[non_exhaustive]
93pub enum UrpAssetLayout {
94 Urp17_3,
96}
97
98const URP_17_3_PLAYERS: [&str; 2] = ["6000.3.12f1", "6000.3.15f1"];
100
101impl UrpAssetLayout {
102 pub fn of(unity: UnityVersion) -> Result<Self> {
105 for v in URP_17_3_PLAYERS {
106 if UnityVersion::parse(v)? == unity {
107 return Ok(UrpAssetLayout::Urp17_3);
108 }
109 }
110 Err(invalid(format!(
111 "a Universal Render Pipeline asset of a Unity {unity} player: its layout is verified only on the players \
112 of Unity {} (the package's fields are not recorded in the asset, and other players store others)",
113 URP_17_3_PLAYERS.join(" and ")
114 )))
115 }
116}
117
118pub fn read_urp_settings(bytes: &[u8], layout: UrpAssetLayout) -> Result<UnityUrpSettings> {
122 match layout {
123 UrpAssetLayout::Urp17_3 => {}
124 }
125 let mut r = Reader::new(bytes, false);
126 let version = r.i32()?;
127 if version != ASSET_VERSION {
128 return Err(invalid(format!(
129 "Universal Render Pipeline asset version {version}: only version {ASSET_VERSION} has a verified layout"
130 )));
131 }
132 r.i32()?; r.i32()?; r.take(12)?; let renderers = pptrs(&mut r, "m_RendererDataList")?;
136 let default_renderer = r.i32()?;
137 let require_depth_texture = flag(&mut r)?;
138 let require_opaque_texture = flag(&mut r)?;
139 r.i32()?; r.take(8)?; r.i32()?; let msaa = r.i32()?;
143 r.f32()?; r.i32()?; r.take(4)?; r.f32()?; let lod_cross_fade = flag(&mut r)?;
148 r.i32()?; let sh_eval_mode = r.i32()?;
150 let light_probe_system = r.i32()?;
151 r.i32()?; r.i32()?; r.take(16)?; r.i32()?; let main_light_mode = r.i32()?;
156 let main_light_shadows = flag(&mut r)?;
157 r.i32()?; let additional_lights_mode = r.i32()?;
159 r.i32()?; let additional_light_shadows = flag(&mut r)?;
161 r.take(16)?; let reflection_probe_blending = flag(&mut r)?;
163 let reflection_probe_box_projection = flag(&mut r)?;
164 let reflection_probe_atlas = flag(&mut r)?;
165 let shadow_distance = r.f32()?;
166 let shadow_cascades = r.i32()?;
167 r.take(4 + 8 + 12 + 4 + 4 + 4)?; let soft_shadows = flag(&mut r)?;
169 r.take(4)?; r.i32()?; r.i32()?; r.i32()?; r.i32()?; r.take(8)?; let mixed_lighting = flag(&mut r)?;
176 let light_cookies = flag(&mut r)?;
177 let light_layers = flag(&mut r)?;
178 r.i32()?; r.i32()?; flag(&mut r)?; r.i32()?; r.i32()?; for _ in 0..4 {
184 flag(&mut r)?;
186 }
187 r.i32()?; r.f32()?; flag(&mut r)?; r.i32()?; flag(&mut r)?; r.take(12)?; r.i32()?; pptr(&mut r)?; strings(&mut r, "apvScenesData.obsoleteSceneBounds.m_Keys")?;
198 let n = list_len(&mut r, "apvScenesData.obsoleteSceneBounds.m_Values", 24)?;
199 for _ in 0..n {
200 r.take(24)?; }
202 strings(&mut r, "apvScenesData.obsoleteHasProbeVolumes.m_Keys")?;
203 let n = list_len(&mut r, "apvScenesData.obsoleteHasProbeVolumes.m_Values", 1)?;
204 for _ in 0..n {
205 r.bool()?;
206 }
207 r.align(4)?;
208 r.i32()?; r.i32()?; r.take(24)?; used_up(&r, "UniversalRenderPipelineAsset")?;
212 let in_range = |what: &str, ok: bool| -> Result<()> {
214 ok.then_some(()).ok_or_else(|| {
215 invalid(format!(
216 "Universal Render Pipeline asset: {what} out of range (an unverified layout)"
217 ))
218 })
219 };
220 in_range("m_ShadowCascadeCount", (1..=4).contains(&shadow_cascades))?;
221 in_range(
222 "m_ShadowDistance",
223 shadow_distance.is_finite() && shadow_distance >= 0.0,
224 )?;
225 in_range("m_MainLightRenderingMode", (0..=2).contains(&main_light_mode))?;
226 in_range(
227 "m_AdditionalLightsRenderingMode",
228 (0..=2).contains(&additional_lights_mode),
229 )?;
230 in_range("m_ShEvalMode", (0..=3).contains(&sh_eval_mode))?;
231 in_range("m_MSAA", [1, 2, 4, 8].contains(&msaa))?;
232 in_range(
233 "m_DefaultRendererIndex",
234 usize::try_from(default_renderer).is_ok_and(|i| i < renderers.len()),
235 )?;
236 Ok(UnityUrpSettings {
237 renderers,
238 default_renderer: i64::from(default_renderer),
239 require_depth_texture,
240 require_opaque_texture,
241 msaa: i64::from(msaa),
242 main_light_mode: i64::from(main_light_mode),
243 main_light_shadows,
244 additional_lights_mode: i64::from(additional_lights_mode),
245 additional_light_shadows,
246 reflection_probe_blending,
247 reflection_probe_box_projection,
248 reflection_probe_atlas,
249 shadow_distance,
250 shadow_cascades: u8::try_from(shadow_cascades).map_err(|_| {
251 invalid(format!(
252 "Universal Render Pipeline asset: m_ShadowCascadeCount {shadow_cascades} out of range"
253 ))
254 })?,
255 soft_shadows,
256 mixed_lighting,
257 light_cookies,
258 light_layers,
259 lod_cross_fade,
260 sh_eval_mode: i64::from(sh_eval_mode),
261 light_probe_system: i64::from(light_probe_system),
262 })
263}
264
265fn flag(r: &mut Reader) -> Result<bool> {
267 let b = r.bool()?;
268 r.align(4)?;
269 Ok(b)
270}
271
272fn strings(r: &mut Reader, what: &str) -> Result<Vec<String>> {
274 let n = list_len(r, what, 4)?;
275 (0..n)
276 .map(|_| {
277 let len = list_len(r, what, 1)?;
278 let s = std::str::from_utf8(r.take(len)?)
279 .map_err(|_| invalid(format!("{what}: a string that is not UTF-8")))?
280 .to_owned();
281 r.align(4)?;
282 Ok(s)
283 })
284 .collect()
285}
286
287fn pptr(r: &mut Reader) -> Result<PPtr> {
289 Ok(PPtr {
290 file_id: r.i32()?,
291 path_id: r.i64()?,
292 })
293}
294
295fn list_len(r: &mut Reader, what: &str, size: usize) -> Result<usize> {
297 let n = r.u32()?;
298 usize::try_from(n)
299 .ok()
300 .filter(|&n| n.checked_mul(size).is_some_and(|len| len <= r.remaining()))
301 .ok_or_else(|| {
302 invalid(format!(
303 "{what}: {n} entries of {size} bytes, {} bytes left",
304 r.remaining()
305 ))
306 })
307}
308
309fn pptrs(r: &mut Reader, what: &str) -> Result<Vec<PPtr>> {
311 let n = list_len(r, what, 12)?;
312 (0..n).map(|_| pptr(r)).collect()
313}
314
315fn choice(r: &mut Reader, what: &str, values: &[i32]) -> Result<i64> {
317 let v = r.i32()?;
318 if values.contains(&v) {
319 Ok(i64::from(v))
320 } else {
321 Err(invalid(format!(
322 "{what} {v} (the values: {values:?}; an unverified layout)"
323 )))
324 }
325}
326
327fn first(n: i32) -> Vec<i32> {
329 (0..n).collect()
330}
331
332fn used_up(r: &Reader, what: &str) -> Result<()> {
334 match r.remaining() {
335 0 => Ok(()),
336 n => Err(invalid(format!(
337 "{what}: {n} bytes past the verified layout's {} (another layout)",
338 r.pos
339 ))),
340 }
341}
342
343const RENDERER_ASSET_VERSION: i32 = 3;
345
346const RENDERER_TAIL: usize = 96;
351
352#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
357pub struct UnityStencilState {
358 pub override_stencil: bool,
360 pub reference: i64,
362 pub compare: i64,
364 pub pass: i64,
366 pub fail: i64,
368 pub z_fail: i64,
370}
371
372#[derive(Clone, Debug, PartialEq, Serialize)]
381pub struct UnityUrpRenderer {
382 pub features: Vec<PPtr>,
385 pub feature_map: Vec<i64>,
388 pub native_render_pass: bool,
390 pub xr_system_data: PPtr,
392 pub post_process_data: PPtr,
394 pub prepass_layer_mask: u32,
396 pub opaque_layer_mask: u32,
398 pub transparent_layer_mask: u32,
400 pub stencil: UnityStencilState,
402 pub shadow_transparent_receive: bool,
404 pub rendering_mode: i64,
406 pub depth_priming_mode: i64,
408 pub copy_depth_mode: i64,
410 pub depth_attachment_format: i64,
412 pub depth_texture_format: i64,
414 pub accurate_gbuffer_normals: bool,
416 pub intermediate_texture_mode: i64,
418}
419
420pub fn read_urp_renderer(bytes: &[u8]) -> Result<UnityUrpRenderer> {
422 const WHAT: &str = "Universal Render Pipeline renderer";
423 let mut r = Reader::new(bytes, false);
424 r.take(12 * 9)?;
426 let features = pptrs(&mut r, "m_RendererFeatures")?;
427 let n = list_len(&mut r, "m_RendererFeatureMap", 8)?;
428 let feature_map = (0..n).map(|_| r.i64()).collect::<Result<Vec<_>>>()?;
429 let native_render_pass = flag(&mut r)?;
430 if r.remaining() != RENDERER_TAIL {
431 return Err(invalid(format!(
432 "{WHAT}: {} bytes after m_UseNativeRenderPass, the verified layout stores {RENDERER_TAIL} (84 is the layout \
433 without xrSystemData, which is not verified)",
434 r.remaining()
435 )));
436 }
437 let xr_system_data = pptr(&mut r)?;
438 let post_process_data = pptr(&mut r)?;
439 let version = r.i32()?;
440 if version != RENDERER_ASSET_VERSION {
441 return Err(invalid(format!(
442 "{WHAT} asset version {version}: only version {RENDERER_ASSET_VERSION} has a verified layout"
443 )));
444 }
445 let prepass_layer_mask = r.u32()?;
446 let opaque_layer_mask = r.u32()?;
447 let transparent_layer_mask = r.u32()?;
448 let stencil = UnityStencilState {
449 override_stencil: flag(&mut r)?,
450 reference: i64::from(r.i32()?),
451 compare: choice(&mut r, "stencilCompareFunction", &first(9))?,
452 pass: choice(&mut r, "passOperation", &first(8))?,
453 fail: choice(&mut r, "failOperation", &first(8))?,
454 z_fail: choice(&mut r, "zFailOperation", &first(8))?,
455 };
456 let depth_formats = [0, 90, 91, 92, 93, 94, 151];
457 let renderer = UnityUrpRenderer {
458 features,
459 feature_map,
460 native_render_pass,
461 xr_system_data,
462 post_process_data,
463 prepass_layer_mask,
464 opaque_layer_mask,
465 transparent_layer_mask,
466 stencil,
467 shadow_transparent_receive: flag(&mut r)?,
468 rendering_mode: choice(&mut r, "m_RenderingMode", &first(4))?,
469 depth_priming_mode: choice(&mut r, "m_DepthPrimingMode", &first(3))?,
470 copy_depth_mode: choice(&mut r, "m_CopyDepthMode", &first(3))?,
471 depth_attachment_format: choice(&mut r, "m_DepthAttachmentFormat", &depth_formats)?,
472 depth_texture_format: choice(&mut r, "m_DepthTextureFormat", &depth_formats)?,
473 accurate_gbuffer_normals: flag(&mut r)?,
474 intermediate_texture_mode: choice(&mut r, "m_IntermediateTextureMode", &first(2))?,
475 };
476 used_up(&r, WHAT)?;
477 Ok(renderer)
478}
479
480pub fn read_urp_feature_active(bytes: &[u8]) -> Result<bool> {
483 flag(&mut Reader::new(bytes, false))
484}
485
486pub const URP_DECAL_FEATURE: &str = "UnityEngine.Rendering.Universal.DecalRendererFeature";
488
489pub const URP_FULL_SCREEN_FEATURE: &str = "UnityEngine.Rendering.Universal.FullScreenPassRendererFeature";
491
492#[derive(Clone, Debug, PartialEq, Serialize)]
495pub struct UnityUrpDecalFeature {
496 pub active: bool,
498 pub technique: i64,
500 pub max_draw_distance: f32,
502 pub decal_layers: bool,
504 pub dbuffer_surface_data: i64,
507 pub screen_space_normal_blend: i64,
509}
510
511pub fn read_urp_decal_feature(bytes: &[u8]) -> Result<UnityUrpDecalFeature> {
513 let mut r = Reader::new(bytes, false);
514 let feature = UnityUrpDecalFeature {
515 active: flag(&mut r)?,
516 technique: choice(&mut r, "DecalSettings.technique", &first(3))?,
517 max_draw_distance: r.f32()?,
518 decal_layers: flag(&mut r)?,
519 dbuffer_surface_data: choice(&mut r, "DBufferSettings.surfaceData", &first(3))?,
520 screen_space_normal_blend: choice(&mut r, "DecalScreenSpaceSettings.normalBlend", &first(3))?,
521 };
522 used_up(&r, "Decal renderer feature")?;
523 Ok(feature)
524}
525
526const FULL_SCREEN_VERSION: i32 = 1;
528
529#[derive(Clone, Debug, PartialEq, Serialize)]
531pub struct UnityUrpFullScreenFeature {
532 pub active: bool,
534 pub injection_point: i64,
537 pub fetch_color_buffer: bool,
539 pub requirements: i64,
542 pub pass_material: PPtr,
544 pub pass_index: i64,
546 pub bind_depth_stencil_attachment: bool,
548}
549
550pub fn read_urp_full_screen_feature(bytes: &[u8]) -> Result<UnityUrpFullScreenFeature> {
552 const WHAT: &str = "Full Screen Pass renderer feature";
553 let mut r = Reader::new(bytes, false);
554 let active = flag(&mut r)?;
555 let injection_point = choice(&mut r, "injectionPoint", &[450, 550, 600])?;
556 let fetch_color_buffer = flag(&mut r)?;
557 let requirements = i64::from(r.i32()?);
558 let pass_material = pptr(&mut r)?;
559 let pass_index = i64::from(r.i32()?);
560 let bind_depth_stencil_attachment = flag(&mut r)?;
561 let version = r.i32()?;
562 if version != FULL_SCREEN_VERSION {
563 return Err(invalid(format!(
564 "{WHAT} m_Version {version}: only version {FULL_SCREEN_VERSION} is verified"
565 )));
566 }
567 used_up(&r, WHAT)?;
568 Ok(UnityUrpFullScreenFeature {
569 active,
570 injection_point,
571 fetch_color_buffer,
572 requirements,
573 pass_material,
574 pass_index,
575 bind_depth_stencil_attachment,
576 })
577}
578
579pub const URP_CAMERA_DATA: &str = "UnityEngine.Rendering.Universal.UniversalAdditionalCameraData";
581
582const CAMERA_VERSION: i32 = 2;
584
585#[derive(Clone, Debug, PartialEq, Serialize)]
587pub struct UnityTaaSettings {
588 pub quality: i64,
590 pub frame_influence: f32,
592 pub jitter_scale: f32,
594 pub mip_bias: f32,
596 pub variance_clamp_scale: f32,
598 pub contrast_adaptive_sharpening: f32,
600}
601
602#[derive(Clone, Debug, PartialEq, Serialize)]
605pub struct UnityUrpCamera {
606 pub render_shadows: bool,
608 pub depth_texture_option: i64,
610 pub opaque_texture_option: i64,
612 pub camera_type: i64,
614 pub stack: Vec<PPtr>,
616 pub renderer_index: i64,
619 pub volume_layer_mask: u32,
621 pub volume_trigger: PPtr,
623 pub volume_update_mode: i64,
625 pub render_post_processing: bool,
627 pub antialiasing: i64,
629 pub antialiasing_quality: i64,
631 pub stop_nan: bool,
633 pub dithering: bool,
635 pub clear_depth: bool,
637 pub allow_xr_rendering: bool,
639 pub allow_hdr_output: bool,
641 pub use_screen_coord_override: bool,
643 pub screen_size_override: [f32; 4],
645 pub screen_coord_scale_bias: [f32; 4],
647 pub requires_depth_texture: bool,
649 pub requires_color_texture: bool,
651 pub taa: UnityTaaSettings,
653}
654
655pub fn read_urp_camera(bytes: &[u8]) -> Result<UnityUrpCamera> {
657 const WHAT: &str = "Universal Render Pipeline camera data";
658 let mut r = Reader::new(bytes, false);
659 let vec4 = |r: &mut Reader| -> Result<[f32; 4]> { Ok([r.f32()?, r.f32()?, r.f32()?, r.f32()?]) };
660 let camera = UnityUrpCamera {
661 render_shadows: flag(&mut r)?,
662 depth_texture_option: choice(&mut r, "m_RequiresDepthTextureOption", &first(3))?,
663 opaque_texture_option: choice(&mut r, "m_RequiresOpaqueTextureOption", &first(3))?,
664 camera_type: choice(&mut r, "m_CameraType", &first(2))?,
665 stack: pptrs(&mut r, "m_Cameras")?,
666 renderer_index: i64::from(r.i32()?),
667 volume_layer_mask: r.u32()?,
668 volume_trigger: pptr(&mut r)?,
669 volume_update_mode: choice(&mut r, "m_VolumeFrameworkUpdateModeOption", &first(3))?,
670 render_post_processing: flag(&mut r)?,
671 antialiasing: choice(&mut r, "m_Antialiasing", &first(4))?,
672 antialiasing_quality: choice(&mut r, "m_AntialiasingQuality", &first(3))?,
673 stop_nan: flag(&mut r)?,
674 dithering: flag(&mut r)?,
675 clear_depth: flag(&mut r)?,
676 allow_xr_rendering: flag(&mut r)?,
677 allow_hdr_output: flag(&mut r)?,
678 use_screen_coord_override: flag(&mut r)?,
679 screen_size_override: vec4(&mut r)?,
680 screen_coord_scale_bias: vec4(&mut r)?,
681 requires_depth_texture: flag(&mut r)?,
682 requires_color_texture: flag(&mut r)?,
683 taa: UnityTaaSettings {
684 quality: choice(&mut r, "TemporalAA.Settings.m_Quality", &first(5))?,
685 frame_influence: r.f32()?,
686 jitter_scale: r.f32()?,
687 mip_bias: r.f32()?,
688 variance_clamp_scale: r.f32()?,
689 contrast_adaptive_sharpening: r.f32()?,
690 },
691 };
692 let version = r.i32()?;
693 if version != CAMERA_VERSION {
694 return Err(invalid(format!(
695 "{WHAT} m_Version {version}: only version {CAMERA_VERSION} is verified"
696 )));
697 }
698 used_up(&r, WHAT)?;
699 Ok(camera)
700}
701
702#[cfg(test)]
703mod tests {
704 use super::*;
705
706 fn asset(cascades: i32, default_renderer: i32) -> Vec<u8> {
708 let mut b: Vec<u8> = Vec::new();
709 let int = |b: &mut Vec<u8>, v: i32| b.extend(v.to_le_bytes());
710 let float = |b: &mut Vec<u8>, v: f32| b.extend(v.to_le_bytes());
711 let bool4 = |b: &mut Vec<u8>, v: bool| b.extend(i32::from(v).to_le_bytes());
712 int(&mut b, 13); int(&mut b, 13);
714 int(&mut b, 1); b.extend([0; 12]); int(&mut b, 1); int(&mut b, 0);
718 b.extend(6168i64.to_le_bytes());
719 int(&mut b, default_renderer);
720 bool4(&mut b, true); bool4(&mut b, false); int(&mut b, 1);
723 bool4(&mut b, false); bool4(&mut b, true); int(&mut b, 0);
726 int(&mut b, 4); float(&mut b, 1.0);
728 int(&mut b, 0); bool4(&mut b, false);
730 float(&mut b, 0.92);
731 bool4(&mut b, true); int(&mut b, 1);
733 int(&mut b, 2); int(&mut b, 0); int(&mut b, 1024);
736 int(&mut b, 256);
737 for _ in 0..4 {
738 bool4(&mut b, false);
739 }
740 int(&mut b, 1); int(&mut b, 1); bool4(&mut b, true); int(&mut b, 4096);
744 int(&mut b, 1); int(&mut b, 8);
746 bool4(&mut b, true); for v in [1024, 256, 512, 1024] {
748 int(&mut b, v);
749 }
750 bool4(&mut b, true); bool4(&mut b, false); bool4(&mut b, true); float(&mut b, 400.0);
754 int(&mut b, cascades);
755 float(&mut b, 0.25);
756 for v in [0.1, 0.3, 0.123, 0.29, 0.54, 0.11, 0.1, 0.5] {
757 float(&mut b, v);
758 }
759 bool4(&mut b, true); bool4(&mut b, true); int(&mut b, 64);
762 int(&mut b, 3);
763 int(&mut b, 2048);
764 int(&mut b, 3);
765 bool4(&mut b, true);
766 bool4(&mut b, false);
767 bool4(&mut b, true); bool4(&mut b, false); bool4(&mut b, true); int(&mut b, 0);
772 int(&mut b, 0);
773 bool4(&mut b, true);
774 int(&mut b, 0);
775 int(&mut b, 32);
776 for _ in 0..4 {
777 bool4(&mut b, true);
778 }
779 int(&mut b, 0);
780 float(&mut b, 0.0);
781 bool4(&mut b, false);
782 int(&mut b, 1);
783 bool4(&mut b, false);
784 for v in [256, 0, 256, 2] {
785 int(&mut b, v);
786 }
787 b.extend([0; 12]); int(&mut b, 1);
790 int(&mut b, 5);
791 b.extend(b"scene\0\0\0");
792 int(&mut b, 1);
793 b.extend([0; 24]);
794 int(&mut b, 1);
795 int(&mut b, 5);
796 b.extend(b"scene\0\0\0");
797 int(&mut b, 1);
798 b.extend([1, 0, 0, 0]);
799 int(&mut b, 0); int(&mut b, 1); b.extend([0; 24]); b
803 }
804
805 #[test]
808 fn a_urp_assets_settings_are_read_by_the_verified_layout() {
809 let read = |b: &[u8]| read_urp_settings(b, UrpAssetLayout::Urp17_3);
810 let s = read(&asset(4, 0)).unwrap();
811 let renderer = PPtr {
812 file_id: 0,
813 path_id: 6168,
814 };
815 assert_eq!((s.renderers.as_slice(), s.default_renderer), (&[renderer][..], 0));
816 assert!(s.require_depth_texture && !s.require_opaque_texture);
817 assert_eq!(s.msaa, 4);
818 assert_eq!((s.shadow_cascades, s.shadow_distance), (4, 400.0));
819 assert!(s.main_light_shadows && s.soft_shadows && s.additional_light_shadows);
820 assert!(
821 s.reflection_probe_blending && !s.reflection_probe_box_projection && s.reflection_probe_atlas
822 );
823 assert!(s.mixed_lighting && !s.light_cookies && s.light_layers && s.lod_cross_fade);
824 assert_eq!(
825 (s.main_light_mode, s.additional_lights_mode, s.sh_eval_mode),
826 (1, 1, 2)
827 );
828 let mut other = asset(4, 0);
830 other[0] = 12;
831 assert!(read(&other).is_err());
832 assert!(read(&asset(7, 0)).is_err());
833 let past = read(&asset(4, 1)).unwrap_err().to_string();
834 assert!(past.contains("m_DefaultRendererIndex"), "{past}");
835 let whole = asset(4, 0);
837 assert!(read(&whole[..whole.len() - 4]).is_err());
838 let mut longer = whole.clone();
839 longer.extend([0; 4]);
840 let longer = read(&longer).unwrap_err().to_string();
841 assert!(longer.contains("past the verified layout"), "{longer}");
842 }
843
844 #[test]
847 fn a_urp_assets_layout_is_identified_by_the_players_unity_version() {
848 let of = |v: &str| UrpAssetLayout::of(UnityVersion::parse(v).unwrap());
849 assert_eq!(of("6000.3.12f1").unwrap(), UrpAssetLayout::Urp17_3);
850 assert_eq!(of("6000.3.15f1").unwrap(), UrpAssetLayout::Urp17_3);
851 let refused = of("6000.3.17f1").unwrap_err().to_string();
852 assert!(refused.contains("Unity 6000.3.17f1 player"), "{refused}");
853 assert!(of("6000.6.4f1").is_err());
854 }
855
856 fn int(b: &mut Vec<u8>, v: i32) {
857 b.extend(v.to_le_bytes());
858 }
859
860 fn reference(b: &mut Vec<u8>, path_id: i64) {
861 int(b, 0);
862 b.extend(path_id.to_le_bytes());
863 }
864
865 fn renderer(version: i32, mode: i32) -> Vec<u8> {
867 let mut b = vec![0; 12 * 9]; int(&mut b, 2);
869 reference(&mut b, 6001);
870 reference(&mut b, 6002);
871 int(&mut b, 2);
872 b.extend(7i64.to_le_bytes());
873 b.extend((-8i64).to_le_bytes());
874 int(&mut b, 1); reference(&mut b, 0); reference(&mut b, 6170); int(&mut b, version);
878 for mask in [0x8a30_9437u32, 0x8a31_d437, u32::MAX] {
879 b.extend(mask.to_le_bytes());
880 }
881 for v in [1, 3, 8, 2, 0, 0] {
882 int(&mut b, v); }
884 int(&mut b, 1); int(&mut b, mode);
886 int(&mut b, 0); int(&mut b, 1); int(&mut b, 92);
889 int(&mut b, 0);
890 int(&mut b, 0);
891 int(&mut b, 1); b
893 }
894
895 #[test]
899 fn a_urp_renderer_is_read_by_the_verified_layout() {
900 let r = read_urp_renderer(&renderer(3, 2)).unwrap();
901 let ids: Vec<i64> = r.features.iter().map(|f| f.path_id).collect();
902 assert_eq!((ids, r.feature_map.clone()), (vec![6001, 6002], vec![7, -8]));
903 assert!(r.native_render_pass && r.shadow_transparent_receive && !r.accurate_gbuffer_normals);
904 assert_eq!((r.xr_system_data.path_id, r.post_process_data.path_id), (0, 6170));
905 assert_eq!(
906 (
907 r.prepass_layer_mask,
908 r.opaque_layer_mask,
909 r.transparent_layer_mask
910 ),
911 (0x8a30_9437, 0x8a31_d437, u32::MAX)
912 );
913 assert_eq!(
914 r.stencil,
915 UnityStencilState {
916 override_stencil: true,
917 reference: 3,
918 compare: 8,
919 pass: 2,
920 fail: 0,
921 z_fail: 0
922 }
923 );
924 assert_eq!(
925 (r.rendering_mode, r.depth_priming_mode, r.copy_depth_mode),
926 (2, 0, 1)
927 );
928 assert_eq!(
929 (
930 r.depth_attachment_format,
931 r.depth_texture_format,
932 r.intermediate_texture_mode
933 ),
934 (92, 0, 1)
935 );
936 let refusal = |b: &[u8]| read_urp_renderer(b).unwrap_err().to_string();
937 assert!(refusal(&renderer(2, 2)).contains("asset version 2"));
938 assert!(refusal(&renderer(3, 4)).contains("m_RenderingMode 4"));
939 let mut longer = renderer(3, 2);
940 longer.extend([0; 4]);
941 assert!(refusal(&longer).contains("100 bytes after m_UseNativeRenderPass"));
942 let mut no_xr = renderer(3, 2);
944 no_xr.drain(160..172);
945 assert!(refusal(&no_xr).contains("84 bytes after"), "{}", refusal(&no_xr));
946 let mut map = renderer(3, 2);
948 map[136] = 1; map.drain(148..156);
950 let short = read_urp_renderer(&map).unwrap();
951 assert_eq!((short.features.len(), short.feature_map), (2, vec![7]));
952 assert_eq!(short.post_process_data.path_id, 6170);
953 assert!(read_urp_renderer(&renderer(3, 2)[..150]).is_err());
954 }
955
956 #[test]
959 fn urp_features_are_read_by_the_verified_layout() {
960 assert!(read_urp_feature_active(&[1, 0, 0, 0, 9, 9]).unwrap());
961 assert!(!read_urp_feature_active(&[0, 0, 0, 0]).unwrap());
962 assert!(read_urp_feature_active(&[2, 0, 0, 0]).is_err());
963 let decal = |technique: i32| {
964 let mut b = Vec::new();
965 int(&mut b, 1);
966 int(&mut b, technique);
967 b.extend(500f32.to_le_bytes());
968 int(&mut b, 0);
969 int(&mut b, 2);
970 int(&mut b, 0);
971 b
972 };
973 assert_eq!(
974 read_urp_decal_feature(&decal(1)).unwrap(),
975 UnityUrpDecalFeature {
976 active: true,
977 technique: 1,
978 max_draw_distance: 500.0,
979 decal_layers: false,
980 dbuffer_surface_data: 2,
981 screen_space_normal_blend: 0
982 }
983 );
984 assert!(read_urp_decal_feature(&decal(3)).is_err());
985 let mut longer = decal(1);
986 longer.extend([0; 4]);
987 assert!(read_urp_decal_feature(&longer).is_err());
988 let full_screen = |injection: i32, version: i32| {
989 let mut b = Vec::new();
990 int(&mut b, 1);
991 int(&mut b, injection);
992 int(&mut b, 1);
993 int(&mut b, -1); reference(&mut b, 7);
995 int(&mut b, 0);
996 int(&mut b, 0);
997 int(&mut b, version);
998 b
999 };
1000 let f = read_urp_full_screen_feature(&full_screen(600, 1)).unwrap();
1001 assert!(f.active && f.fetch_color_buffer && !f.bind_depth_stencil_attachment);
1002 assert_eq!(
1003 (
1004 f.injection_point,
1005 f.requirements,
1006 f.pass_material.path_id,
1007 f.pass_index
1008 ),
1009 (600, -1, 7, 0)
1010 );
1011 let refusal = |b: &[u8]| read_urp_full_screen_feature(b).unwrap_err().to_string();
1012 assert!(refusal(&full_screen(300, 1)).contains("injectionPoint 300"));
1013 assert!(refusal(&full_screen(600, 0)).contains("m_Version 0"));
1014 }
1015
1016 fn camera(stack: i32, version: i32) -> Vec<u8> {
1018 let mut b = Vec::new();
1019 int(&mut b, 1); int(&mut b, 2);
1021 int(&mut b, 2);
1022 int(&mut b, 0); int(&mut b, stack);
1024 for i in 0..stack {
1025 reference(&mut b, i64::from(i) + 100);
1026 }
1027 int(&mut b, -1); b.extend(0x8000_0000u32.to_le_bytes());
1029 reference(&mut b, 0);
1030 int(&mut b, 2);
1031 int(&mut b, 1); int(&mut b, 3); int(&mut b, 2);
1034 for v in [0, 1, 1, 1, 1, 0] {
1035 int(&mut b, v);
1036 }
1037 for v in [0f32, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0] {
1038 b.extend(v.to_le_bytes());
1039 }
1040 int(&mut b, 0);
1041 int(&mut b, 0);
1042 int(&mut b, 3); for v in [0.1f32, 1.0, 0.0, 0.9, 0.0] {
1044 b.extend(v.to_le_bytes());
1045 }
1046 int(&mut b, version);
1047 b
1048 }
1049
1050 #[test]
1053 fn a_urp_camera_is_read_by_the_verified_layout() {
1054 assert_eq!(camera(0, 2).len(), 148);
1055 let c = read_urp_camera(&camera(2, 2)).unwrap();
1056 let stack: Vec<i64> = c.stack.iter().map(|p| p.path_id).collect();
1057 assert_eq!(stack, vec![100, 101]);
1058 assert_eq!(
1059 (
1060 c.renderer_index,
1061 c.depth_texture_option,
1062 c.antialiasing,
1063 c.taa.quality
1064 ),
1065 (-1, 2, 3, 3)
1066 );
1067 assert!(c.render_shadows && c.render_post_processing && c.clear_depth && !c.stop_nan && c.dithering);
1068 assert_eq!(
1069 (c.volume_layer_mask, c.screen_coord_scale_bias),
1070 (0x8000_0000, [1.0, 1.0, 0.0, 0.0])
1071 );
1072 assert_eq!(c.taa.variance_clamp_scale, 0.9);
1073 assert!(
1074 read_urp_camera(&camera(0, 1))
1075 .unwrap_err()
1076 .to_string()
1077 .contains("m_Version 1")
1078 );
1079 let mut longer = camera(0, 2);
1080 longer.extend([0; 4]);
1081 assert!(read_urp_camera(&longer).is_err());
1082 }
1083}