1use std::io::{Cursor, Read, Seek, SeekFrom};
24
25use crate::binary::{
26 check_slice_in_bounds, checked_to_usize, read_f32, read_i32, read_u16, read_u32, read_u8,
27};
28
29use super::controllers::{MdlController, MdlControllerType, MdlKey};
30use super::types::{
31 AabbNode, MdlAabb, MdlAnimMesh, MdlCamera, MdlDangly, MdlLight, MdlMesh, MdlNodeData,
32 MdlReference, MdlSaber, MdlSkin,
33};
34use super::{
35 aabb_offsets, anim_header_offsets, anim_mesh_offsets, dangly_offsets, header_offsets,
36 light_offsets, mesh_offsets, node_flags, node_offsets, saber_offsets, skin_offsets, Mdl,
37 MdlAnimEvent, MdlAnimNode, MdlAnimation, MdlError, MdlNode, AABB_EXTRA_SIZE,
38 ANIMATION_EVENT_SIZE, ANIMATION_HEADER_SIZE, ANIM_MESH_EXTRA_SIZE, DANGLY_EXTRA_SIZE,
39 EMITTER_EXTRA_SIZE, LIGHT_EXTRA_SIZE, MAX_FACE_SIZE, MDL_WRAPPER_SIZE, MESH_EXTRA_SIZE,
40 NODE_HEADER_SIZE, NUM_SABER_VERTS, REFERENCE_EXTRA_SIZE, SABER_EXTRA_SIZE, SKIN_EXTRA_SIZE,
41};
42
43#[cfg_attr(
47 feature = "tracing",
48 tracing::instrument(level = "debug", skip(reader, mdx_bytes))
49)]
50pub fn read_mdl<R: Read>(reader: &mut R, mdx_bytes: Option<&[u8]>) -> Result<Mdl, MdlError> {
51 let mut buffer = Vec::new();
52 reader.read_to_end(&mut buffer)?;
53 crate::trace_debug!(bytes_len = buffer.len(), "read mdl bytes from reader");
54 read_mdl_from_bytes(&buffer, mdx_bytes)
55}
56
57#[cfg_attr(
63 feature = "tracing",
64 tracing::instrument(level = "debug", skip(bytes, mdx_bytes), fields(bytes_len = bytes.len()))
65)]
66pub fn read_mdl_from_bytes(bytes: &[u8], mdx_bytes: Option<&[u8]>) -> Result<Mdl, MdlError> {
67 read_mdl_from_cursor(&mut Cursor::new(bytes), mdx_bytes)
68}
69
70struct MdxMeshInfo {
80 stride: u32,
82 vertex_count: usize,
84 mdx_data_offset: usize,
88 vert_array_offset: usize,
92 pos_off: i32,
94 norm_off: i32,
95 color_off: i32,
96 uv1_off: i32,
97 uv2_off: i32,
98 uv3_off: i32,
99 uv4_off: i32,
100 tangent_off: i32,
101 bone_weights_off: i32,
103 bone_indices_off: i32,
104}
105
106#[derive(Default)]
108struct MdxVertexData {
109 positions: Vec<[f32; 3]>,
110 normals: Vec<[f32; 3]>,
111 vertex_colors: Vec<[u8; 4]>,
112 uv1: Vec<[f32; 2]>,
113 uv2: Vec<[f32; 2]>,
114 uv3: Vec<[f32; 2]>,
115 uv4: Vec<[f32; 2]>,
116 tangent_space: Vec<[[f32; 3]; 3]>,
117 bone_weights: Vec<[f32; 4]>,
118 bone_indices: Vec<[f32; 4]>,
119 clamped_vertex_count: Option<u16>,
121}
122
123fn read_mdl_from_cursor<R: Read + Seek>(
132 reader: &mut R,
133 mdx_bytes: Option<&[u8]>,
134) -> Result<Mdl, MdlError> {
135 let len = reader.seek(SeekFrom::End(0))?;
137 if len < MDL_WRAPPER_SIZE {
138 return Err(crate::binary::BinaryLayoutError::UnexpectedEof("preamble").into());
139 }
140 reader.seek(SeekFrom::Start(MDL_WRAPPER_SIZE))?;
141
142 let mut content = Vec::new();
144 reader.read_to_end(&mut content)?;
145 let bytes = &content;
146
147 if bytes.len() < 196 {
149 return Err(crate::binary::BinaryLayoutError::UnexpectedEof("header").into());
150 }
151
152 let geometry_fn_ptr1 = read_u32(bytes, header_offsets::FN_PTR1)?;
154 let geometry_fn_ptr2 = read_u32(bytes, header_offsets::FN_PTR2)?;
155 let root_node_offset = checked_to_usize(
156 read_u32(bytes, header_offsets::ROOT_NODE_PTR)?,
157 "root_offset",
158 )?;
159 let node_count = read_u32(bytes, header_offsets::NODE_COUNT)?;
160 let model_type = read_u8(bytes, header_offsets::MODEL_TYPE)?;
161
162 let classification = read_u8(bytes, header_offsets::CLASSIFICATION)?;
164 let subclassification = read_u8(bytes, header_offsets::SUBCLASSIFICATION)?;
165 let affected_by_fog = read_u8(bytes, header_offsets::AFFECTED_BY_FOG)?;
166
167 let mut bounding_box = [0.0f32; 6];
169 for (i, val) in bounding_box.iter_mut().enumerate() {
170 *val = read_f32(bytes, header_offsets::BOUNDING_BOX_MIN + i * 4)?;
171 }
172 let radius = read_f32(bytes, header_offsets::RADIUS)?;
173 let animation_scale = read_f32(bytes, header_offsets::ANIMATION_SCALE)?;
174
175 let supermodel_name = read_fixed_string(
177 bytes,
178 header_offsets::SUPERMODEL_NAME,
179 header_offsets::SUPERMODEL_NAME_SIZE,
180 );
181
182 let name_offsets_ptr = checked_to_usize(
183 read_u32(bytes, header_offsets::NAME_OFFSETS_PTR)?,
184 "name_offsets",
185 )?;
186 let name_count = read_u32(bytes, header_offsets::NAME_COUNT)?;
187
188 let mut names = Vec::new();
190 if name_offsets_ptr > 0 && name_offsets_ptr < bytes.len() {
191 let name_count = checked_to_usize(name_count, "name_count")?;
192 for i in 0..name_count {
193 let ptr_offset = name_offsets_ptr + (i * 4);
194 if ptr_offset + 4 > bytes.len() {
195 crate::trace_warn!(
196 index = i,
197 ptr_offset,
198 bytes_len = bytes.len(),
199 "name offset array truncated"
200 );
201 break;
202 }
203
204 let name_offset = checked_to_usize(read_u32(bytes, ptr_offset)?, "name_ptr")?;
205
206 if name_offset < bytes.len() {
207 names.push(crate::binary::read_c_string(bytes, name_offset));
208 } else {
209 crate::trace_warn!(
210 index = i,
211 name_offset,
212 bytes_len = bytes.len(),
213 "name string offset out of bounds, using synthetic name"
214 );
215 names.push(format!("Node_{}", i));
216 }
217 }
218 }
219
220 let mut mdx_infos: Vec<MdxMeshInfo> = Vec::new();
224 let mut dfs_mesh_counter: usize = 0;
225 let mut root = read_node(
226 bytes,
227 root_node_offset,
228 &names,
229 None,
230 None,
231 &mut mdx_infos,
232 &mut dfs_mesh_counter,
233 )?;
234
235 if let Some(mdx) = mdx_bytes {
252 populate_mdx_data(&mut root, mdx, &mdx_infos)?;
253 populate_content_positions_fallback(&mut root, bytes, &mdx_infos)?;
258 } else {
259 populate_content_positions(&mut root, bytes, &mdx_infos)?;
260 }
261
262 let anim_arr_ptr = read_u32(bytes, header_offsets::ANIMATION_ARR_PTR)?;
264 let anim_arr_count = read_u32(bytes, header_offsets::ANIMATION_ARR_COUNT)?;
265 let animations = read_animations(bytes, anim_arr_ptr, anim_arr_count, &names)?;
266
267 let off_anim_root_raw = read_u32(bytes, header_offsets::OFF_ANIM_ROOT)?;
270 let anim_root_node = if off_anim_root_raw
271 != u32::try_from(root_node_offset)
272 .map_err(|_| MdlError::ValueOverflow("root_node_offset"))?
273 && off_anim_root_raw > 0
274 {
275 let ar_offset = checked_to_usize(off_anim_root_raw, "off_anim_root")?;
276 if ar_offset + 6 <= bytes.len() {
277 let name_idx = usize::from(read_u16(bytes, ar_offset + node_offsets::NODE_ID)?);
278 if name_idx < names.len() {
279 Some(names[name_idx].clone())
280 } else {
281 None
282 }
283 } else {
284 None
285 }
286 } else {
287 None
288 };
289
290 Ok(Mdl {
291 root_node: root,
292 geometry_fn_ptr1,
293 geometry_fn_ptr2,
294 model_type,
295 classification,
296 subclassification,
297 affected_by_fog,
298 supermodel_name,
299 node_count,
300 bounding_box,
301 radius,
302 animation_scale,
303 animations,
304 anim_root_node,
305 })
306}
307
308struct ControllerReadResult {
321 controllers: Vec<MdlController>,
322 orphan_data: Vec<f32>,
323}
324
325fn read_controllers(
326 bytes: &[u8],
327 key_ptr: usize,
328 key_count: usize,
329 data_ptr: usize,
330 data_count: usize,
331) -> Result<ControllerReadResult, MdlError> {
332 let mut controller_data = Vec::with_capacity(data_count);
334 if data_count > 0 && data_ptr > 0 {
335 check_slice_in_bounds(bytes, data_ptr, data_count * 4, "controller_data")?;
336 for i in 0..data_count {
337 let val = read_f32(bytes, data_ptr + (i * 4))?;
338 controller_data.push(val);
339 }
340 }
341
342 let mut controllers = Vec::with_capacity(key_count);
343 if key_count > 0 && key_ptr > 0 {
344 for i in 0..key_count {
345 let key_offset = key_ptr + (i * 16);
346 if key_offset + 16 > bytes.len() {
347 crate::trace_warn!(
348 index = i,
349 key_offset,
350 bytes_len = bytes.len(),
351 "controller key header truncated"
352 );
353 break;
354 }
355
356 let type_code = read_u32(bytes, key_offset)?;
357 let controller_type = MdlControllerType::from(type_code);
358
359 let mut key_unknown_04 = [0u8; 2];
361 key_unknown_04.copy_from_slice(&bytes[key_offset + 4..key_offset + 6]);
362
363 let row_count = usize::from(read_u16(bytes, key_offset + 6)?);
364 let time_index = usize::from(read_u16(bytes, key_offset + 8)?);
365 let data_index = usize::from(read_u16(bytes, key_offset + 10)?);
366 let raw_column_count = read_u8(bytes, key_offset + 12)?;
367
368 let mut key_unknown_0d = [0u8; 3];
369 key_unknown_0d.copy_from_slice(&bytes[key_offset + 13..key_offset + 16]);
370
371 let actual_columns =
381 if controller_type == MdlControllerType::ORIENTATION && raw_column_count == 2 {
382 1
385 } else {
386 let base = usize::from(raw_column_count & 0x0F);
387 let has_bezier = (raw_column_count & super::controllers::CTRL_FLAG_BEZIER) != 0;
388 if has_bezier {
389 base * 3
390 } else {
391 base
392 }
393 };
394
395 let mut keys = Vec::with_capacity(row_count);
397
398 if time_index + row_count <= controller_data.len() {
399 for r in 0..row_count {
400 let time = controller_data[time_index + r];
401 let mut values = Vec::with_capacity(actual_columns);
402
403 let data_start = data_index + (r * actual_columns);
404 if data_start + actual_columns <= controller_data.len() {
405 for c in 0..actual_columns {
406 values.push(controller_data[data_start + c]);
407 }
408 } else {
409 crate::trace_warn!(
410 controller = ?controller_type,
411 row = r,
412 data_start,
413 actual_columns,
414 raw_column_count,
415 data_len = controller_data.len(),
416 "controller keyframe data out of bounds"
417 );
418 }
419 keys.push(MdlKey { time, values });
420 }
421 } else {
422 crate::trace_warn!(
423 controller = ?controller_type,
424 time_index,
425 row_count,
426 data_len = controller_data.len(),
427 "controller time indices out of bounds"
428 );
429 }
430
431 controllers.push(MdlController {
432 controller_type,
433 keys,
434 raw_column_count,
435 key_unknown_04,
436 key_unknown_0d,
437 });
438 }
439 }
440
441 let orphan_data = if controllers.is_empty() && !controller_data.is_empty() {
443 controller_data
444 } else {
445 Vec::new()
446 };
447
448 Ok(ControllerReadResult {
449 controllers,
450 orphan_data,
451 })
452}
453
454fn read_animations(
460 bytes: &[u8],
461 anim_arr_ptr: u32,
462 anim_arr_count: u32,
463 names: &[String],
464) -> Result<Vec<MdlAnimation>, MdlError> {
465 if anim_arr_count == 0 {
466 return Ok(Vec::new());
467 }
468
469 let arr_offset = checked_to_usize(anim_arr_ptr, "anim_arr_ptr")?;
470 let anim_count = checked_to_usize(anim_arr_count, "anim_count")?;
471 if arr_offset + (anim_count * 4) > bytes.len() {
472 return Err(MdlError::InvalidData(format!(
473 "animation offset array at {arr_offset} with {anim_arr_count} entries exceeds content"
474 )));
475 }
476
477 let mut anim_offsets = Vec::with_capacity(anim_count);
479 for i in 0..anim_count {
480 let off = checked_to_usize(read_u32(bytes, arr_offset + i * 4)?, "anim_header_offset")?;
481 anim_offsets.push(off);
482 }
483
484 let mut animations = Vec::with_capacity(anim_offsets.len());
485 for &offset in &anim_offsets {
486 animations.push(read_animation(bytes, offset, names)?);
487 }
488 Ok(animations)
489}
490
491fn read_animation(bytes: &[u8], offset: usize, names: &[String]) -> Result<MdlAnimation, MdlError> {
493 check_slice_in_bounds(bytes, offset, ANIMATION_HEADER_SIZE, "animation_header")?;
494
495 let fn_ptr1 = read_u32(bytes, offset + anim_header_offsets::FN_PTR1)?;
496 let fn_ptr2 = read_u32(bytes, offset + anim_header_offsets::FN_PTR2)?;
497
498 let name = read_fixed_string(
500 bytes,
501 offset + anim_header_offsets::NAME,
502 anim_header_offsets::NAME_SIZE,
503 );
504
505 let root_node_ptr = checked_to_usize(
506 read_u32(bytes, offset + anim_header_offsets::ROOT_NODE_PTR)?,
507 "anim_root_node_ptr",
508 )?;
509
510 let length = read_f32(bytes, offset + anim_header_offsets::LENGTH)?;
512 let transition_time = read_f32(bytes, offset + anim_header_offsets::TRANSITION)?;
513
514 let anim_root = read_fixed_string(
516 bytes,
517 offset + anim_header_offsets::ANIM_ROOT,
518 anim_header_offsets::ANIM_ROOT_SIZE,
519 );
520
521 let event_arr_ptr = read_u32(bytes, offset + anim_header_offsets::EVENT_ARR_PTR)?;
523 let event_arr_count = read_u32(bytes, offset + anim_header_offsets::EVENT_ARR_COUNT)?;
524
525 let events = read_anim_events(bytes, event_arr_ptr, event_arr_count)?;
526
527 let root_node = read_anim_node(bytes, root_node_ptr, names)?;
529
530 Ok(MdlAnimation {
531 name,
532 length,
533 transition_time,
534 anim_root,
535 events,
536 root_node,
537 fn_ptr1,
538 fn_ptr2,
539 })
540}
541
542fn read_anim_events(
544 bytes: &[u8],
545 event_arr_ptr: u32,
546 event_arr_count: u32,
547) -> Result<Vec<MdlAnimEvent>, MdlError> {
548 if event_arr_count == 0 {
549 return Ok(Vec::new());
550 }
551
552 let arr_offset = checked_to_usize(event_arr_ptr, "event_arr_ptr")?;
553 let event_count = checked_to_usize(event_arr_count, "event_count")?;
554 let total_size = event_count * ANIMATION_EVENT_SIZE;
555 if arr_offset + total_size > bytes.len() {
556 return Err(MdlError::InvalidData(format!(
557 "event array at {arr_offset} with {event_arr_count} events exceeds content"
558 )));
559 }
560
561 let mut events = Vec::with_capacity(event_count);
562 for i in 0..event_count {
563 let event_offset = arr_offset + i * ANIMATION_EVENT_SIZE;
564 let time = read_f32(bytes, event_offset)?;
565 let name = read_fixed_string(bytes, event_offset + 4, 32);
566 events.push(MdlAnimEvent { time, name });
567 }
568 Ok(events)
569}
570
571fn read_anim_node(bytes: &[u8], offset: usize, names: &[String]) -> Result<MdlAnimNode, MdlError> {
577 check_slice_in_bounds(bytes, offset, NODE_HEADER_SIZE, "anim_node_header")?;
578
579 let node_number = read_u16(bytes, offset + 0x02)?;
581
582 let name_index = usize::from(read_u16(bytes, offset + node_offsets::NODE_ID)?);
584 let name = if name_index < names.len() {
585 names[name_index].clone()
586 } else {
587 format!("AnimNode_{}", name_index)
588 };
589
590 let child_offset_ptr = checked_to_usize(
592 read_u32(bytes, offset + node_offsets::CHILD_ARRAY_PTR)?,
593 "anim_child_offset_ptr",
594 )?;
595 let child_count = checked_to_usize(
596 read_u32(bytes, offset + node_offsets::CHILD_COUNT)?,
597 "anim_child_count",
598 )?;
599
600 let ctrl_key_ptr = checked_to_usize(
602 read_u32(bytes, offset + node_offsets::CONTROLLER_KEY_PTR)?,
603 "anim_ctrl_key_ptr",
604 )?;
605 let ctrl_key_count = checked_to_usize(
606 read_u32(bytes, offset + node_offsets::CONTROLLER_KEY_COUNT)?,
607 "anim_ctrl_key_count",
608 )?;
609 let ctrl_data_ptr = checked_to_usize(
610 read_u32(bytes, offset + node_offsets::CONTROLLER_DATA_PTR)?,
611 "anim_ctrl_data_ptr",
612 )?;
613 let ctrl_data_count = checked_to_usize(
614 read_u32(bytes, offset + node_offsets::CONTROLLER_DATA_COUNT)?,
615 "anim_ctrl_data_count",
616 )?;
617
618 let ctrl_result = read_controllers(
620 bytes,
621 ctrl_key_ptr,
622 ctrl_key_count,
623 ctrl_data_ptr,
624 ctrl_data_count,
625 )?;
626
627 let mut children = Vec::with_capacity(child_count);
629 if child_count > 0 && child_offset_ptr > 0 && child_offset_ptr + child_count * 4 <= bytes.len()
630 {
631 for i in 0..child_count {
632 let child_ptr =
633 checked_to_usize(read_u32(bytes, child_offset_ptr + i * 4)?, "anim_child_ptr")?;
634 children.push(read_anim_node(bytes, child_ptr, names)?);
635 }
636 }
637
638 Ok(MdlAnimNode {
639 name,
640 node_number,
641 controllers: ctrl_result.controllers,
642 orphan_controller_data: ctrl_result.orphan_data,
643 children,
644 })
645}
646
647fn read_node(
652 bytes: &[u8],
653 offset: usize,
654 names: &[String],
655 parent_node_id: Option<u16>,
656 node_end_hint: Option<usize>,
657 mdx_infos: &mut Vec<MdxMeshInfo>,
658 dfs_mesh_counter: &mut usize,
659) -> Result<MdlNode, MdlError> {
660 check_slice_in_bounds(bytes, offset, NODE_HEADER_SIZE, "node_header")?;
662
663 let flags = u32::from(read_u16(bytes, offset + node_offsets::FLAGS)?);
664
665 let mut header_padding_02 = [0u8; 2];
667 header_padding_02.copy_from_slice(&bytes[offset + 0x02..offset + 0x04]);
668
669 let node_id = usize::from(read_u16(bytes, offset + node_offsets::NODE_ID)?);
670
671 let mut header_padding_06 = [0u8; 2];
672 header_padding_06.copy_from_slice(&bytes[offset + 0x06..offset + 0x08]);
673
674 let px = read_f32(bytes, offset + node_offsets::POS_X)?;
675 let py = read_f32(bytes, offset + node_offsets::POS_X + 4)?;
676 let pz = read_f32(bytes, offset + node_offsets::POS_X + 8)?;
677
678 let rw = read_f32(bytes, offset + node_offsets::ORIENTATION_W)?;
680 let rx = read_f32(bytes, offset + node_offsets::ORIENTATION_W + 4)?;
681 let ry = read_f32(bytes, offset + node_offsets::ORIENTATION_W + 8)?;
682 let rz = read_f32(bytes, offset + node_offsets::ORIENTATION_W + 12)?;
683
684 let child_offset_ptr = checked_to_usize(
685 read_u32(bytes, offset + node_offsets::CHILD_ARRAY_PTR)?,
686 "child_offset",
687 )?;
688 let child_count = checked_to_usize(
689 read_u32(bytes, offset + node_offsets::CHILD_COUNT)?,
690 "child_count",
691 )?;
692
693 let controller_key_ptr = checked_to_usize(
694 read_u32(bytes, offset + node_offsets::CONTROLLER_KEY_PTR)?,
695 "controller_key_ptr",
696 )?;
697 let controller_key_count = checked_to_usize(
698 read_u32(bytes, offset + node_offsets::CONTROLLER_KEY_COUNT)?,
699 "controller_key_count",
700 )?;
701
702 let controller_data_ptr = checked_to_usize(
703 read_u32(bytes, offset + node_offsets::CONTROLLER_DATA_PTR)?,
704 "controller_data_ptr",
705 )?;
706 let controller_data_count = checked_to_usize(
707 read_u32(bytes, offset + node_offsets::CONTROLLER_DATA_COUNT)?,
708 "controller_data_count",
709 )?;
710
711 let name = if node_id < names.len() {
712 names[node_id].clone()
713 } else {
714 format!("Node_{}", node_id)
715 };
716
717 let ctrl_result = read_controllers(
719 bytes,
720 controller_key_ptr,
721 controller_key_count,
722 controller_data_ptr,
723 controller_data_count,
724 )?;
725
726 let mut current_offset = offset + NODE_HEADER_SIZE; if (flags & node_flags::LIGHT) != 0 {
732 current_offset += LIGHT_EXTRA_SIZE;
733 }
734 if (flags & node_flags::EMITTER) != 0 {
735 current_offset += EMITTER_EXTRA_SIZE;
736 }
737 if (flags & node_flags::REFERENCE) != 0 {
741 current_offset += REFERENCE_EXTRA_SIZE;
742 }
743
744 let node_data = if (flags & node_flags::MESH) != 0 {
748 *dfs_mesh_counter += 1;
749
750 let mut node_data_end_hint = node_end_hint.unwrap_or(bytes.len());
751 for ptr in [child_offset_ptr, controller_key_ptr, controller_data_ptr] {
752 if ptr > 0 {
753 node_data_end_hint = node_data_end_hint.min(ptr);
754 }
755 }
756
757 let (mesh, info) = read_mesh_header(bytes, current_offset)?;
758 mdx_infos.push(info);
759
760 if (flags & node_flags::SABER) != 0 {
761 let saber = read_saber_extra(bytes, current_offset + MESH_EXTRA_SIZE, mesh)?;
762 MdlNodeData::Saber(saber)
763 } else if (flags & node_flags::AABB) != 0 {
764 let aabb = read_aabb_extra(
765 bytes,
766 current_offset + MESH_EXTRA_SIZE,
767 node_data_end_hint,
768 mesh,
769 )?;
770 MdlNodeData::Aabb(aabb)
771 } else if (flags & node_flags::DANGLY) != 0 {
772 let dangly = read_dangly_extra(bytes, current_offset + MESH_EXTRA_SIZE, mesh)?;
773 MdlNodeData::Dangly(dangly)
774 } else if (flags & node_flags::ANIM) != 0 {
775 let anim = read_anim_mesh_extra(bytes, current_offset + MESH_EXTRA_SIZE, mesh)?;
776 MdlNodeData::AnimMesh(anim)
777 } else if (flags & node_flags::SKIN) != 0 {
778 let skin = read_skin_extra(bytes, current_offset + MESH_EXTRA_SIZE, mesh)?;
779 if let Some(last_info) = mdx_infos.last_mut() {
782 last_info.bone_weights_off = skin.mdx_bone_weights_offset;
783 last_info.bone_indices_off = skin.mdx_bone_indices_offset;
784 }
785 MdlNodeData::Skin(skin)
786 } else {
787 MdlNodeData::Mesh(mesh)
788 }
789 } else if (flags & node_flags::LIGHT) != 0 {
790 MdlNodeData::Light(read_light_header(bytes, offset + NODE_HEADER_SIZE)?)
791 } else if (flags & node_flags::EMITTER) != 0 {
792 MdlNodeData::Emitter(read_emitter_header(bytes, offset + NODE_HEADER_SIZE)?)
793 } else if (flags & node_flags::CAMERA) != 0 {
794 MdlNodeData::Camera(MdlCamera::new())
795 } else if (flags & node_flags::REFERENCE) != 0 {
796 MdlNodeData::Reference(read_reference_header(bytes, offset + NODE_HEADER_SIZE)?)
797 } else {
798 MdlNodeData::Base
799 };
800
801 let mut children = Vec::with_capacity(child_count);
802 if child_count > 0 && child_offset_ptr > 0 {
803 let mut child_ptrs = Vec::with_capacity(child_count);
804 for i in 0..child_count {
805 let ptr_loc = child_offset_ptr + (i * 4);
806 let child_ptr = checked_to_usize(read_u32(bytes, ptr_loc)?, "child_ptr")?;
807 child_ptrs.push(child_ptr);
808 }
809
810 for (i, child_ptr) in child_ptrs.iter().copied().enumerate() {
811 let mut child_end_hint: Option<usize> = None;
812 let mut consider_end = |candidate: Option<usize>| {
813 if let Some(end) = candidate.filter(|end| *end > child_ptr) {
814 child_end_hint = Some(child_end_hint.map_or(end, |curr| curr.min(end)));
815 }
816 };
817 consider_end(child_ptrs.get(i + 1).copied());
818 consider_end((controller_key_ptr > 0).then_some(controller_key_ptr));
819 consider_end((controller_data_ptr > 0).then_some(controller_data_ptr));
820 consider_end(node_end_hint);
821
822 let child_node = read_node(
823 bytes,
824 child_ptr,
825 names,
826 u16::try_from(node_id).ok(),
827 child_end_hint,
828 mdx_infos,
829 dfs_mesh_counter,
830 )?;
831 children.push(child_node);
832 }
833 }
834
835 Ok(MdlNode {
836 name,
837 parent_index: parent_node_id,
838 children,
839 position: [px, py, pz],
840 rotation: [rw, rx, ry, rz],
841 node_data,
842 controllers: ctrl_result.controllers,
843 orphan_controller_data: ctrl_result.orphan_data,
844 header_padding_02,
845 header_padding_06,
846 })
847}
848
849fn read_mesh_header(bytes: &[u8], offset: usize) -> Result<(MdlMesh, MdxMeshInfo), MdlError> {
854 use super::types::MdlFace;
855
856 check_slice_in_bounds(bytes, offset, MESH_EXTRA_SIZE, "mesh_header")?;
858
859 let face_offset = checked_to_usize(
860 read_u32(bytes, offset + mesh_offsets::FACE_ARRAY_OFFSET)?,
861 "face_offset",
862 )?;
863 let face_count = checked_to_usize(
864 read_u32(bytes, offset + mesh_offsets::FACE_COUNT)?,
865 "face_count",
866 )?;
867
868 let vertex_indices_count_ptr = checked_to_usize(
872 read_u32(bytes, offset + mesh_offsets::VERTEX_INDICES_COUNT_ARRAY_PTR)?,
873 "vertex_indices_count_ptr",
874 )?;
875 let vertex_indices_count_count = read_u32(
876 bytes,
877 offset + mesh_offsets::VERTEX_INDICES_COUNT_ARRAY_COUNT,
878 )?;
879 let _vertex_indices_count_alloc = read_u32(
880 bytes,
881 offset + mesh_offsets::VERTEX_INDICES_COUNT_ARRAY_ALLOC,
882 )?;
883
884 let _mdx_offsets_count = read_u32(bytes, offset + mesh_offsets::MDX_OFFSETS_ARRAY_COUNT)?;
886 let _mdx_offsets_alloc = read_u32(bytes, offset + mesh_offsets::MDX_OFFSETS_ARRAY_ALLOC)?;
887
888 let index_buffer_pools_ptr = checked_to_usize(
889 read_u32(bytes, offset + mesh_offsets::INDEX_BUFFER_POOLS_ARRAY_PTR)?,
890 "index_buffer_pools_ptr",
891 )?;
892 let index_buffer_pools_count =
893 read_u32(bytes, offset + mesh_offsets::INDEX_BUFFER_POOLS_ARRAY_COUNT)?;
894 let _index_buffer_pools_alloc =
895 read_u32(bytes, offset + mesh_offsets::INDEX_BUFFER_POOLS_ARRAY_ALLOC)?;
896
897 let shared_index_offset = read_i32(bytes, offset + mesh_offsets::SHARED_INDEX_OFFSET)?;
898 let shared_index_pool = read_i32(bytes, offset + mesh_offsets::SHARED_INDEX_POOL)?;
899 let shared_index_size = read_i32(bytes, offset + mesh_offsets::SHARED_INDEX_SIZE)?;
900 let indices_per_face = read_u32(bytes, offset + mesh_offsets::INDICES_PER_FACE)?;
901
902 let vertex_count = usize::from(read_u16(bytes, offset + mesh_offsets::VERTEX_COUNT)?);
904 let mdx_data_offset = checked_to_usize(
905 read_u32(bytes, offset + mesh_offsets::MDX_DATA_OFFSET)?,
906 "mdx_data_offset",
907 )?;
908 let vert_array_offset = checked_to_usize(
909 read_u32(bytes, offset + mesh_offsets::VERT_ARRAY_OFFSET)?,
910 "vert_array_offset",
911 )?;
912 let vertex_stride = read_u32(bytes, offset + mesh_offsets::VERTEX_STRUCT_SIZE)?;
913
914 let render = read_u8(bytes, offset + mesh_offsets::RENDER)? != 0;
916 let shadow = read_u8(bytes, offset + mesh_offsets::SHADOW)? != 0;
917
918 let fn_ptr_gen_vertices = read_u32(bytes, offset + mesh_offsets::FN_PTR_GEN_VERTICES)?;
920 let fn_ptr_remove_temp_array =
921 read_u32(bytes, offset + mesh_offsets::FN_PTR_REMOVE_TEMP_ARRAY)?;
922
923 let bounding_min = [
925 read_f32(bytes, offset + mesh_offsets::BOUNDING_MIN)?,
926 read_f32(bytes, offset + mesh_offsets::BOUNDING_MIN + 4)?,
927 read_f32(bytes, offset + mesh_offsets::BOUNDING_MIN + 8)?,
928 ];
929 let bounding_max = [
930 read_f32(bytes, offset + mesh_offsets::BOUNDING_MAX)?,
931 read_f32(bytes, offset + mesh_offsets::BOUNDING_MAX + 4)?,
932 read_f32(bytes, offset + mesh_offsets::BOUNDING_MAX + 8)?,
933 ];
934 let bsphere_radius = read_f32(bytes, offset + mesh_offsets::BSPHERE_RADIUS)?;
935 let bsphere_center = [
936 read_f32(bytes, offset + mesh_offsets::BSPHERE_CENTER)?,
937 read_f32(bytes, offset + mesh_offsets::BSPHERE_CENTER + 4)?,
938 read_f32(bytes, offset + mesh_offsets::BSPHERE_CENTER + 8)?,
939 ];
940
941 let diffuse_color = [
943 read_f32(bytes, offset + mesh_offsets::DIFFUSE_COLOR)?,
944 read_f32(bytes, offset + mesh_offsets::DIFFUSE_COLOR + 4)?,
945 read_f32(bytes, offset + mesh_offsets::DIFFUSE_COLOR + 8)?,
946 ];
947 let ambient_color = [
948 read_f32(bytes, offset + mesh_offsets::AMBIENT_COLOR)?,
949 read_f32(bytes, offset + mesh_offsets::AMBIENT_COLOR + 4)?,
950 read_f32(bytes, offset + mesh_offsets::AMBIENT_COLOR + 8)?,
951 ];
952 let transparency_hint = read_i32(bytes, offset + mesh_offsets::TRANSPARENCY_HINT)?;
953
954 let texture_0 = read_fixed_string(
956 bytes,
957 offset + mesh_offsets::TEXTURE_0,
958 mesh_offsets::TEXTURE_NAME_SIZE,
959 );
960 let texture_1 = read_fixed_string(
961 bytes,
962 offset + mesh_offsets::TEXTURE_1,
963 mesh_offsets::TEXTURE_NAME_SIZE,
964 );
965
966 let animate_uv = read_i32(bytes, offset + mesh_offsets::ANIMATE_UV)?;
968 let uv_direction_x = read_f32(bytes, offset + mesh_offsets::UV_DIRECTION_X)?;
969 let uv_direction_y = read_f32(bytes, offset + mesh_offsets::UV_DIRECTION_Y)?;
970 let uv_jitter = read_f32(bytes, offset + mesh_offsets::UV_JITTER)?;
971 let uv_jitter_speed = read_f32(bytes, offset + mesh_offsets::UV_JITTER_SPEED)?;
972
973 let texture_channel_count = read_u16(bytes, offset + mesh_offsets::TEXTURE_CHANNEL_COUNT)?;
975 let light_mapped = read_u8(bytes, offset + mesh_offsets::LIGHT_MAPPED)? != 0;
976 let rotate_texture = read_u8(bytes, offset + mesh_offsets::ROTATE_TEXTURE)? != 0;
977 let is_background_geometry =
978 read_u8(bytes, offset + mesh_offsets::IS_BACKGROUND_GEOMETRY)? != 0;
979 let beaming = read_u8(bytes, offset + mesh_offsets::BEAMING)? != 0;
980 let total_surface_area = read_f32(bytes, offset + mesh_offsets::TOTAL_SURFACE_AREA)?;
981
982 let mut faces = Vec::with_capacity(face_count);
984 if face_count > 0 && face_offset > 0 {
985 if face_offset + (face_count * MAX_FACE_SIZE) <= bytes.len() {
986 for i in 0..face_count {
987 let curr = face_offset + (i * MAX_FACE_SIZE);
988 faces.push(MdlFace {
989 plane_normal: [
990 read_f32(bytes, curr)?,
991 read_f32(bytes, curr + 4)?,
992 read_f32(bytes, curr + 8)?,
993 ],
994 plane_distance: read_f32(bytes, curr + 12)?,
995 surface_id: read_u32(bytes, curr + 16)?,
996 adjacent: [
997 read_u16(bytes, curr + 20)?,
998 read_u16(bytes, curr + 22)?,
999 read_u16(bytes, curr + 24)?,
1000 ],
1001 vertex_indices: [
1002 read_u16(bytes, curr + 26)?,
1003 read_u16(bytes, curr + 28)?,
1004 read_u16(bytes, curr + 30)?,
1005 ],
1006 });
1007 }
1008 } else {
1009 crate::trace_warn!(
1010 face_offset,
1011 face_count,
1012 bytes_len = bytes.len(),
1013 "face array extends beyond buffer, skipping faces"
1014 );
1015 }
1016 }
1017
1018 let inverted_counter = if index_buffer_pools_count > 0 && index_buffer_pools_ptr > 0 {
1026 if index_buffer_pools_ptr + 4 <= bytes.len() {
1027 read_u32(bytes, index_buffer_pools_ptr)?
1028 } else {
1029 0
1030 }
1031 } else {
1032 0
1033 };
1034
1035 let has_embedded_positions = vertex_indices_count_count == 1
1037 && vertex_indices_count_ptr > 0
1038 && vert_array_offset == vertex_indices_count_ptr.saturating_add(4);
1039
1040 let pos_off = read_i32(bytes, offset + mesh_offsets::MDX_POSITION_OFFSET)?;
1043 let norm_off = read_i32(bytes, offset + mesh_offsets::MDX_NORMAL_OFFSET)?;
1044 let color_off = read_i32(bytes, offset + mesh_offsets::MDX_COLOR_OFFSET)?;
1045 let uv1_off = read_i32(bytes, offset + mesh_offsets::MDX_UV1_OFFSET)?;
1046 let uv2_off = read_i32(bytes, offset + mesh_offsets::MDX_UV2_OFFSET)?;
1047 let uv3_off = read_i32(bytes, offset + mesh_offsets::MDX_UV3_OFFSET)?;
1048 let uv4_off = read_i32(bytes, offset + mesh_offsets::MDX_UV4_OFFSET)?;
1049 let tangent_off = read_i32(bytes, offset + mesh_offsets::MDX_TANGENT_SPACE_OFFSET)?;
1050
1051 let info = MdxMeshInfo {
1052 stride: vertex_stride,
1053 vertex_count,
1054 mdx_data_offset,
1055 vert_array_offset,
1056 pos_off,
1057 norm_off,
1058 color_off,
1059 uv1_off,
1060 uv2_off,
1061 uv3_off,
1062 uv4_off,
1063 tangent_off,
1064 bone_weights_off: -1,
1065 bone_indices_off: -1,
1066 };
1067
1068 let mesh = MdlMesh {
1069 fn_ptr_gen_vertices,
1070 fn_ptr_remove_temp_array,
1071 bounding_min,
1072 bounding_max,
1073 bsphere_radius,
1074 bsphere_center,
1075 diffuse_color,
1076 ambient_color,
1077 transparency_hint,
1078 texture_0,
1079 texture_1,
1080 animate_uv,
1081 uv_direction_x,
1082 uv_direction_y,
1083 uv_jitter,
1084 uv_jitter_speed,
1085 texture_channel_count,
1086 light_mapped,
1087 rotate_texture,
1088 is_background_geometry,
1089 beaming,
1090 total_surface_area,
1091 positions: Vec::new(),
1092 normals: Vec::new(),
1093 vertex_colors: Vec::new(),
1094 uv1: Vec::new(),
1095 uv2: Vec::new(),
1096 uv3: Vec::new(),
1097 uv4: Vec::new(),
1098 tangent_space: Vec::new(),
1099 faces,
1100 inverted_counter,
1101 has_embedded_positions,
1102 shared_index_offset,
1103 shared_index_pool,
1104 shared_index_size,
1105 indices_per_face,
1106 vertex_count: u16::try_from(vertex_count)
1107 .map_err(|_| MdlError::ValueOverflow("vertex_count"))?,
1108 render,
1109 shadow,
1110 };
1111
1112 Ok((mesh, info))
1113}
1114
1115fn populate_mdx_data(
1127 root: &mut MdlNode,
1128 mdx: &[u8],
1129 infos: &[MdxMeshInfo],
1130) -> Result<(), MdlError> {
1131 if infos.is_empty() {
1132 return Ok(());
1133 }
1134
1135 let mut results: Vec<MdxVertexData> =
1137 (0..infos.len()).map(|_| MdxVertexData::default()).collect();
1138
1139 for (idx, info) in infos.iter().enumerate() {
1140 let data = read_mdx_vertices(mdx, info)?;
1141 results[idx] = data;
1142 }
1143
1144 let mut counter = 0;
1146 apply_vertex_data(root, &mut results, &mut counter);
1147
1148 Ok(())
1149}
1150
1151fn read_mdx_vertices(mdx: &[u8], info: &MdxMeshInfo) -> Result<MdxVertexData, MdlError> {
1154 let mut data = MdxVertexData::default();
1155
1156 if info.vertex_count == 0 || info.stride == 0 {
1157 return Ok(data);
1158 }
1159
1160 let stride = checked_to_usize(info.stride, "vertex_stride")?;
1161 let mdx_base = info.mdx_data_offset;
1162
1163 for i in 0..info.vertex_count {
1164 let base = mdx_base + (i * stride);
1165 if base + stride > mdx.len() {
1166 crate::trace_warn!(
1167 vertex_index = i,
1168 base,
1169 stride,
1170 mdx_len = mdx.len(),
1171 mdx_cursor = mdx_base,
1172 "MDX vertex data truncated"
1173 );
1174 break;
1175 }
1176
1177 if let Ok(off) = usize::try_from(info.pos_off) {
1179 let o = base + off;
1180 data.positions.push([
1181 read_f32(mdx, o)?,
1182 read_f32(mdx, o + 4)?,
1183 read_f32(mdx, o + 8)?,
1184 ]);
1185 }
1186
1187 if let Ok(off) = usize::try_from(info.norm_off) {
1189 let o = base + off;
1190 data.normals.push([
1191 read_f32(mdx, o)?,
1192 read_f32(mdx, o + 4)?,
1193 read_f32(mdx, o + 8)?,
1194 ]);
1195 }
1196
1197 if let Ok(off) = usize::try_from(info.color_off) {
1199 let o = base + off;
1200 data.vertex_colors.push([
1201 read_u8(mdx, o)?,
1202 read_u8(mdx, o + 1)?,
1203 read_u8(mdx, o + 2)?,
1204 read_u8(mdx, o + 3)?,
1205 ]);
1206 }
1207
1208 if let Ok(off) = usize::try_from(info.uv1_off) {
1210 let o = base + off;
1211 data.uv1.push([read_f32(mdx, o)?, read_f32(mdx, o + 4)?]);
1212 }
1213
1214 if let Ok(off) = usize::try_from(info.uv2_off) {
1216 let o = base + off;
1217 data.uv2.push([read_f32(mdx, o)?, read_f32(mdx, o + 4)?]);
1218 }
1219
1220 if let Ok(off) = usize::try_from(info.uv3_off) {
1222 let o = base + off;
1223 data.uv3.push([read_f32(mdx, o)?, read_f32(mdx, o + 4)?]);
1224 }
1225
1226 if let Ok(off) = usize::try_from(info.uv4_off) {
1228 let o = base + off;
1229 data.uv4.push([read_f32(mdx, o)?, read_f32(mdx, o + 4)?]);
1230 }
1231
1232 if let Ok(off) = usize::try_from(info.tangent_off) {
1234 let o = base + off;
1235 data.tangent_space.push([
1236 [
1237 read_f32(mdx, o)?,
1238 read_f32(mdx, o + 4)?,
1239 read_f32(mdx, o + 8)?,
1240 ],
1241 [
1242 read_f32(mdx, o + 12)?,
1243 read_f32(mdx, o + 16)?,
1244 read_f32(mdx, o + 20)?,
1245 ],
1246 [
1247 read_f32(mdx, o + 24)?,
1248 read_f32(mdx, o + 28)?,
1249 read_f32(mdx, o + 32)?,
1250 ],
1251 ]);
1252 }
1253
1254 if let Ok(off) = usize::try_from(info.bone_weights_off) {
1256 let o = base + off;
1257 data.bone_weights.push([
1258 read_f32(mdx, o)?,
1259 read_f32(mdx, o + 4)?,
1260 read_f32(mdx, o + 8)?,
1261 read_f32(mdx, o + 12)?,
1262 ]);
1263 }
1264
1265 if let Ok(off) = usize::try_from(info.bone_indices_off) {
1267 let o = base + off;
1268 data.bone_indices.push([
1269 read_f32(mdx, o)?,
1270 read_f32(mdx, o + 4)?,
1271 read_f32(mdx, o + 8)?,
1272 read_f32(mdx, o + 12)?,
1273 ]);
1274 }
1275 }
1276
1277 let actual_vertex_count = data
1284 .positions
1285 .len()
1286 .max(data.normals.len())
1287 .max(data.vertex_colors.len())
1288 .max(data.uv1.len())
1289 .max(data.uv2.len())
1290 .max(data.uv3.len())
1291 .max(data.uv4.len())
1292 .max(data.tangent_space.len());
1293
1294 if actual_vertex_count > 0 && actual_vertex_count < info.vertex_count {
1295 data.clamped_vertex_count = Some(
1296 u16::try_from(actual_vertex_count)
1297 .map_err(|_| MdlError::ValueOverflow("actual_vertex_count"))?,
1298 );
1299 }
1300
1301 Ok(data)
1302}
1303
1304fn populate_content_positions(
1311 root: &mut MdlNode,
1312 bytes: &[u8],
1313 infos: &[MdxMeshInfo],
1314) -> Result<(), MdlError> {
1315 if infos.is_empty() {
1316 return Ok(());
1317 }
1318
1319 let mut results: Vec<MdxVertexData> =
1321 (0..infos.len()).map(|_| MdxVertexData::default()).collect();
1322
1323 for (idx, info) in infos.iter().enumerate() {
1324 if info.vertex_count == 0 || info.vert_array_offset == 0 {
1325 continue;
1326 }
1327
1328 let pos_end = info.vert_array_offset + (info.vertex_count * 12);
1329 if pos_end <= bytes.len() {
1330 let mut positions = Vec::with_capacity(info.vertex_count);
1331 for i in 0..info.vertex_count {
1332 let o = info.vert_array_offset + (i * 12);
1333 positions.push([
1334 read_f32(bytes, o)?,
1335 read_f32(bytes, o + 4)?,
1336 read_f32(bytes, o + 8)?,
1337 ]);
1338 }
1339 results[idx].positions = positions;
1340 } else {
1341 crate::trace_warn!(
1342 vert_array_offset = info.vert_array_offset,
1343 vertex_count = info.vertex_count,
1344 pos_end,
1345 bytes_len = bytes.len(),
1346 "MDL content position data extends beyond buffer"
1347 );
1348 }
1349 }
1350
1351 let mut counter = 0;
1353 apply_vertex_data(root, &mut results, &mut counter);
1354
1355 Ok(())
1356}
1357
1358fn populate_content_positions_fallback(
1366 root: &mut MdlNode,
1367 bytes: &[u8],
1368 infos: &[MdxMeshInfo],
1369) -> Result<(), MdlError> {
1370 if infos.is_empty() {
1371 return Ok(());
1372 }
1373
1374 let mut counter = 0;
1375 fill_missing_positions(root, bytes, infos, &mut counter)?;
1376 Ok(())
1377}
1378
1379fn fill_missing_positions(
1382 node: &mut MdlNode,
1383 bytes: &[u8],
1384 infos: &[MdxMeshInfo],
1385 counter: &mut usize,
1386) -> Result<(), MdlError> {
1387 if node.node_data.mesh().is_some() {
1388 let idx = *counter;
1389 *counter += 1;
1390
1391 if let (Some(info), Some(mesh)) = (infos.get(idx), node.node_data.mesh_mut()) {
1392 if mesh.positions.is_empty() && info.vertex_count > 0 && info.vert_array_offset > 0 {
1393 let pos_end = info.vert_array_offset + (info.vertex_count * 12);
1394 if pos_end <= bytes.len() {
1395 let mut positions = Vec::with_capacity(info.vertex_count);
1396 for i in 0..info.vertex_count {
1397 let o = info.vert_array_offset + (i * 12);
1398 positions.push([
1399 read_f32(bytes, o)?,
1400 read_f32(bytes, o + 4)?,
1401 read_f32(bytes, o + 8)?,
1402 ]);
1403 }
1404 mesh.positions = positions;
1405 } else {
1406 crate::trace_warn!(
1407 vert_array_offset = info.vert_array_offset,
1408 vertex_count = info.vertex_count,
1409 pos_end,
1410 bytes_len = bytes.len(),
1411 "fallback content position data extends beyond buffer"
1412 );
1413 }
1414 }
1415 }
1416 }
1417
1418 for child in &mut node.children {
1419 fill_missing_positions(child, bytes, infos, counter)?;
1420 }
1421
1422 Ok(())
1423}
1424
1425fn apply_vertex_data(node: &mut MdlNode, results: &mut Vec<MdxVertexData>, counter: &mut usize) {
1429 if node.node_data.mesh().is_some() {
1430 let idx = *counter;
1431 *counter += 1;
1432
1433 if idx < results.len() {
1434 let mut data = std::mem::take(&mut results[idx]);
1435 let bone_weights = std::mem::take(&mut data.bone_weights);
1436 let bone_indices = std::mem::take(&mut data.bone_indices);
1437
1438 if let Some(mesh) = node.node_data.mesh_mut() {
1439 mesh.positions = data.positions;
1440 mesh.normals = data.normals;
1441 mesh.vertex_colors = data.vertex_colors;
1442 mesh.uv1 = data.uv1;
1443 mesh.uv2 = data.uv2;
1444 mesh.uv3 = data.uv3;
1445 mesh.uv4 = data.uv4;
1446 mesh.tangent_space = data.tangent_space;
1447 if let Some(clamped) = data.clamped_vertex_count {
1448 mesh.vertex_count = clamped;
1449 }
1450 }
1451
1452 if let MdlNodeData::Skin(skin) = &mut node.node_data {
1455 skin.bone_weights = bone_weights;
1456 skin.bone_indices = bone_indices;
1457 }
1458 }
1459 }
1460
1461 for child in &mut node.children {
1462 apply_vertex_data(child, results, counter);
1463 }
1464}
1465
1466fn read_fixed_string(bytes: &[u8], offset: usize, max_len: usize) -> String {
1472 crate::binary::read_fixed_c_string(bytes, offset, max_len)
1473}
1474
1475fn read_cstring(bytes: &[u8], offset: usize) -> String {
1477 crate::binary::read_c_string(bytes, offset)
1478}
1479
1480fn read_cexo_array<T>(
1486 bytes: &[u8],
1487 offset: usize,
1488 ptr_field: usize,
1489 count_field: usize,
1490 element_size: usize,
1491 label: &'static str,
1492 reader: impl FnOnce(&[u8], usize, usize) -> Result<Vec<T>, MdlError>,
1493) -> Result<Vec<T>, MdlError> {
1494 let ptr = checked_to_usize(read_u32(bytes, offset + ptr_field)?, label)?;
1495 let count = checked_to_usize(read_u32(bytes, offset + count_field)?, label)?;
1496
1497 if count == 0 || ptr == 0 {
1498 return Ok(Vec::new());
1499 }
1500
1501 let byte_size = count * element_size;
1502 if ptr + byte_size > bytes.len() {
1503 crate::trace_warn!(
1504 ptr,
1505 count,
1506 bytes_len = bytes.len(),
1507 label,
1508 "CExoArrayList extends beyond buffer"
1509 );
1510 return Ok(Vec::new());
1511 }
1512
1513 reader(bytes, ptr, count)
1514}
1515
1516fn read_f32_array(bytes: &[u8], ptr: usize, count: usize) -> Result<Vec<f32>, MdlError> {
1518 let mut result = Vec::with_capacity(count);
1519 for i in 0..count {
1520 result.push(read_f32(bytes, ptr + i * 4)?);
1521 }
1522 Ok(result)
1523}
1524
1525fn read_vec3_array(bytes: &[u8], ptr: usize, count: usize) -> Result<Vec<[f32; 3]>, MdlError> {
1527 let mut result = Vec::with_capacity(count);
1528 for i in 0..count {
1529 let base = ptr + i * 12;
1530 result.push([
1531 read_f32(bytes, base)?,
1532 read_f32(bytes, base + 4)?,
1533 read_f32(bytes, base + 8)?,
1534 ]);
1535 }
1536 Ok(result)
1537}
1538
1539fn read_quat_array(bytes: &[u8], ptr: usize, count: usize) -> Result<Vec<[f32; 4]>, MdlError> {
1541 let mut result = Vec::with_capacity(count);
1542 for i in 0..count {
1543 let base = ptr + i * 16;
1544 result.push([
1545 read_f32(bytes, base)?,
1546 read_f32(bytes, base + 4)?,
1547 read_f32(bytes, base + 8)?,
1548 read_f32(bytes, base + 12)?,
1549 ]);
1550 }
1551 Ok(result)
1552}
1553
1554fn read_reference_header(bytes: &[u8], offset: usize) -> Result<MdlReference, MdlError> {
1559 check_slice_in_bounds(bytes, offset, REFERENCE_EXTRA_SIZE, "reference_header")?;
1560
1561 let ref_model = read_fixed_string(bytes, offset, 32);
1562 let reattachable = read_i32(bytes, offset + 0x20)?;
1563
1564 Ok(MdlReference {
1565 ref_model,
1566 reattachable,
1567 })
1568}
1569
1570fn read_light_header(bytes: &[u8], offset: usize) -> Result<MdlLight, MdlError> {
1582 check_slice_in_bounds(bytes, offset, LIGHT_EXTRA_SIZE, "light_header")?;
1583
1584 let flare_radius = read_f32(bytes, offset + light_offsets::FLARE_RADIUS)?;
1585
1586 let sp_base = offset + light_offsets::TEXTURE_SAFE_PTRS_PTR;
1588 let texture_safe_ptrs = [
1589 read_u32(bytes, sp_base)?,
1590 read_u32(bytes, sp_base + 4)?,
1591 read_u32(bytes, sp_base + 8)?,
1592 ];
1593
1594 let flare_sizes = read_cexo_array(
1596 bytes,
1597 offset,
1598 light_offsets::FLARE_SIZES_PTR,
1599 light_offsets::FLARE_SIZES_COUNT,
1600 4,
1601 "flare_sizes",
1602 read_f32_array,
1603 )?;
1604
1605 let flare_positions = read_cexo_array(
1607 bytes,
1608 offset,
1609 light_offsets::FLARE_POSITIONS_PTR,
1610 light_offsets::FLARE_POSITIONS_COUNT,
1611 4,
1612 "flare_positions",
1613 read_f32_array,
1614 )?;
1615
1616 let flare_color_shifts = read_cexo_array(
1618 bytes,
1619 offset,
1620 light_offsets::FLARE_COLOR_SHIFTS_PTR,
1621 light_offsets::FLARE_COLOR_SHIFTS_COUNT,
1622 12,
1623 "flare_color_shifts",
1624 read_vec3_array,
1625 )?;
1626
1627 let flare_texture_names = read_cexo_array(
1630 bytes,
1631 offset,
1632 light_offsets::FLARE_TEX_NAMES_PTR,
1633 light_offsets::FLARE_TEX_NAMES_COUNT,
1634 4,
1635 "flare_tex_names",
1636 |b, ptr, count| {
1637 let mut names = Vec::with_capacity(count);
1638 for i in 0..count {
1639 let str_offset =
1640 checked_to_usize(read_u32(b, ptr + i * 4)?, "flare_tex_name_str_ptr")?;
1641 if str_offset > 0 && str_offset < b.len() {
1642 names.push(read_cstring(b, str_offset));
1643 } else {
1644 names.push(String::new());
1645 }
1646 }
1647 Ok(names)
1648 },
1649 )?;
1650
1651 let priority = read_i32(bytes, offset + light_offsets::PRIORITY)?;
1652 let num_dynamic_types = read_i32(bytes, offset + light_offsets::NUM_DYNAMIC_TYPES)?;
1653 let affectdynamic = read_i32(bytes, offset + light_offsets::AFFECTDYNAMIC)?;
1654 let shadow = read_i32(bytes, offset + light_offsets::SHADOW)?;
1655 let ambientonly = read_i32(bytes, offset + light_offsets::AMBIENTONLY)?;
1656 let generateflare = read_i32(bytes, offset + light_offsets::GENERATEFLARE)?;
1657 let fading_light = read_i32(bytes, offset + light_offsets::FADING_LIGHT)?;
1658
1659 Ok(MdlLight {
1660 flare_radius,
1661 texture_safe_ptrs,
1662 flare_sizes,
1663 flare_positions,
1664 flare_color_shifts,
1665 flare_texture_names,
1666 priority,
1667 num_dynamic_types,
1668 affectdynamic,
1669 shadow,
1670 ambientonly,
1671 generateflare,
1672 fading_light,
1673 })
1674}
1675
1676fn read_emitter_header(bytes: &[u8], offset: usize) -> Result<super::types::MdlEmitter, MdlError> {
1681 check_slice_in_bounds(bytes, offset, EMITTER_EXTRA_SIZE, "emitter_header")?;
1682
1683 let deadspace = read_f32(bytes, offset)?;
1684 let blast_radius = read_f32(bytes, offset + 0x04)?;
1685 let blast_length = read_f32(bytes, offset + 0x08)?;
1686 let num_branches = read_i32(bytes, offset + 0x0C)?;
1687 let control_pt_smoothing = read_i32(bytes, offset + 0x10)?;
1688 let x_grid = read_i32(bytes, offset + 0x14)?;
1689 let y_grid = read_i32(bytes, offset + 0x18)?;
1690 let spawn_type = read_i32(bytes, offset + 0x1C)?;
1691
1692 let update = read_fixed_string(bytes, offset + 0x20, 32);
1693 let render = read_fixed_string(bytes, offset + 0x40, 32);
1694 let blend = read_fixed_string(bytes, offset + 0x60, 32);
1695 let texture = read_fixed_string(bytes, offset + 0x80, 32);
1696 let chunk_name = read_fixed_string(bytes, offset + 0xA0, 16);
1697
1698 let two_sided_tex = read_i32(bytes, offset + 0xB0)?;
1699 let loop_emitter = read_i32(bytes, offset + 0xB4)?;
1700 let render_order = read_u16(bytes, offset + 0xB8)?;
1701 let frame_blending = read_u8(bytes, offset + 0xBA)? != 0;
1702 let depth_texture_name = read_fixed_string(bytes, offset + 0xBB, 16);
1703
1704 let mut reserved = [0u8; 21];
1706 reserved.copy_from_slice(&bytes[offset + 0xCB..offset + 0xE0]);
1707
1708 Ok(super::types::MdlEmitter {
1709 deadspace,
1710 blast_radius,
1711 blast_length,
1712 num_branches,
1713 control_pt_smoothing,
1714 x_grid,
1715 y_grid,
1716 spawn_type,
1717 update,
1718 render,
1719 blend,
1720 texture,
1721 chunk_name,
1722 two_sided_tex,
1723 loop_emitter,
1724 render_order,
1725 frame_blending,
1726 depth_texture_name,
1727 reserved,
1728 })
1729}
1730
1731fn read_dangly_extra(bytes: &[u8], offset: usize, mesh: MdlMesh) -> Result<MdlDangly, MdlError> {
1742 check_slice_in_bounds(bytes, offset, DANGLY_EXTRA_SIZE, "dangly_extra")?;
1743
1744 let constraints = read_cexo_array(
1746 bytes,
1747 offset,
1748 dangly_offsets::CONSTRAINTS_PTR,
1749 dangly_offsets::CONSTRAINTS_COUNT,
1750 4,
1751 "dangly_constraints",
1752 read_f32_array,
1753 )?;
1754
1755 let displacement = read_f32(bytes, offset + dangly_offsets::DISPLACEMENT)?;
1756 let tightness = read_f32(bytes, offset + dangly_offsets::TIGHTNESS)?;
1757 let period = read_f32(bytes, offset + dangly_offsets::PERIOD)?;
1758
1759 let dangly_verts_ptr = checked_to_usize(
1762 read_u32(bytes, offset + dangly_offsets::DATA_PTR)?,
1763 "dangly_verts_ptr",
1764 )?;
1765
1766 let vert_count = usize::from(mesh.vertex_count);
1768 let mut dangly_vertices = Vec::with_capacity(vert_count);
1769 if vert_count > 0 && dangly_verts_ptr > 0 {
1770 let required = vert_count * 12;
1771 if dangly_verts_ptr + required <= bytes.len() {
1772 dangly_vertices = read_vec3_array(bytes, dangly_verts_ptr, vert_count)?;
1773 } else {
1774 crate::trace_warn!(
1775 dangly_verts_ptr,
1776 vert_count,
1777 bytes_len = bytes.len(),
1778 "dangly vertices array extends beyond buffer"
1779 );
1780 }
1781 }
1782
1783 Ok(MdlDangly {
1784 mesh,
1785 constraints,
1786 displacement,
1787 tightness,
1788 period,
1789 dangly_vertices,
1790 })
1791}
1792
1793fn read_skin_extra(bytes: &[u8], offset: usize, mesh: MdlMesh) -> Result<MdlSkin, MdlError> {
1803 check_slice_in_bounds(bytes, offset, SKIN_EXTRA_SIZE, "skin_extra")?;
1804
1805 let mdx_bone_weights_offset = read_i32(bytes, offset + skin_offsets::MDX_BONE_WEIGHTS_OFFSET)?;
1812 let mdx_bone_indices_offset = read_i32(bytes, offset + skin_offsets::MDX_BONE_INDICES_OFFSET)?;
1813
1814 let bonemap_ptr = checked_to_usize(
1816 read_u32(bytes, offset + skin_offsets::BONEMAP_PTR)?,
1817 "skin_bonemap_ptr",
1818 )?;
1819 let bonemap_count = read_u32(bytes, offset + skin_offsets::BONEMAP_COUNT)?;
1820
1821 let mut bonemap = Vec::new();
1822 if bonemap_count > 0 && bonemap_ptr > 0 {
1823 let entry_count = checked_to_usize(bonemap_count, "bonemap_count")?;
1824 let byte_len = entry_count.checked_mul(4).unwrap_or(0);
1825 if byte_len > 0 && bonemap_ptr + byte_len <= bytes.len() {
1826 bonemap.reserve(entry_count);
1827 for i in 0..entry_count {
1828 bonemap.push(read_u32(bytes, bonemap_ptr + i * 4)?);
1829 }
1830 } else {
1831 crate::trace_warn!(
1832 bonemap_ptr,
1833 bonemap_count,
1834 bytes_len = bytes.len(),
1835 "skin bonemap extends beyond buffer"
1836 );
1837 }
1838 }
1839
1840 let qbone_ref_inv = read_cexo_array(
1842 bytes,
1843 offset,
1844 skin_offsets::QBONE_REF_INV_PTR,
1845 skin_offsets::QBONE_REF_INV_COUNT,
1846 16,
1847 "skin_qbone_ref_inv",
1848 read_quat_array,
1849 )?;
1850
1851 let tbone_ref_inv = read_cexo_array(
1853 bytes,
1854 offset,
1855 skin_offsets::TBONE_REF_INV_PTR,
1856 skin_offsets::TBONE_REF_INV_COUNT,
1857 12,
1858 "skin_tbone_ref_inv",
1859 read_vec3_array,
1860 )?;
1861
1862 let bone_constant_indices = read_cexo_array(
1864 bytes,
1865 offset,
1866 skin_offsets::BONE_CONSTANT_INDICES_PTR,
1867 skin_offsets::BONE_CONSTANT_INDICES_COUNT,
1868 4,
1869 "skin_bone_constant_indices",
1870 |b, ptr, count| {
1871 let mut indices = Vec::with_capacity(count);
1872 for i in 0..count {
1873 indices.push(read_i32(b, ptr + (i * 4))?);
1874 }
1875 Ok(indices)
1876 },
1877 )?;
1878
1879 let mut bone_node_numbers = [0u16; 16];
1881 let bnn_offset = offset + skin_offsets::BONE_NODE_NUMBERS;
1882 for (i, slot) in bone_node_numbers.iter_mut().enumerate() {
1883 *slot = read_u16(bytes, bnn_offset + i * 2)?;
1884 }
1885
1886 Ok(MdlSkin {
1890 mesh,
1891 mdx_bone_weights_offset,
1892 mdx_bone_indices_offset,
1893 bone_weights: Vec::new(),
1894 bone_indices: Vec::new(),
1895 bonemap,
1896 qbone_ref_inv,
1897 tbone_ref_inv,
1898 bone_constant_indices,
1899 bone_node_numbers,
1900 })
1901}
1902
1903fn read_anim_mesh_extra(
1914 bytes: &[u8],
1915 offset: usize,
1916 mesh: MdlMesh,
1917) -> Result<MdlAnimMesh, MdlError> {
1918 check_slice_in_bounds(bytes, offset, ANIM_MESH_EXTRA_SIZE, "anim_mesh_extra")?;
1919
1920 let sample_period = read_f32(bytes, offset + anim_mesh_offsets::SAMPLE_PERIOD)?;
1921
1922 let anim_verts = read_cexo_array(
1924 bytes,
1925 offset,
1926 anim_mesh_offsets::ANIM_VERTS_PTR,
1927 anim_mesh_offsets::ANIM_VERTS_COUNT,
1928 12,
1929 "anim_verts",
1930 read_vec3_array,
1931 )?;
1932
1933 let anim_t_verts = read_cexo_array(
1935 bytes,
1936 offset,
1937 anim_mesh_offsets::ANIM_T_VERTS_PTR,
1938 anim_mesh_offsets::ANIM_T_VERTS_COUNT,
1939 12,
1940 "anim_t_verts",
1941 read_vec3_array,
1942 )?;
1943
1944 let data_ptr_1 = read_u32(bytes, offset + anim_mesh_offsets::DATA_PTR_1)?;
1947 let data_count_1 = read_u32(bytes, offset + anim_mesh_offsets::DATA_COUNT_1)?;
1948 let padding_24 = read_u32(bytes, offset + anim_mesh_offsets::PADDING_24)?;
1949 let anim_vertices_ptr = read_u32(bytes, offset + anim_mesh_offsets::ANIM_VERTICES_PTR)?;
1950 let anim_tex_vertices_ptr = read_u32(bytes, offset + anim_mesh_offsets::ANIM_TEX_VERTICES_PTR)?;
1951 let anim_vertices_count_val = read_u32(bytes, offset + anim_mesh_offsets::ANIM_VERTICES_COUNT)?;
1952 let anim_tex_vertices_count =
1953 read_u32(bytes, offset + anim_mesh_offsets::ANIM_TEX_VERTICES_COUNT)?;
1954
1955 Ok(MdlAnimMesh {
1956 mesh,
1957 sample_period,
1958 anim_verts,
1959 anim_t_verts,
1960 data_ptr_1,
1961 data_count_1,
1962 padding_24,
1963 anim_vertices_ptr,
1964 anim_tex_vertices_ptr,
1965 anim_vertices_count: anim_vertices_count_val,
1966 anim_tex_vertices_count,
1967 })
1968}
1969
1970fn read_aabb_extra(
1979 bytes: &[u8],
1980 offset: usize,
1981 _node_data_end_hint: usize,
1982 mesh: MdlMesh,
1983) -> Result<MdlAabb, MdlError> {
1984 check_slice_in_bounds(bytes, offset, AABB_EXTRA_SIZE, "aabb_extra")?;
1985
1986 let aabb_tree_ptr = checked_to_usize(
1987 read_u32(bytes, offset + aabb_offsets::TREE_PTR)?,
1988 "aabb_tree_ptr",
1989 )?;
1990
1991 let aabb_tree = if aabb_tree_ptr > 0 {
1992 Some(Box::new(read_aabb_tree(bytes, aabb_tree_ptr)?))
1993 } else {
1994 None
1995 };
1996
1997 Ok(MdlAabb { mesh, aabb_tree })
1998}
1999
2000fn read_aabb_tree(bytes: &[u8], ptr: usize) -> Result<AabbNode, MdlError> {
2007 const AABB_NODE_SIZE: usize = 40;
2008
2009 check_slice_in_bounds(bytes, ptr, AABB_NODE_SIZE, "aabb_node")?;
2010
2011 let box_min = [
2012 read_f32(bytes, ptr)?,
2013 read_f32(bytes, ptr + 4)?,
2014 read_f32(bytes, ptr + 8)?,
2015 ];
2016 let box_max = [
2017 read_f32(bytes, ptr + 12)?,
2018 read_f32(bytes, ptr + 16)?,
2019 read_f32(bytes, ptr + 20)?,
2020 ];
2021 let right_ptr = checked_to_usize(read_u32(bytes, ptr + 24)?, "aabb_right_child")?;
2023 let left_ptr = checked_to_usize(read_u32(bytes, ptr + 28)?, "aabb_left_child")?;
2024 let face_index = read_i32(bytes, ptr + 32)?;
2025 let split_direction_flags = read_u32(bytes, ptr + 36)?;
2026
2027 let left = if left_ptr > 0 {
2028 Some(Box::new(read_aabb_tree(bytes, left_ptr)?))
2029 } else {
2030 None
2031 };
2032 let right = if right_ptr > 0 {
2033 Some(Box::new(read_aabb_tree(bytes, right_ptr)?))
2034 } else {
2035 None
2036 };
2037
2038 Ok(AabbNode {
2039 box_min,
2040 box_max,
2041 face_index,
2042 split_direction_flags,
2043 left,
2044 right,
2045 })
2046}
2047
2048fn read_saber_extra(bytes: &[u8], offset: usize, mesh: MdlMesh) -> Result<MdlSaber, MdlError> {
2065 check_slice_in_bounds(bytes, offset, SABER_EXTRA_SIZE, "saber_extra")?;
2066
2067 let verts_ptr = checked_to_usize(
2068 read_u32(bytes, offset + saber_offsets::VERTS_PTR)?,
2069 "saber_verts_ptr",
2070 )?;
2071 let uvs_ptr = checked_to_usize(
2072 read_u32(bytes, offset + saber_offsets::UVS_PTR)?,
2073 "saber_uvs_ptr",
2074 )?;
2075 let normals_ptr = checked_to_usize(
2076 read_u32(bytes, offset + saber_offsets::NORMALS_PTR)?,
2077 "saber_normals_ptr",
2078 )?;
2079 let gl_pool_vert = read_u32(bytes, offset + saber_offsets::GL_POOL_VERT)?;
2080 let gl_pool_index = read_u32(bytes, offset + saber_offsets::GL_POOL_INDEX)?;
2081
2082 let mut saber_verts = Vec::with_capacity(NUM_SABER_VERTS);
2084 if verts_ptr > 0 {
2085 let byte_size = NUM_SABER_VERTS * 12;
2086 if verts_ptr + byte_size <= bytes.len() {
2087 saber_verts = read_vec3_array(bytes, verts_ptr, NUM_SABER_VERTS)?;
2088 } else {
2089 crate::trace_warn!(
2090 verts_ptr,
2091 byte_size,
2092 bytes_len = bytes.len(),
2093 "saber_verts array extends beyond buffer"
2094 );
2095 }
2096 }
2097
2098 let mut saber_uvs = Vec::with_capacity(NUM_SABER_VERTS);
2100 if uvs_ptr > 0 {
2101 let byte_size = NUM_SABER_VERTS * 8;
2102 if uvs_ptr + byte_size <= bytes.len() {
2103 for i in 0..NUM_SABER_VERTS {
2104 let base = uvs_ptr + i * 8;
2105 saber_uvs.push([read_f32(bytes, base)?, read_f32(bytes, base + 4)?]);
2106 }
2107 } else {
2108 crate::trace_warn!(
2109 uvs_ptr,
2110 byte_size,
2111 bytes_len = bytes.len(),
2112 "saber_uvs array extends beyond buffer"
2113 );
2114 }
2115 }
2116
2117 let mut saber_normals = Vec::with_capacity(NUM_SABER_VERTS);
2119 if normals_ptr > 0 {
2120 let byte_size = NUM_SABER_VERTS * 12;
2121 if normals_ptr + byte_size <= bytes.len() {
2122 saber_normals = read_vec3_array(bytes, normals_ptr, NUM_SABER_VERTS)?;
2123 } else {
2124 crate::trace_warn!(
2125 normals_ptr,
2126 byte_size,
2127 bytes_len = bytes.len(),
2128 "saber_normals array extends beyond buffer"
2129 );
2130 }
2131 }
2132
2133 Ok(MdlSaber {
2134 mesh,
2135 saber_verts,
2136 saber_uvs,
2137 saber_normals,
2138 gl_pool_vert,
2139 gl_pool_index,
2140 })
2141}