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(
52 feature = "tracing",
53 tracing::instrument(level = "debug", skip(reader, mdx_bytes))
54)]
55pub fn read_mdl<R: Read>(reader: &mut R, mdx_bytes: Option<&[u8]>) -> Result<Mdl, MdlError> {
56 let mut buffer = Vec::new();
57 reader.read_to_end(&mut buffer)?;
58 crate::trace_debug!(bytes_len = buffer.len(), "read mdl bytes from reader");
59 read_mdl_from_bytes(&buffer, mdx_bytes)
60}
61
62#[cfg_attr(
81 feature = "tracing",
82 tracing::instrument(level = "debug", skip(bytes, mdx_bytes), fields(bytes_len = bytes.len()))
83)]
84pub fn read_mdl_from_bytes(bytes: &[u8], mdx_bytes: Option<&[u8]>) -> Result<Mdl, MdlError> {
85 read_mdl_from_cursor(&mut Cursor::new(bytes), mdx_bytes)
86}
87
88struct MdxMeshInfo {
98 stride: u32,
100 vertex_count: usize,
102 mdx_data_offset: usize,
106 vert_array_offset: usize,
110 pos_off: i32,
112 norm_off: i32,
113 color_off: i32,
114 uv1_off: i32,
115 uv2_off: i32,
116 uv3_off: i32,
117 uv4_off: i32,
118 tangent_off: i32,
119 bone_weights_off: i32,
121 bone_indices_off: i32,
122}
123
124#[derive(Default)]
126struct MdxVertexData {
127 positions: Vec<[f32; 3]>,
128 normals: Vec<[f32; 3]>,
129 vertex_colors: Vec<[u8; 4]>,
130 uv1: Vec<[f32; 2]>,
131 uv2: Vec<[f32; 2]>,
132 uv3: Vec<[f32; 2]>,
133 uv4: Vec<[f32; 2]>,
134 tangent_space: Vec<[[f32; 3]; 3]>,
135 bone_weights: Vec<[f32; 4]>,
136 bone_indices: Vec<[f32; 4]>,
137 clamped_vertex_count: Option<u16>,
139}
140
141fn read_mdl_from_cursor<R: Read + Seek>(
150 reader: &mut R,
151 mdx_bytes: Option<&[u8]>,
152) -> Result<Mdl, MdlError> {
153 let len = reader.seek(SeekFrom::End(0))?;
155 if len < MDL_WRAPPER_SIZE {
156 return Err(crate::binary::BinaryLayoutError::UnexpectedEof("preamble").into());
157 }
158 reader.seek(SeekFrom::Start(MDL_WRAPPER_SIZE))?;
159
160 let mut content = Vec::new();
162 reader.read_to_end(&mut content)?;
163 let bytes = &content;
164
165 if bytes.len() < 196 {
167 return Err(crate::binary::BinaryLayoutError::UnexpectedEof("header").into());
168 }
169
170 let geometry_fn_ptr1 = read_u32(bytes, header_offsets::FN_PTR1)?;
172 let geometry_fn_ptr2 = read_u32(bytes, header_offsets::FN_PTR2)?;
173 let root_node_offset = checked_to_usize(
174 read_u32(bytes, header_offsets::ROOT_NODE_PTR)?,
175 "root_offset",
176 )?;
177 let node_count = read_u32(bytes, header_offsets::NODE_COUNT)?;
178 let model_type = read_u8(bytes, header_offsets::MODEL_TYPE)?;
179
180 let classification = read_u8(bytes, header_offsets::CLASSIFICATION)?;
182 let subclassification = read_u8(bytes, header_offsets::SUBCLASSIFICATION)?;
183 let affected_by_fog = read_u8(bytes, header_offsets::AFFECTED_BY_FOG)?;
184
185 let mut bounding_box = [0.0f32; 6];
187 for (i, val) in bounding_box.iter_mut().enumerate() {
188 *val = read_f32(bytes, header_offsets::BOUNDING_BOX_MIN + i * 4)?;
189 }
190 let radius = read_f32(bytes, header_offsets::RADIUS)?;
191 let animation_scale = read_f32(bytes, header_offsets::ANIMATION_SCALE)?;
192
193 let supermodel_name = read_fixed_string(
195 bytes,
196 header_offsets::SUPERMODEL_NAME,
197 header_offsets::SUPERMODEL_NAME_SIZE,
198 );
199
200 let name_offsets_ptr = checked_to_usize(
201 read_u32(bytes, header_offsets::NAME_OFFSETS_PTR)?,
202 "name_offsets",
203 )?;
204 let name_count = read_u32(bytes, header_offsets::NAME_COUNT)?;
205
206 let mut names = Vec::new();
208 if name_offsets_ptr > 0 && name_offsets_ptr < bytes.len() {
209 let name_count = checked_to_usize(name_count, "name_count")?;
210 for i in 0..name_count {
211 let ptr_offset = name_offsets_ptr + (i * 4);
212 if ptr_offset + 4 > bytes.len() {
213 crate::trace_warn!(
214 index = i,
215 ptr_offset,
216 bytes_len = bytes.len(),
217 "name offset array truncated"
218 );
219 break;
220 }
221
222 let name_offset = checked_to_usize(read_u32(bytes, ptr_offset)?, "name_ptr")?;
223
224 if name_offset < bytes.len() {
225 names.push(crate::binary::read_c_string(bytes, name_offset));
226 } else {
227 crate::trace_warn!(
228 index = i,
229 name_offset,
230 bytes_len = bytes.len(),
231 "name string offset out of bounds, using synthetic name"
232 );
233 names.push(format!("Node_{}", i));
234 }
235 }
236 }
237
238 let mut mdx_infos: Vec<MdxMeshInfo> = Vec::new();
242 let mut dfs_mesh_counter: usize = 0;
243 let mut root = read_node(
244 bytes,
245 root_node_offset,
246 &names,
247 None,
248 None,
249 &mut mdx_infos,
250 &mut dfs_mesh_counter,
251 )?;
252
253 if let Some(mdx) = mdx_bytes {
270 populate_mdx_data(&mut root, mdx, &mdx_infos)?;
271 populate_content_positions_fallback(&mut root, bytes, &mdx_infos)?;
276 } else {
277 populate_content_positions(&mut root, bytes, &mdx_infos)?;
278 }
279
280 let anim_arr_ptr = read_u32(bytes, header_offsets::ANIMATION_ARR_PTR)?;
282 let anim_arr_count = read_u32(bytes, header_offsets::ANIMATION_ARR_COUNT)?;
283 let animations = read_animations(bytes, anim_arr_ptr, anim_arr_count, &names)?;
284
285 let off_anim_root_raw = read_u32(bytes, header_offsets::OFF_ANIM_ROOT)?;
288 let anim_root_node = if off_anim_root_raw
289 != u32::try_from(root_node_offset)
290 .map_err(|_| MdlError::ValueOverflow("root_node_offset"))?
291 && off_anim_root_raw > 0
292 {
293 let ar_offset = checked_to_usize(off_anim_root_raw, "off_anim_root")?;
294 if ar_offset + 6 <= bytes.len() {
295 let name_idx = usize::from(read_u16(bytes, ar_offset + node_offsets::NAME_INDEX)?);
296 if name_idx < names.len() {
297 Some(names[name_idx].clone())
298 } else {
299 None
300 }
301 } else {
302 None
303 }
304 } else {
305 None
306 };
307
308 Ok(Mdl {
309 root_node: root,
310 geometry_fn_ptr1,
311 geometry_fn_ptr2,
312 model_type,
313 classification,
314 subclassification,
315 affected_by_fog,
316 supermodel_name,
317 node_count,
318 bounding_box,
319 radius,
320 animation_scale,
321 animations,
322 anim_root_node,
323 })
324}
325
326struct ControllerReadResult {
339 controllers: Vec<MdlController>,
340 orphan_data: Vec<f32>,
341}
342
343fn read_controllers(
344 bytes: &[u8],
345 key_ptr: usize,
346 key_count: usize,
347 data_ptr: usize,
348 data_count: usize,
349) -> Result<ControllerReadResult, MdlError> {
350 let mut controller_data = Vec::with_capacity(data_count);
352 if data_count > 0 && data_ptr > 0 {
353 check_slice_in_bounds(bytes, data_ptr, data_count * 4, "controller_data")?;
354 for i in 0..data_count {
355 let val = read_f32(bytes, data_ptr + (i * 4))?;
356 controller_data.push(val);
357 }
358 }
359
360 let mut controllers = Vec::with_capacity(key_count);
361 if key_count > 0 && key_ptr > 0 {
362 for i in 0..key_count {
363 let key_offset = key_ptr + (i * 16);
364 if key_offset + 16 > bytes.len() {
365 crate::trace_warn!(
366 index = i,
367 key_offset,
368 bytes_len = bytes.len(),
369 "controller key header truncated"
370 );
371 break;
372 }
373
374 let type_code = read_u32(bytes, key_offset)?;
375 let controller_type = MdlControllerType::from(type_code);
376
377 let mut key_unknown_04 = [0u8; 2];
379 key_unknown_04.copy_from_slice(&bytes[key_offset + 4..key_offset + 6]);
380
381 let row_count = usize::from(read_u16(bytes, key_offset + 6)?);
382 let time_index = usize::from(read_u16(bytes, key_offset + 8)?);
383 let data_index = usize::from(read_u16(bytes, key_offset + 10)?);
384 let raw_column_count = read_u8(bytes, key_offset + 12)?;
385
386 let mut key_unknown_0d = [0u8; 3];
387 key_unknown_0d.copy_from_slice(&bytes[key_offset + 13..key_offset + 16]);
388
389 let actual_columns =
399 if controller_type == MdlControllerType::ORIENTATION && raw_column_count == 2 {
400 1
403 } else {
404 let base = usize::from(raw_column_count & 0x0F);
405 let has_bezier = (raw_column_count & super::controllers::CTRL_FLAG_BEZIER) != 0;
406 if has_bezier {
407 base * 3
408 } else {
409 base
410 }
411 };
412
413 let mut keys = Vec::with_capacity(row_count);
415
416 if time_index + row_count <= controller_data.len() {
417 for r in 0..row_count {
418 let time = controller_data[time_index + r];
419 let mut values = Vec::with_capacity(actual_columns);
420
421 let data_start = data_index + (r * actual_columns);
422 if data_start + actual_columns <= controller_data.len() {
423 for c in 0..actual_columns {
424 values.push(controller_data[data_start + c]);
425 }
426 } else {
427 crate::trace_warn!(
428 controller = ?controller_type,
429 row = r,
430 data_start,
431 actual_columns,
432 raw_column_count,
433 data_len = controller_data.len(),
434 "controller keyframe data out of bounds"
435 );
436 }
437 keys.push(MdlKey { time, values });
438 }
439 } else {
440 crate::trace_warn!(
441 controller = ?controller_type,
442 time_index,
443 row_count,
444 data_len = controller_data.len(),
445 "controller time indices out of bounds"
446 );
447 }
448
449 controllers.push(MdlController {
450 controller_type,
451 keys,
452 raw_column_count,
453 key_unknown_04,
454 key_unknown_0d,
455 });
456 }
457 }
458
459 let orphan_data = if controllers.is_empty() && !controller_data.is_empty() {
461 controller_data
462 } else {
463 Vec::new()
464 };
465
466 Ok(ControllerReadResult {
467 controllers,
468 orphan_data,
469 })
470}
471
472fn read_animations(
478 bytes: &[u8],
479 anim_arr_ptr: u32,
480 anim_arr_count: u32,
481 names: &[String],
482) -> Result<Vec<MdlAnimation>, MdlError> {
483 if anim_arr_count == 0 {
484 return Ok(Vec::new());
485 }
486
487 let arr_offset = checked_to_usize(anim_arr_ptr, "anim_arr_ptr")?;
488 let anim_count = checked_to_usize(anim_arr_count, "anim_count")?;
489 if arr_offset + (anim_count * 4) > bytes.len() {
490 return Err(MdlError::InvalidData(format!(
491 "animation offset array at {arr_offset} with {anim_arr_count} entries exceeds content"
492 )));
493 }
494
495 let mut anim_offsets = Vec::with_capacity(anim_count);
497 for i in 0..anim_count {
498 let off = checked_to_usize(read_u32(bytes, arr_offset + i * 4)?, "anim_header_offset")?;
499 anim_offsets.push(off);
500 }
501
502 let mut animations = Vec::with_capacity(anim_offsets.len());
503 for &offset in &anim_offsets {
504 animations.push(read_animation(bytes, offset, names)?);
505 }
506 Ok(animations)
507}
508
509fn read_animation(bytes: &[u8], offset: usize, names: &[String]) -> Result<MdlAnimation, MdlError> {
511 check_slice_in_bounds(bytes, offset, ANIMATION_HEADER_SIZE, "animation_header")?;
512
513 let fn_ptr1 = read_u32(bytes, offset + anim_header_offsets::FN_PTR1)?;
514 let fn_ptr2 = read_u32(bytes, offset + anim_header_offsets::FN_PTR2)?;
515
516 let name = read_fixed_string(
518 bytes,
519 offset + anim_header_offsets::NAME,
520 anim_header_offsets::NAME_SIZE,
521 );
522
523 let root_node_ptr = checked_to_usize(
524 read_u32(bytes, offset + anim_header_offsets::ROOT_NODE_PTR)?,
525 "anim_root_node_ptr",
526 )?;
527
528 let length = read_f32(bytes, offset + anim_header_offsets::LENGTH)?;
530 let transition_time = read_f32(bytes, offset + anim_header_offsets::TRANSITION)?;
531
532 let anim_root = read_fixed_string(
534 bytes,
535 offset + anim_header_offsets::ANIM_ROOT,
536 anim_header_offsets::ANIM_ROOT_SIZE,
537 );
538
539 let event_arr_ptr = read_u32(bytes, offset + anim_header_offsets::EVENT_ARR_PTR)?;
541 let event_arr_count = read_u32(bytes, offset + anim_header_offsets::EVENT_ARR_COUNT)?;
542
543 let events = read_anim_events(bytes, event_arr_ptr, event_arr_count)?;
544
545 let root_node = read_anim_node(bytes, root_node_ptr, names)?;
547
548 Ok(MdlAnimation {
549 name,
550 length,
551 transition_time,
552 anim_root,
553 events,
554 root_node,
555 fn_ptr1,
556 fn_ptr2,
557 })
558}
559
560fn read_anim_events(
562 bytes: &[u8],
563 event_arr_ptr: u32,
564 event_arr_count: u32,
565) -> Result<Vec<MdlAnimEvent>, MdlError> {
566 if event_arr_count == 0 {
567 return Ok(Vec::new());
568 }
569
570 let arr_offset = checked_to_usize(event_arr_ptr, "event_arr_ptr")?;
571 let event_count = checked_to_usize(event_arr_count, "event_count")?;
572 let total_size = event_count * ANIMATION_EVENT_SIZE;
573 if arr_offset + total_size > bytes.len() {
574 return Err(MdlError::InvalidData(format!(
575 "event array at {arr_offset} with {event_arr_count} events exceeds content"
576 )));
577 }
578
579 let mut events = Vec::with_capacity(event_count);
580 for i in 0..event_count {
581 let event_offset = arr_offset + i * ANIMATION_EVENT_SIZE;
582 let time = read_f32(bytes, event_offset)?;
583 let name = read_fixed_string(bytes, event_offset + 4, 32);
584 events.push(MdlAnimEvent { time, name });
585 }
586 Ok(events)
587}
588
589fn read_anim_node(bytes: &[u8], offset: usize, names: &[String]) -> Result<MdlAnimNode, MdlError> {
595 check_slice_in_bounds(bytes, offset, NODE_HEADER_SIZE, "anim_node_header")?;
596
597 let node_number = read_u16(bytes, offset + 0x02)?;
599
600 let name_index = usize::from(read_u16(bytes, offset + node_offsets::NAME_INDEX)?);
602 let name = if name_index < names.len() {
603 names[name_index].clone()
604 } else {
605 format!("AnimNode_{}", name_index)
606 };
607
608 let child_offset_ptr = checked_to_usize(
610 read_u32(bytes, offset + node_offsets::CHILD_ARRAY_PTR)?,
611 "anim_child_offset_ptr",
612 )?;
613 let child_count = checked_to_usize(
614 read_u32(bytes, offset + node_offsets::CHILD_COUNT)?,
615 "anim_child_count",
616 )?;
617
618 let ctrl_key_ptr = checked_to_usize(
620 read_u32(bytes, offset + node_offsets::CONTROLLER_KEY_PTR)?,
621 "anim_ctrl_key_ptr",
622 )?;
623 let ctrl_key_count = checked_to_usize(
624 read_u32(bytes, offset + node_offsets::CONTROLLER_KEY_COUNT)?,
625 "anim_ctrl_key_count",
626 )?;
627 let ctrl_data_ptr = checked_to_usize(
628 read_u32(bytes, offset + node_offsets::CONTROLLER_DATA_PTR)?,
629 "anim_ctrl_data_ptr",
630 )?;
631 let ctrl_data_count = checked_to_usize(
632 read_u32(bytes, offset + node_offsets::CONTROLLER_DATA_COUNT)?,
633 "anim_ctrl_data_count",
634 )?;
635
636 let ctrl_result = read_controllers(
638 bytes,
639 ctrl_key_ptr,
640 ctrl_key_count,
641 ctrl_data_ptr,
642 ctrl_data_count,
643 )?;
644
645 let mut children = Vec::with_capacity(child_count);
647 if child_count > 0 && child_offset_ptr > 0 && child_offset_ptr + child_count * 4 <= bytes.len()
648 {
649 for i in 0..child_count {
650 let child_ptr =
651 checked_to_usize(read_u32(bytes, child_offset_ptr + i * 4)?, "anim_child_ptr")?;
652 children.push(read_anim_node(bytes, child_ptr, names)?);
653 }
654 }
655
656 Ok(MdlAnimNode {
657 name,
658 node_number,
659 controllers: ctrl_result.controllers,
660 orphan_controller_data: ctrl_result.orphan_data,
661 children,
662 })
663}
664
665fn read_node(
670 bytes: &[u8],
671 offset: usize,
672 names: &[String],
673 parent_node_id: Option<u16>,
674 node_end_hint: Option<usize>,
675 mdx_infos: &mut Vec<MdxMeshInfo>,
676 dfs_mesh_counter: &mut usize,
677) -> Result<MdlNode, MdlError> {
678 check_slice_in_bounds(bytes, offset, NODE_HEADER_SIZE, "node_header")?;
680
681 let flags = u32::from(read_u16(bytes, offset + node_offsets::FLAGS)?);
682
683 let mut header_padding_02 = [0u8; 2];
685 header_padding_02.copy_from_slice(&bytes[offset + 0x02..offset + 0x04]);
686
687 let name_index = usize::from(read_u16(bytes, offset + node_offsets::NAME_INDEX)?);
688
689 let mut header_padding_06 = [0u8; 2];
690 header_padding_06.copy_from_slice(&bytes[offset + 0x06..offset + 0x08]);
691
692 let px = read_f32(bytes, offset + node_offsets::POS_X)?;
693 let py = read_f32(bytes, offset + node_offsets::POS_X + 4)?;
694 let pz = read_f32(bytes, offset + node_offsets::POS_X + 8)?;
695
696 let rw = read_f32(bytes, offset + node_offsets::ORIENTATION_W)?;
698 let rx = read_f32(bytes, offset + node_offsets::ORIENTATION_W + 4)?;
699 let ry = read_f32(bytes, offset + node_offsets::ORIENTATION_W + 8)?;
700 let rz = read_f32(bytes, offset + node_offsets::ORIENTATION_W + 12)?;
701
702 let child_offset_ptr = checked_to_usize(
703 read_u32(bytes, offset + node_offsets::CHILD_ARRAY_PTR)?,
704 "child_offset",
705 )?;
706 let child_count = checked_to_usize(
707 read_u32(bytes, offset + node_offsets::CHILD_COUNT)?,
708 "child_count",
709 )?;
710
711 let controller_key_ptr = checked_to_usize(
712 read_u32(bytes, offset + node_offsets::CONTROLLER_KEY_PTR)?,
713 "controller_key_ptr",
714 )?;
715 let controller_key_count = checked_to_usize(
716 read_u32(bytes, offset + node_offsets::CONTROLLER_KEY_COUNT)?,
717 "controller_key_count",
718 )?;
719
720 let controller_data_ptr = checked_to_usize(
721 read_u32(bytes, offset + node_offsets::CONTROLLER_DATA_PTR)?,
722 "controller_data_ptr",
723 )?;
724 let controller_data_count = checked_to_usize(
725 read_u32(bytes, offset + node_offsets::CONTROLLER_DATA_COUNT)?,
726 "controller_data_count",
727 )?;
728
729 let name = if name_index < names.len() {
730 names[name_index].clone()
731 } else {
732 format!("Node_{}", name_index)
733 };
734
735 let ctrl_result = read_controllers(
737 bytes,
738 controller_key_ptr,
739 controller_key_count,
740 controller_data_ptr,
741 controller_data_count,
742 )?;
743
744 let mut current_offset = offset + NODE_HEADER_SIZE; if (flags & node_flags::LIGHT) != 0 {
750 current_offset += LIGHT_EXTRA_SIZE;
751 }
752 if (flags & node_flags::EMITTER) != 0 {
753 current_offset += EMITTER_EXTRA_SIZE;
754 }
755 if (flags & node_flags::REFERENCE) != 0 {
759 current_offset += REFERENCE_EXTRA_SIZE;
760 }
761
762 let node_data = if (flags & node_flags::MESH) != 0 {
766 *dfs_mesh_counter += 1;
767
768 let mut node_data_end_hint = node_end_hint.unwrap_or(bytes.len());
769 for ptr in [child_offset_ptr, controller_key_ptr, controller_data_ptr] {
770 if ptr > 0 {
771 node_data_end_hint = node_data_end_hint.min(ptr);
772 }
773 }
774
775 let (mesh, info) = read_mesh_header(bytes, current_offset)?;
776 mdx_infos.push(info);
777
778 if (flags & node_flags::SABER) != 0 {
779 let saber = read_saber_extra(bytes, current_offset + MESH_EXTRA_SIZE, mesh)?;
780 MdlNodeData::Saber(saber)
781 } else if (flags & node_flags::AABB) != 0 {
782 let aabb = read_aabb_extra(
783 bytes,
784 current_offset + MESH_EXTRA_SIZE,
785 node_data_end_hint,
786 mesh,
787 )?;
788 MdlNodeData::Aabb(aabb)
789 } else if (flags & node_flags::DANGLY) != 0 {
790 let dangly = read_dangly_extra(bytes, current_offset + MESH_EXTRA_SIZE, mesh)?;
791 MdlNodeData::Dangly(dangly)
792 } else if (flags & node_flags::ANIM) != 0 {
793 let anim = read_anim_mesh_extra(bytes, current_offset + MESH_EXTRA_SIZE, mesh)?;
794 MdlNodeData::AnimMesh(anim)
795 } else if (flags & node_flags::SKIN) != 0 {
796 let skin = read_skin_extra(bytes, current_offset + MESH_EXTRA_SIZE, mesh)?;
797 if let Some(last_info) = mdx_infos.last_mut() {
800 last_info.bone_weights_off = skin.mdx_bone_weights_offset;
801 last_info.bone_indices_off = skin.mdx_bone_indices_offset;
802 }
803 MdlNodeData::Skin(skin)
804 } else {
805 MdlNodeData::Mesh(mesh)
806 }
807 } else if (flags & node_flags::LIGHT) != 0 {
808 MdlNodeData::Light(read_light_header(bytes, offset + NODE_HEADER_SIZE)?)
809 } else if (flags & node_flags::EMITTER) != 0 {
810 MdlNodeData::Emitter(read_emitter_header(bytes, offset + NODE_HEADER_SIZE)?)
811 } else if (flags & node_flags::CAMERA) != 0 {
812 MdlNodeData::Camera(MdlCamera::new())
813 } else if (flags & node_flags::REFERENCE) != 0 {
814 MdlNodeData::Reference(read_reference_header(bytes, offset + NODE_HEADER_SIZE)?)
815 } else {
816 MdlNodeData::Base
817 };
818
819 let mut children = Vec::with_capacity(child_count);
820 if child_count > 0 && child_offset_ptr > 0 {
821 let mut child_ptrs = Vec::with_capacity(child_count);
822 for i in 0..child_count {
823 let ptr_loc = child_offset_ptr + (i * 4);
824 let child_ptr = checked_to_usize(read_u32(bytes, ptr_loc)?, "child_ptr")?;
825 child_ptrs.push(child_ptr);
826 }
827
828 for (i, child_ptr) in child_ptrs.iter().copied().enumerate() {
829 let mut child_end_hint: Option<usize> = None;
830 let mut consider_end = |candidate: Option<usize>| {
831 if let Some(end) = candidate.filter(|end| *end > child_ptr) {
832 child_end_hint = Some(child_end_hint.map_or(end, |curr| curr.min(end)));
833 }
834 };
835 consider_end(child_ptrs.get(i + 1).copied());
836 consider_end((controller_key_ptr > 0).then_some(controller_key_ptr));
837 consider_end((controller_data_ptr > 0).then_some(controller_data_ptr));
838 consider_end(node_end_hint);
839
840 let child_node = read_node(
841 bytes,
842 child_ptr,
843 names,
844 u16::try_from(name_index).ok(),
845 child_end_hint,
846 mdx_infos,
847 dfs_mesh_counter,
848 )?;
849 children.push(child_node);
850 }
851 }
852
853 Ok(MdlNode {
854 name,
855 parent_index: parent_node_id,
856 children,
857 position: [px, py, pz],
858 rotation: [rw, rx, ry, rz],
859 node_data,
860 controllers: ctrl_result.controllers,
861 orphan_controller_data: ctrl_result.orphan_data,
862 header_padding_02,
863 header_padding_06,
864 })
865}
866
867fn read_mesh_header(bytes: &[u8], offset: usize) -> Result<(MdlMesh, MdxMeshInfo), MdlError> {
872 use super::types::MdlFace;
873
874 check_slice_in_bounds(bytes, offset, MESH_EXTRA_SIZE, "mesh_header")?;
876
877 let face_offset = checked_to_usize(
878 read_u32(bytes, offset + mesh_offsets::FACE_ARRAY_OFFSET)?,
879 "face_offset",
880 )?;
881 let face_count = checked_to_usize(
882 read_u32(bytes, offset + mesh_offsets::FACE_COUNT)?,
883 "face_count",
884 )?;
885
886 let vertex_indices_count_ptr = checked_to_usize(
890 read_u32(bytes, offset + mesh_offsets::VERTEX_INDICES_COUNT_ARRAY_PTR)?,
891 "vertex_indices_count_ptr",
892 )?;
893 let vertex_indices_count_count = read_u32(
894 bytes,
895 offset + mesh_offsets::VERTEX_INDICES_COUNT_ARRAY_COUNT,
896 )?;
897 let _vertex_indices_count_alloc = read_u32(
898 bytes,
899 offset + mesh_offsets::VERTEX_INDICES_COUNT_ARRAY_ALLOC,
900 )?;
901
902 let _mdx_offsets_count = read_u32(bytes, offset + mesh_offsets::MDX_OFFSETS_ARRAY_COUNT)?;
904 let _mdx_offsets_alloc = read_u32(bytes, offset + mesh_offsets::MDX_OFFSETS_ARRAY_ALLOC)?;
905
906 let index_buffer_pools_ptr = checked_to_usize(
907 read_u32(bytes, offset + mesh_offsets::INDEX_BUFFER_POOLS_ARRAY_PTR)?,
908 "index_buffer_pools_ptr",
909 )?;
910 let index_buffer_pools_count =
911 read_u32(bytes, offset + mesh_offsets::INDEX_BUFFER_POOLS_ARRAY_COUNT)?;
912 let _index_buffer_pools_alloc =
913 read_u32(bytes, offset + mesh_offsets::INDEX_BUFFER_POOLS_ARRAY_ALLOC)?;
914
915 let shared_index_offset = read_i32(bytes, offset + mesh_offsets::SHARED_INDEX_OFFSET)?;
916 let shared_index_pool = read_i32(bytes, offset + mesh_offsets::SHARED_INDEX_POOL)?;
917 let shared_index_size = read_i32(bytes, offset + mesh_offsets::SHARED_INDEX_SIZE)?;
918 let indices_per_face = read_u32(bytes, offset + mesh_offsets::INDICES_PER_FACE)?;
919
920 let vertex_count = usize::from(read_u16(bytes, offset + mesh_offsets::VERTEX_COUNT)?);
922 let mdx_data_offset = checked_to_usize(
923 read_u32(bytes, offset + mesh_offsets::MDX_DATA_OFFSET)?,
924 "mdx_data_offset",
925 )?;
926 let vert_array_offset = checked_to_usize(
927 read_u32(bytes, offset + mesh_offsets::VERT_ARRAY_OFFSET)?,
928 "vert_array_offset",
929 )?;
930 let vertex_stride = read_u32(bytes, offset + mesh_offsets::VERTEX_STRUCT_SIZE)?;
931
932 let render = read_u8(bytes, offset + mesh_offsets::RENDER)? != 0;
934 let shadow = read_u8(bytes, offset + mesh_offsets::SHADOW)? != 0;
935
936 let fn_ptr_gen_vertices = read_u32(bytes, offset + mesh_offsets::FN_PTR_GEN_VERTICES)?;
938 let fn_ptr_remove_temp_array =
939 read_u32(bytes, offset + mesh_offsets::FN_PTR_REMOVE_TEMP_ARRAY)?;
940
941 let bounding_min = [
943 read_f32(bytes, offset + mesh_offsets::BOUNDING_MIN)?,
944 read_f32(bytes, offset + mesh_offsets::BOUNDING_MIN + 4)?,
945 read_f32(bytes, offset + mesh_offsets::BOUNDING_MIN + 8)?,
946 ];
947 let bounding_max = [
948 read_f32(bytes, offset + mesh_offsets::BOUNDING_MAX)?,
949 read_f32(bytes, offset + mesh_offsets::BOUNDING_MAX + 4)?,
950 read_f32(bytes, offset + mesh_offsets::BOUNDING_MAX + 8)?,
951 ];
952 let bsphere_radius = read_f32(bytes, offset + mesh_offsets::BSPHERE_RADIUS)?;
953 let bsphere_center = [
954 read_f32(bytes, offset + mesh_offsets::BSPHERE_CENTER)?,
955 read_f32(bytes, offset + mesh_offsets::BSPHERE_CENTER + 4)?,
956 read_f32(bytes, offset + mesh_offsets::BSPHERE_CENTER + 8)?,
957 ];
958
959 let diffuse_color = [
961 read_f32(bytes, offset + mesh_offsets::DIFFUSE_COLOR)?,
962 read_f32(bytes, offset + mesh_offsets::DIFFUSE_COLOR + 4)?,
963 read_f32(bytes, offset + mesh_offsets::DIFFUSE_COLOR + 8)?,
964 ];
965 let ambient_color = [
966 read_f32(bytes, offset + mesh_offsets::AMBIENT_COLOR)?,
967 read_f32(bytes, offset + mesh_offsets::AMBIENT_COLOR + 4)?,
968 read_f32(bytes, offset + mesh_offsets::AMBIENT_COLOR + 8)?,
969 ];
970 let transparency_hint = read_i32(bytes, offset + mesh_offsets::TRANSPARENCY_HINT)?;
971
972 let texture_0 = read_fixed_string(
974 bytes,
975 offset + mesh_offsets::TEXTURE_0,
976 mesh_offsets::TEXTURE_NAME_SIZE,
977 );
978 let texture_1 = read_fixed_string(
979 bytes,
980 offset + mesh_offsets::TEXTURE_1,
981 mesh_offsets::TEXTURE_NAME_SIZE,
982 );
983
984 let animate_uv = read_i32(bytes, offset + mesh_offsets::ANIMATE_UV)?;
986 let uv_direction_x = read_f32(bytes, offset + mesh_offsets::UV_DIRECTION_X)?;
987 let uv_direction_y = read_f32(bytes, offset + mesh_offsets::UV_DIRECTION_Y)?;
988 let uv_jitter = read_f32(bytes, offset + mesh_offsets::UV_JITTER)?;
989 let uv_jitter_speed = read_f32(bytes, offset + mesh_offsets::UV_JITTER_SPEED)?;
990
991 let texture_channel_count = read_u16(bytes, offset + mesh_offsets::TEXTURE_CHANNEL_COUNT)?;
993 let light_mapped = read_u8(bytes, offset + mesh_offsets::LIGHT_MAPPED)? != 0;
994 let rotate_texture = read_u8(bytes, offset + mesh_offsets::ROTATE_TEXTURE)? != 0;
995 let is_background_geometry =
996 read_u8(bytes, offset + mesh_offsets::IS_BACKGROUND_GEOMETRY)? != 0;
997 let beaming = read_u8(bytes, offset + mesh_offsets::BEAMING)? != 0;
998 let total_surface_area = read_f32(bytes, offset + mesh_offsets::TOTAL_SURFACE_AREA)?;
999
1000 let mut faces = Vec::with_capacity(face_count);
1002 if face_count > 0 && face_offset > 0 {
1003 if face_offset + (face_count * MAX_FACE_SIZE) <= bytes.len() {
1004 for i in 0..face_count {
1005 let curr = face_offset + (i * MAX_FACE_SIZE);
1006 faces.push(MdlFace {
1007 plane_normal: [
1008 read_f32(bytes, curr)?,
1009 read_f32(bytes, curr + 4)?,
1010 read_f32(bytes, curr + 8)?,
1011 ],
1012 plane_distance: read_f32(bytes, curr + 12)?,
1013 surface_id: read_u32(bytes, curr + 16)?,
1014 adjacent: [
1015 read_u16(bytes, curr + 20)?,
1016 read_u16(bytes, curr + 22)?,
1017 read_u16(bytes, curr + 24)?,
1018 ],
1019 vertex_indices: [
1020 read_u16(bytes, curr + 26)?,
1021 read_u16(bytes, curr + 28)?,
1022 read_u16(bytes, curr + 30)?,
1023 ],
1024 });
1025 }
1026 } else {
1027 crate::trace_warn!(
1028 face_offset,
1029 face_count,
1030 bytes_len = bytes.len(),
1031 "face array extends beyond buffer, skipping faces"
1032 );
1033 }
1034 }
1035
1036 let inverted_counter = if index_buffer_pools_count > 0 && index_buffer_pools_ptr > 0 {
1044 if index_buffer_pools_ptr + 4 <= bytes.len() {
1045 read_u32(bytes, index_buffer_pools_ptr)?
1046 } else {
1047 0
1048 }
1049 } else {
1050 0
1051 };
1052
1053 let has_embedded_positions = vertex_indices_count_count == 1
1055 && vertex_indices_count_ptr > 0
1056 && vert_array_offset == vertex_indices_count_ptr.saturating_add(4);
1057
1058 let pos_off = read_i32(bytes, offset + mesh_offsets::MDX_POSITION_OFFSET)?;
1061 let norm_off = read_i32(bytes, offset + mesh_offsets::MDX_NORMAL_OFFSET)?;
1062 let color_off = read_i32(bytes, offset + mesh_offsets::MDX_COLOR_OFFSET)?;
1063 let uv1_off = read_i32(bytes, offset + mesh_offsets::MDX_UV1_OFFSET)?;
1064 let uv2_off = read_i32(bytes, offset + mesh_offsets::MDX_UV2_OFFSET)?;
1065 let uv3_off = read_i32(bytes, offset + mesh_offsets::MDX_UV3_OFFSET)?;
1066 let uv4_off = read_i32(bytes, offset + mesh_offsets::MDX_UV4_OFFSET)?;
1067 let tangent_off = read_i32(bytes, offset + mesh_offsets::MDX_TANGENT_SPACE_OFFSET)?;
1068
1069 let info = MdxMeshInfo {
1070 stride: vertex_stride,
1071 vertex_count,
1072 mdx_data_offset,
1073 vert_array_offset,
1074 pos_off,
1075 norm_off,
1076 color_off,
1077 uv1_off,
1078 uv2_off,
1079 uv3_off,
1080 uv4_off,
1081 tangent_off,
1082 bone_weights_off: -1,
1083 bone_indices_off: -1,
1084 };
1085
1086 let mesh = MdlMesh {
1087 fn_ptr_gen_vertices,
1088 fn_ptr_remove_temp_array,
1089 bounding_min,
1090 bounding_max,
1091 bsphere_radius,
1092 bsphere_center,
1093 diffuse_color,
1094 ambient_color,
1095 transparency_hint,
1096 texture_0,
1097 texture_1,
1098 animate_uv,
1099 uv_direction_x,
1100 uv_direction_y,
1101 uv_jitter,
1102 uv_jitter_speed,
1103 texture_channel_count,
1104 light_mapped,
1105 rotate_texture,
1106 is_background_geometry,
1107 beaming,
1108 total_surface_area,
1109 positions: Vec::new(),
1110 normals: Vec::new(),
1111 vertex_colors: Vec::new(),
1112 uv1: Vec::new(),
1113 uv2: Vec::new(),
1114 uv3: Vec::new(),
1115 uv4: Vec::new(),
1116 tangent_space: Vec::new(),
1117 faces,
1118 inverted_counter,
1119 has_embedded_positions,
1120 shared_index_offset,
1121 shared_index_pool,
1122 shared_index_size,
1123 indices_per_face,
1124 vertex_count: u16::try_from(vertex_count)
1125 .map_err(|_| MdlError::ValueOverflow("vertex_count"))?,
1126 render,
1127 shadow,
1128 };
1129
1130 Ok((mesh, info))
1131}
1132
1133fn populate_mdx_data(
1145 root: &mut MdlNode,
1146 mdx: &[u8],
1147 infos: &[MdxMeshInfo],
1148) -> Result<(), MdlError> {
1149 if infos.is_empty() {
1150 return Ok(());
1151 }
1152
1153 let mut results: Vec<MdxVertexData> =
1155 (0..infos.len()).map(|_| MdxVertexData::default()).collect();
1156
1157 for (idx, info) in infos.iter().enumerate() {
1158 let data = read_mdx_vertices(mdx, info)?;
1159 results[idx] = data;
1160 }
1161
1162 let mut counter = 0;
1164 apply_vertex_data(root, &mut results, &mut counter);
1165
1166 Ok(())
1167}
1168
1169fn read_mdx_vertices(mdx: &[u8], info: &MdxMeshInfo) -> Result<MdxVertexData, MdlError> {
1172 let mut data = MdxVertexData::default();
1173
1174 if info.vertex_count == 0 || info.stride == 0 {
1175 return Ok(data);
1176 }
1177
1178 let stride = checked_to_usize(info.stride, "vertex_stride")?;
1179 let mdx_base = info.mdx_data_offset;
1180
1181 for i in 0..info.vertex_count {
1182 let base = mdx_base + (i * stride);
1183 if base + stride > mdx.len() {
1184 crate::trace_warn!(
1185 vertex_index = i,
1186 base,
1187 stride,
1188 mdx_len = mdx.len(),
1189 mdx_cursor = mdx_base,
1190 "MDX vertex data truncated"
1191 );
1192 break;
1193 }
1194
1195 if let Ok(off) = usize::try_from(info.pos_off) {
1197 let o = base + off;
1198 data.positions.push([
1199 read_f32(mdx, o)?,
1200 read_f32(mdx, o + 4)?,
1201 read_f32(mdx, o + 8)?,
1202 ]);
1203 }
1204
1205 if let Ok(off) = usize::try_from(info.norm_off) {
1207 let o = base + off;
1208 data.normals.push([
1209 read_f32(mdx, o)?,
1210 read_f32(mdx, o + 4)?,
1211 read_f32(mdx, o + 8)?,
1212 ]);
1213 }
1214
1215 if let Ok(off) = usize::try_from(info.color_off) {
1217 let o = base + off;
1218 data.vertex_colors.push([
1219 read_u8(mdx, o)?,
1220 read_u8(mdx, o + 1)?,
1221 read_u8(mdx, o + 2)?,
1222 read_u8(mdx, o + 3)?,
1223 ]);
1224 }
1225
1226 if let Ok(off) = usize::try_from(info.uv1_off) {
1228 let o = base + off;
1229 data.uv1.push([read_f32(mdx, o)?, read_f32(mdx, o + 4)?]);
1230 }
1231
1232 if let Ok(off) = usize::try_from(info.uv2_off) {
1234 let o = base + off;
1235 data.uv2.push([read_f32(mdx, o)?, read_f32(mdx, o + 4)?]);
1236 }
1237
1238 if let Ok(off) = usize::try_from(info.uv3_off) {
1240 let o = base + off;
1241 data.uv3.push([read_f32(mdx, o)?, read_f32(mdx, o + 4)?]);
1242 }
1243
1244 if let Ok(off) = usize::try_from(info.uv4_off) {
1246 let o = base + off;
1247 data.uv4.push([read_f32(mdx, o)?, read_f32(mdx, o + 4)?]);
1248 }
1249
1250 if let Ok(off) = usize::try_from(info.tangent_off) {
1252 let o = base + off;
1253 data.tangent_space.push([
1254 [
1255 read_f32(mdx, o)?,
1256 read_f32(mdx, o + 4)?,
1257 read_f32(mdx, o + 8)?,
1258 ],
1259 [
1260 read_f32(mdx, o + 12)?,
1261 read_f32(mdx, o + 16)?,
1262 read_f32(mdx, o + 20)?,
1263 ],
1264 [
1265 read_f32(mdx, o + 24)?,
1266 read_f32(mdx, o + 28)?,
1267 read_f32(mdx, o + 32)?,
1268 ],
1269 ]);
1270 }
1271
1272 if let Ok(off) = usize::try_from(info.bone_weights_off) {
1274 let o = base + off;
1275 data.bone_weights.push([
1276 read_f32(mdx, o)?,
1277 read_f32(mdx, o + 4)?,
1278 read_f32(mdx, o + 8)?,
1279 read_f32(mdx, o + 12)?,
1280 ]);
1281 }
1282
1283 if let Ok(off) = usize::try_from(info.bone_indices_off) {
1285 let o = base + off;
1286 data.bone_indices.push([
1287 read_f32(mdx, o)?,
1288 read_f32(mdx, o + 4)?,
1289 read_f32(mdx, o + 8)?,
1290 read_f32(mdx, o + 12)?,
1291 ]);
1292 }
1293 }
1294
1295 let actual_vertex_count = data
1302 .positions
1303 .len()
1304 .max(data.normals.len())
1305 .max(data.vertex_colors.len())
1306 .max(data.uv1.len())
1307 .max(data.uv2.len())
1308 .max(data.uv3.len())
1309 .max(data.uv4.len())
1310 .max(data.tangent_space.len());
1311
1312 if actual_vertex_count > 0 && actual_vertex_count < info.vertex_count {
1313 data.clamped_vertex_count = Some(
1314 u16::try_from(actual_vertex_count)
1315 .map_err(|_| MdlError::ValueOverflow("actual_vertex_count"))?,
1316 );
1317 }
1318
1319 Ok(data)
1320}
1321
1322fn populate_content_positions(
1329 root: &mut MdlNode,
1330 bytes: &[u8],
1331 infos: &[MdxMeshInfo],
1332) -> Result<(), MdlError> {
1333 if infos.is_empty() {
1334 return Ok(());
1335 }
1336
1337 let mut results: Vec<MdxVertexData> =
1339 (0..infos.len()).map(|_| MdxVertexData::default()).collect();
1340
1341 for (idx, info) in infos.iter().enumerate() {
1342 if info.vertex_count == 0 || info.vert_array_offset == 0 {
1343 continue;
1344 }
1345
1346 let pos_end = info.vert_array_offset + (info.vertex_count * 12);
1347 if pos_end <= bytes.len() {
1348 let mut positions = Vec::with_capacity(info.vertex_count);
1349 for i in 0..info.vertex_count {
1350 let o = info.vert_array_offset + (i * 12);
1351 positions.push([
1352 read_f32(bytes, o)?,
1353 read_f32(bytes, o + 4)?,
1354 read_f32(bytes, o + 8)?,
1355 ]);
1356 }
1357 results[idx].positions = positions;
1358 } else {
1359 crate::trace_warn!(
1360 vert_array_offset = info.vert_array_offset,
1361 vertex_count = info.vertex_count,
1362 pos_end,
1363 bytes_len = bytes.len(),
1364 "MDL content position data extends beyond buffer"
1365 );
1366 }
1367 }
1368
1369 let mut counter = 0;
1371 apply_vertex_data(root, &mut results, &mut counter);
1372
1373 Ok(())
1374}
1375
1376fn populate_content_positions_fallback(
1384 root: &mut MdlNode,
1385 bytes: &[u8],
1386 infos: &[MdxMeshInfo],
1387) -> Result<(), MdlError> {
1388 if infos.is_empty() {
1389 return Ok(());
1390 }
1391
1392 let mut counter = 0;
1393 fill_missing_positions(root, bytes, infos, &mut counter)?;
1394 Ok(())
1395}
1396
1397fn fill_missing_positions(
1400 node: &mut MdlNode,
1401 bytes: &[u8],
1402 infos: &[MdxMeshInfo],
1403 counter: &mut usize,
1404) -> Result<(), MdlError> {
1405 if node.node_data.mesh().is_some() {
1406 let idx = *counter;
1407 *counter += 1;
1408
1409 if let (Some(info), Some(mesh)) = (infos.get(idx), node.node_data.mesh_mut()) {
1410 if mesh.positions.is_empty() && info.vertex_count > 0 && info.vert_array_offset > 0 {
1411 let pos_end = info.vert_array_offset + (info.vertex_count * 12);
1412 if pos_end <= bytes.len() {
1413 let mut positions = Vec::with_capacity(info.vertex_count);
1414 for i in 0..info.vertex_count {
1415 let o = info.vert_array_offset + (i * 12);
1416 positions.push([
1417 read_f32(bytes, o)?,
1418 read_f32(bytes, o + 4)?,
1419 read_f32(bytes, o + 8)?,
1420 ]);
1421 }
1422 mesh.positions = positions;
1423 } else {
1424 crate::trace_warn!(
1425 vert_array_offset = info.vert_array_offset,
1426 vertex_count = info.vertex_count,
1427 pos_end,
1428 bytes_len = bytes.len(),
1429 "fallback content position data extends beyond buffer"
1430 );
1431 }
1432 }
1433 }
1434 }
1435
1436 for child in &mut node.children {
1437 fill_missing_positions(child, bytes, infos, counter)?;
1438 }
1439
1440 Ok(())
1441}
1442
1443fn apply_vertex_data(node: &mut MdlNode, results: &mut Vec<MdxVertexData>, counter: &mut usize) {
1447 if node.node_data.mesh().is_some() {
1448 let idx = *counter;
1449 *counter += 1;
1450
1451 if idx < results.len() {
1452 let mut data = std::mem::take(&mut results[idx]);
1453 let bone_weights = std::mem::take(&mut data.bone_weights);
1454 let bone_indices = std::mem::take(&mut data.bone_indices);
1455
1456 if let Some(mesh) = node.node_data.mesh_mut() {
1457 mesh.positions = data.positions;
1458 mesh.normals = data.normals;
1459 mesh.vertex_colors = data.vertex_colors;
1460 mesh.uv1 = data.uv1;
1461 mesh.uv2 = data.uv2;
1462 mesh.uv3 = data.uv3;
1463 mesh.uv4 = data.uv4;
1464 mesh.tangent_space = data.tangent_space;
1465 if let Some(clamped) = data.clamped_vertex_count {
1466 mesh.vertex_count = clamped;
1467 }
1468 }
1469
1470 if let MdlNodeData::Skin(skin) = &mut node.node_data {
1473 skin.bone_weights = bone_weights;
1474 skin.bone_indices = bone_indices;
1475 }
1476 }
1477 }
1478
1479 for child in &mut node.children {
1480 apply_vertex_data(child, results, counter);
1481 }
1482}
1483
1484fn read_fixed_string(bytes: &[u8], offset: usize, max_len: usize) -> String {
1490 crate::binary::read_fixed_c_string(bytes, offset, max_len)
1491}
1492
1493fn read_cstring(bytes: &[u8], offset: usize) -> String {
1495 crate::binary::read_c_string(bytes, offset)
1496}
1497
1498fn read_cexo_array<T>(
1504 bytes: &[u8],
1505 offset: usize,
1506 ptr_field: usize,
1507 count_field: usize,
1508 element_size: usize,
1509 label: &'static str,
1510 reader: impl FnOnce(&[u8], usize, usize) -> Result<Vec<T>, MdlError>,
1511) -> Result<Vec<T>, MdlError> {
1512 let ptr = checked_to_usize(read_u32(bytes, offset + ptr_field)?, label)?;
1513 let count = checked_to_usize(read_u32(bytes, offset + count_field)?, label)?;
1514
1515 if count == 0 || ptr == 0 {
1516 return Ok(Vec::new());
1517 }
1518
1519 let byte_size = count * element_size;
1520 if ptr + byte_size > bytes.len() {
1521 crate::trace_warn!(
1522 ptr,
1523 count,
1524 bytes_len = bytes.len(),
1525 label,
1526 "CExoArrayList extends beyond buffer"
1527 );
1528 return Ok(Vec::new());
1529 }
1530
1531 reader(bytes, ptr, count)
1532}
1533
1534fn read_f32_array(bytes: &[u8], ptr: usize, count: usize) -> Result<Vec<f32>, MdlError> {
1536 let mut result = Vec::with_capacity(count);
1537 for i in 0..count {
1538 result.push(read_f32(bytes, ptr + i * 4)?);
1539 }
1540 Ok(result)
1541}
1542
1543fn read_vec3_array(bytes: &[u8], ptr: usize, count: usize) -> Result<Vec<[f32; 3]>, MdlError> {
1545 let mut result = Vec::with_capacity(count);
1546 for i in 0..count {
1547 let base = ptr + i * 12;
1548 result.push([
1549 read_f32(bytes, base)?,
1550 read_f32(bytes, base + 4)?,
1551 read_f32(bytes, base + 8)?,
1552 ]);
1553 }
1554 Ok(result)
1555}
1556
1557fn read_quat_array(bytes: &[u8], ptr: usize, count: usize) -> Result<Vec<[f32; 4]>, MdlError> {
1559 let mut result = Vec::with_capacity(count);
1560 for i in 0..count {
1561 let base = ptr + i * 16;
1562 result.push([
1563 read_f32(bytes, base)?,
1564 read_f32(bytes, base + 4)?,
1565 read_f32(bytes, base + 8)?,
1566 read_f32(bytes, base + 12)?,
1567 ]);
1568 }
1569 Ok(result)
1570}
1571
1572fn read_reference_header(bytes: &[u8], offset: usize) -> Result<MdlReference, MdlError> {
1577 check_slice_in_bounds(bytes, offset, REFERENCE_EXTRA_SIZE, "reference_header")?;
1578
1579 let ref_model = read_fixed_string(bytes, offset, 32);
1580 let reattachable = read_i32(bytes, offset + 0x20)?;
1581
1582 Ok(MdlReference {
1583 ref_model,
1584 reattachable,
1585 })
1586}
1587
1588fn read_light_header(bytes: &[u8], offset: usize) -> Result<MdlLight, MdlError> {
1600 check_slice_in_bounds(bytes, offset, LIGHT_EXTRA_SIZE, "light_header")?;
1601
1602 let flare_radius = read_f32(bytes, offset + light_offsets::FLARE_RADIUS)?;
1603
1604 let sp_base = offset + light_offsets::TEXTURE_SAFE_PTRS_PTR;
1606 let texture_safe_ptrs = [
1607 read_u32(bytes, sp_base)?,
1608 read_u32(bytes, sp_base + 4)?,
1609 read_u32(bytes, sp_base + 8)?,
1610 ];
1611
1612 let flare_sizes = read_cexo_array(
1614 bytes,
1615 offset,
1616 light_offsets::FLARE_SIZES_PTR,
1617 light_offsets::FLARE_SIZES_COUNT,
1618 4,
1619 "flare_sizes",
1620 read_f32_array,
1621 )?;
1622
1623 let flare_positions = read_cexo_array(
1625 bytes,
1626 offset,
1627 light_offsets::FLARE_POSITIONS_PTR,
1628 light_offsets::FLARE_POSITIONS_COUNT,
1629 4,
1630 "flare_positions",
1631 read_f32_array,
1632 )?;
1633
1634 let flare_color_shifts = read_cexo_array(
1636 bytes,
1637 offset,
1638 light_offsets::FLARE_COLOR_SHIFTS_PTR,
1639 light_offsets::FLARE_COLOR_SHIFTS_COUNT,
1640 12,
1641 "flare_color_shifts",
1642 read_vec3_array,
1643 )?;
1644
1645 let flare_texture_names = read_cexo_array(
1648 bytes,
1649 offset,
1650 light_offsets::FLARE_TEX_NAMES_PTR,
1651 light_offsets::FLARE_TEX_NAMES_COUNT,
1652 4,
1653 "flare_tex_names",
1654 |b, ptr, count| {
1655 let mut names = Vec::with_capacity(count);
1656 for i in 0..count {
1657 let str_offset =
1658 checked_to_usize(read_u32(b, ptr + i * 4)?, "flare_tex_name_str_ptr")?;
1659 if str_offset > 0 && str_offset < b.len() {
1660 names.push(read_cstring(b, str_offset));
1661 } else {
1662 names.push(String::new());
1663 }
1664 }
1665 Ok(names)
1666 },
1667 )?;
1668
1669 let priority = read_i32(bytes, offset + light_offsets::PRIORITY)?;
1670 let num_dynamic_types = read_i32(bytes, offset + light_offsets::NUM_DYNAMIC_TYPES)?;
1671 let affectdynamic = read_i32(bytes, offset + light_offsets::AFFECTDYNAMIC)?;
1672 let shadow = read_i32(bytes, offset + light_offsets::SHADOW)?;
1673 let ambientonly = read_i32(bytes, offset + light_offsets::AMBIENTONLY)?;
1674 let generateflare = read_i32(bytes, offset + light_offsets::GENERATEFLARE)?;
1675 let fading_light = read_i32(bytes, offset + light_offsets::FADING_LIGHT)?;
1676
1677 Ok(MdlLight {
1678 flare_radius,
1679 texture_safe_ptrs,
1680 flare_sizes,
1681 flare_positions,
1682 flare_color_shifts,
1683 flare_texture_names,
1684 priority,
1685 num_dynamic_types,
1686 affectdynamic,
1687 shadow,
1688 ambientonly,
1689 generateflare,
1690 fading_light,
1691 })
1692}
1693
1694fn read_emitter_header(bytes: &[u8], offset: usize) -> Result<super::types::MdlEmitter, MdlError> {
1699 check_slice_in_bounds(bytes, offset, EMITTER_EXTRA_SIZE, "emitter_header")?;
1700
1701 let deadspace = read_f32(bytes, offset)?;
1702 let blast_radius = read_f32(bytes, offset + 0x04)?;
1703 let blast_length = read_f32(bytes, offset + 0x08)?;
1704 let num_branches = read_i32(bytes, offset + 0x0C)?;
1705 let control_pt_smoothing = read_i32(bytes, offset + 0x10)?;
1706 let x_grid = read_i32(bytes, offset + 0x14)?;
1707 let y_grid = read_i32(bytes, offset + 0x18)?;
1708 let spawn_type = read_i32(bytes, offset + 0x1C)?;
1709
1710 let update = read_fixed_string(bytes, offset + 0x20, 32);
1711 let render = read_fixed_string(bytes, offset + 0x40, 32);
1712 let blend = read_fixed_string(bytes, offset + 0x60, 32);
1713 let texture = read_fixed_string(bytes, offset + 0x80, 32);
1714 let chunk_name = read_fixed_string(bytes, offset + 0xA0, 16);
1715
1716 let two_sided_tex = read_i32(bytes, offset + 0xB0)?;
1717 let loop_emitter = read_i32(bytes, offset + 0xB4)?;
1718 let render_order = read_u16(bytes, offset + 0xB8)?;
1719 let frame_blending = read_u8(bytes, offset + 0xBA)? != 0;
1720 let depth_texture_name = read_fixed_string(bytes, offset + 0xBB, 16);
1721
1722 let mut reserved = [0u8; 21];
1724 reserved.copy_from_slice(&bytes[offset + 0xCB..offset + 0xE0]);
1725
1726 Ok(super::types::MdlEmitter {
1727 deadspace,
1728 blast_radius,
1729 blast_length,
1730 num_branches,
1731 control_pt_smoothing,
1732 x_grid,
1733 y_grid,
1734 spawn_type,
1735 update,
1736 render,
1737 blend,
1738 texture,
1739 chunk_name,
1740 two_sided_tex,
1741 loop_emitter,
1742 render_order,
1743 frame_blending,
1744 depth_texture_name,
1745 reserved,
1746 })
1747}
1748
1749fn read_dangly_extra(bytes: &[u8], offset: usize, mesh: MdlMesh) -> Result<MdlDangly, MdlError> {
1760 check_slice_in_bounds(bytes, offset, DANGLY_EXTRA_SIZE, "dangly_extra")?;
1761
1762 let constraints = read_cexo_array(
1764 bytes,
1765 offset,
1766 dangly_offsets::CONSTRAINTS_PTR,
1767 dangly_offsets::CONSTRAINTS_COUNT,
1768 4,
1769 "dangly_constraints",
1770 read_f32_array,
1771 )?;
1772
1773 let displacement = read_f32(bytes, offset + dangly_offsets::DISPLACEMENT)?;
1774 let tightness = read_f32(bytes, offset + dangly_offsets::TIGHTNESS)?;
1775 let period = read_f32(bytes, offset + dangly_offsets::PERIOD)?;
1776
1777 let dangly_verts_ptr = checked_to_usize(
1780 read_u32(bytes, offset + dangly_offsets::DATA_PTR)?,
1781 "dangly_verts_ptr",
1782 )?;
1783
1784 let vert_count = usize::from(mesh.vertex_count);
1786 let mut dangly_vertices = Vec::with_capacity(vert_count);
1787 if vert_count > 0 && dangly_verts_ptr > 0 {
1788 let required = vert_count * 12;
1789 if dangly_verts_ptr + required <= bytes.len() {
1790 dangly_vertices = read_vec3_array(bytes, dangly_verts_ptr, vert_count)?;
1791 } else {
1792 crate::trace_warn!(
1793 dangly_verts_ptr,
1794 vert_count,
1795 bytes_len = bytes.len(),
1796 "dangly vertices array extends beyond buffer"
1797 );
1798 }
1799 }
1800
1801 Ok(MdlDangly {
1802 mesh,
1803 constraints,
1804 displacement,
1805 tightness,
1806 period,
1807 dangly_vertices,
1808 })
1809}
1810
1811fn read_skin_extra(bytes: &[u8], offset: usize, mesh: MdlMesh) -> Result<MdlSkin, MdlError> {
1821 check_slice_in_bounds(bytes, offset, SKIN_EXTRA_SIZE, "skin_extra")?;
1822
1823 let mdx_bone_weights_offset = read_i32(bytes, offset + skin_offsets::MDX_BONE_WEIGHTS_OFFSET)?;
1830 let mdx_bone_indices_offset = read_i32(bytes, offset + skin_offsets::MDX_BONE_INDICES_OFFSET)?;
1831
1832 let bonemap_ptr = checked_to_usize(
1834 read_u32(bytes, offset + skin_offsets::BONEMAP_PTR)?,
1835 "skin_bonemap_ptr",
1836 )?;
1837 let bonemap_count = read_u32(bytes, offset + skin_offsets::BONEMAP_COUNT)?;
1838
1839 let mut bonemap = Vec::new();
1840 if bonemap_count > 0 && bonemap_ptr > 0 {
1841 let entry_count = checked_to_usize(bonemap_count, "bonemap_count")?;
1842 let byte_len = entry_count.checked_mul(4).unwrap_or(0);
1843 if byte_len > 0 && bonemap_ptr + byte_len <= bytes.len() {
1844 bonemap.reserve(entry_count);
1845 for i in 0..entry_count {
1846 bonemap.push(read_u32(bytes, bonemap_ptr + i * 4)?);
1847 }
1848 } else {
1849 crate::trace_warn!(
1850 bonemap_ptr,
1851 bonemap_count,
1852 bytes_len = bytes.len(),
1853 "skin bonemap extends beyond buffer"
1854 );
1855 }
1856 }
1857
1858 let qbone_ref_inv = read_cexo_array(
1860 bytes,
1861 offset,
1862 skin_offsets::QBONE_REF_INV_PTR,
1863 skin_offsets::QBONE_REF_INV_COUNT,
1864 16,
1865 "skin_qbone_ref_inv",
1866 read_quat_array,
1867 )?;
1868
1869 let tbone_ref_inv = read_cexo_array(
1871 bytes,
1872 offset,
1873 skin_offsets::TBONE_REF_INV_PTR,
1874 skin_offsets::TBONE_REF_INV_COUNT,
1875 12,
1876 "skin_tbone_ref_inv",
1877 read_vec3_array,
1878 )?;
1879
1880 let bone_constant_indices = read_cexo_array(
1882 bytes,
1883 offset,
1884 skin_offsets::BONE_CONSTANT_INDICES_PTR,
1885 skin_offsets::BONE_CONSTANT_INDICES_COUNT,
1886 4,
1887 "skin_bone_constant_indices",
1888 |b, ptr, count| {
1889 let mut indices = Vec::with_capacity(count);
1890 for i in 0..count {
1891 indices.push(read_i32(b, ptr + (i * 4))?);
1892 }
1893 Ok(indices)
1894 },
1895 )?;
1896
1897 let mut bone_node_numbers = [0u16; 16];
1899 let bnn_offset = offset + skin_offsets::BONE_NODE_NUMBERS;
1900 for (i, slot) in bone_node_numbers.iter_mut().enumerate() {
1901 *slot = read_u16(bytes, bnn_offset + i * 2)?;
1902 }
1903
1904 Ok(MdlSkin {
1908 mesh,
1909 mdx_bone_weights_offset,
1910 mdx_bone_indices_offset,
1911 bone_weights: Vec::new(),
1912 bone_indices: Vec::new(),
1913 bonemap,
1914 qbone_ref_inv,
1915 tbone_ref_inv,
1916 bone_constant_indices,
1917 bone_node_numbers,
1918 })
1919}
1920
1921fn read_anim_mesh_extra(
1932 bytes: &[u8],
1933 offset: usize,
1934 mesh: MdlMesh,
1935) -> Result<MdlAnimMesh, MdlError> {
1936 check_slice_in_bounds(bytes, offset, ANIM_MESH_EXTRA_SIZE, "anim_mesh_extra")?;
1937
1938 let sample_period = read_f32(bytes, offset + anim_mesh_offsets::SAMPLE_PERIOD)?;
1939
1940 let anim_verts = read_cexo_array(
1942 bytes,
1943 offset,
1944 anim_mesh_offsets::ANIM_VERTS_PTR,
1945 anim_mesh_offsets::ANIM_VERTS_COUNT,
1946 12,
1947 "anim_verts",
1948 read_vec3_array,
1949 )?;
1950
1951 let anim_t_verts = read_cexo_array(
1953 bytes,
1954 offset,
1955 anim_mesh_offsets::ANIM_T_VERTS_PTR,
1956 anim_mesh_offsets::ANIM_T_VERTS_COUNT,
1957 12,
1958 "anim_t_verts",
1959 read_vec3_array,
1960 )?;
1961
1962 let data_ptr_1 = read_u32(bytes, offset + anim_mesh_offsets::DATA_PTR_1)?;
1965 let data_count_1 = read_u32(bytes, offset + anim_mesh_offsets::DATA_COUNT_1)?;
1966 let padding_24 = read_u32(bytes, offset + anim_mesh_offsets::PADDING_24)?;
1967 let anim_vertices_ptr = read_u32(bytes, offset + anim_mesh_offsets::ANIM_VERTICES_PTR)?;
1968 let anim_tex_vertices_ptr = read_u32(bytes, offset + anim_mesh_offsets::ANIM_TEX_VERTICES_PTR)?;
1969 let anim_vertices_count_val = read_u32(bytes, offset + anim_mesh_offsets::ANIM_VERTICES_COUNT)?;
1970 let anim_tex_vertices_count =
1971 read_u32(bytes, offset + anim_mesh_offsets::ANIM_TEX_VERTICES_COUNT)?;
1972
1973 Ok(MdlAnimMesh {
1974 mesh,
1975 sample_period,
1976 anim_verts,
1977 anim_t_verts,
1978 data_ptr_1,
1979 data_count_1,
1980 padding_24,
1981 anim_vertices_ptr,
1982 anim_tex_vertices_ptr,
1983 anim_vertices_count: anim_vertices_count_val,
1984 anim_tex_vertices_count,
1985 })
1986}
1987
1988fn read_aabb_extra(
1997 bytes: &[u8],
1998 offset: usize,
1999 _node_data_end_hint: usize,
2000 mesh: MdlMesh,
2001) -> Result<MdlAabb, MdlError> {
2002 check_slice_in_bounds(bytes, offset, AABB_EXTRA_SIZE, "aabb_extra")?;
2003
2004 let aabb_tree_ptr = checked_to_usize(
2005 read_u32(bytes, offset + aabb_offsets::TREE_PTR)?,
2006 "aabb_tree_ptr",
2007 )?;
2008
2009 let aabb_tree = if aabb_tree_ptr > 0 {
2010 Some(Box::new(read_aabb_tree(bytes, aabb_tree_ptr)?))
2011 } else {
2012 None
2013 };
2014
2015 Ok(MdlAabb { mesh, aabb_tree })
2016}
2017
2018fn read_aabb_tree(bytes: &[u8], ptr: usize) -> Result<AabbNode, MdlError> {
2025 const AABB_NODE_SIZE: usize = 40;
2026
2027 check_slice_in_bounds(bytes, ptr, AABB_NODE_SIZE, "aabb_node")?;
2028
2029 let box_min = [
2030 read_f32(bytes, ptr)?,
2031 read_f32(bytes, ptr + 4)?,
2032 read_f32(bytes, ptr + 8)?,
2033 ];
2034 let box_max = [
2035 read_f32(bytes, ptr + 12)?,
2036 read_f32(bytes, ptr + 16)?,
2037 read_f32(bytes, ptr + 20)?,
2038 ];
2039 let right_ptr = checked_to_usize(read_u32(bytes, ptr + 24)?, "aabb_right_child")?;
2041 let left_ptr = checked_to_usize(read_u32(bytes, ptr + 28)?, "aabb_left_child")?;
2042 let face_index = read_i32(bytes, ptr + 32)?;
2043 let split_direction_flags = read_u32(bytes, ptr + 36)?;
2044
2045 let left = if left_ptr > 0 {
2046 Some(Box::new(read_aabb_tree(bytes, left_ptr)?))
2047 } else {
2048 None
2049 };
2050 let right = if right_ptr > 0 {
2051 Some(Box::new(read_aabb_tree(bytes, right_ptr)?))
2052 } else {
2053 None
2054 };
2055
2056 Ok(AabbNode {
2057 box_min,
2058 box_max,
2059 face_index,
2060 split_direction_flags,
2061 left,
2062 right,
2063 })
2064}
2065
2066fn read_saber_extra(bytes: &[u8], offset: usize, mesh: MdlMesh) -> Result<MdlSaber, MdlError> {
2084 check_slice_in_bounds(bytes, offset, SABER_EXTRA_SIZE, "saber_extra")?;
2085
2086 let verts_ptr = checked_to_usize(
2087 read_u32(bytes, offset + saber_offsets::VERTS_PTR)?,
2088 "saber_verts_ptr",
2089 )?;
2090 let uvs_ptr = checked_to_usize(
2091 read_u32(bytes, offset + saber_offsets::UVS_PTR)?,
2092 "saber_uvs_ptr",
2093 )?;
2094 let normals_ptr = checked_to_usize(
2095 read_u32(bytes, offset + saber_offsets::NORMALS_PTR)?,
2096 "saber_normals_ptr",
2097 )?;
2098 let gl_pool_vert = read_u32(bytes, offset + saber_offsets::GL_POOL_VERT)?;
2099 let gl_pool_index = read_u32(bytes, offset + saber_offsets::GL_POOL_INDEX)?;
2100
2101 let mut saber_verts = Vec::with_capacity(NUM_SABER_VERTS);
2103 if verts_ptr > 0 {
2104 let byte_size = NUM_SABER_VERTS * 12;
2105 if verts_ptr + byte_size <= bytes.len() {
2106 saber_verts = read_vec3_array(bytes, verts_ptr, NUM_SABER_VERTS)?;
2107 } else {
2108 crate::trace_warn!(
2109 verts_ptr,
2110 byte_size,
2111 bytes_len = bytes.len(),
2112 "saber_verts array extends beyond buffer"
2113 );
2114 }
2115 }
2116
2117 let mut saber_uvs = Vec::with_capacity(NUM_SABER_VERTS);
2119 if uvs_ptr > 0 {
2120 let byte_size = NUM_SABER_VERTS * 8;
2121 if uvs_ptr + byte_size <= bytes.len() {
2122 for i in 0..NUM_SABER_VERTS {
2123 let base = uvs_ptr + i * 8;
2124 saber_uvs.push([read_f32(bytes, base)?, read_f32(bytes, base + 4)?]);
2125 }
2126 } else {
2127 crate::trace_warn!(
2128 uvs_ptr,
2129 byte_size,
2130 bytes_len = bytes.len(),
2131 "saber_uvs array extends beyond buffer"
2132 );
2133 }
2134 }
2135
2136 let mut saber_normals = Vec::with_capacity(NUM_SABER_VERTS);
2138 if normals_ptr > 0 {
2139 let byte_size = NUM_SABER_VERTS * 12;
2140 if normals_ptr + byte_size <= bytes.len() {
2141 saber_normals = read_vec3_array(bytes, normals_ptr, NUM_SABER_VERTS)?;
2142 } else {
2143 crate::trace_warn!(
2144 normals_ptr,
2145 byte_size,
2146 bytes_len = bytes.len(),
2147 "saber_normals array extends beyond buffer"
2148 );
2149 }
2150 }
2151
2152 Ok(MdlSaber {
2153 mesh,
2154 saber_verts,
2155 saber_uvs,
2156 saber_normals,
2157 gl_pool_vert,
2158 gl_pool_index,
2159 })
2160}