rw_parser_rs/renderware/dff/
dff_parser.rs

1//! # DFF Parser
2//!
3//! A parser for RenderWare DFF (Clump) files, commonly used in Grand Theft Auto 3,
4//! Vice City, and San Andreas. This module provides structures and methods to
5//! deserialize binary DFF data into a structured format.
6//!
7//! ## Features
8//!
9//! - Parses geometry, materials, frames, and skinning data.
10//! - Determines the model type (Generic, Vehicle, Skin).
11//! - Supports multiple RenderWare versions.
12//! - Outputs a serializable `RwDff` structure.
13//!
14//! ## Example
15//!
16//! ```no_run
17//! use rw_parser_rs::renderware::dff::dff_parser::DffParser;
18//! use std::fs;
19//!
20//! let file_data = fs::read("path/to/your/model.dff").unwrap();
21//! let mut parser = DffParser::new(&file_data);
22//! let dff_data = parser.parse().unwrap();
23//!
24//! println!("Model version: {}", dff_data.version);
25//! ```
26
27use super::dff_model_type::DffModelType;
28use crate::renderware::common::types::{
29    RwColor, RwMatrix3, RwMatrix4, RwSphere, RwTextureCoordinate, RwTriangle, RwVector3, RwVector4,
30};
31use crate::renderware::rw_file::RwFile;
32use crate::renderware::rw_sections::RwSections;
33use crate::utils::rw_version::{unpack_version, RwVersion};
34use std::io::Result;
35use num::FromPrimitive;
36
37use serde::Serialize;
38
39/// Represents the top-level structure of a parsed DFF file.
40///
41/// This struct contains all the deserialized data from a RenderWare Clump,
42/// including geometry, frame hierarchy, and metadata.
43#[derive(Debug, Clone, PartialEq, Serialize)]
44pub struct RwDff {
45    /// The determined type of the model (e.g., Skin, Vehicle).
46    pub model_type: DffModelType,
47    /// The RenderWare version string (e.g., "3.6.0.3").
48    pub version: String,
49    /// The packed integer representation of the RenderWare version.
50    pub version_number: u32,
51    /// A list of geometries contained within the DFF file.
52    pub geometry_list: Option<RwGeometryList>,
53    /// The frame hierarchy (skeleton) of the model.
54    pub frame_list: Option<RwFrameList>,
55    /// Atomic data mapping geometries to frames.
56    pub atomics: Vec<u32>,
57    /// A list of dummy object names.
58    pub dummies: Vec<String>,
59    /// Animation node data, typically for skinned models.
60    pub anim_nodes: Vec<RwAnimNode>,
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
64pub struct RwClump {
65    pub atomic_count: u32,
66    pub light_count: Option<u32>,
67    pub camera_count: Option<u32>,
68}
69
70#[derive(Debug, Clone, PartialEq, Serialize)]
71pub struct RwAnimNode {
72    pub bone_id: i32,
73    pub bones_count: i32,
74    pub bones: Vec<RwBone>,
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
78pub struct RwBone {
79    pub bone_id: i32,
80    pub bone_index: i32,
81    pub flags: i32,
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
85pub struct RwFrame {
86    pub rotation_matrix: RwMatrix3,
87    pub coordinates_offset: RwVector3,
88    pub parent_frame: i32,
89}
90
91#[derive(Debug, Clone, PartialEq, Serialize)]
92pub struct RwFrameList {
93    pub frame_count: u32,
94    pub frames: Vec<RwFrame>,
95}
96
97#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
98pub struct RwTexture {
99    pub texture_filtering: u8,
100    pub u_addressing: u8,
101    pub v_addressing: u8,
102    pub uses_mip_levels: bool,
103    pub texture_name: String,
104}
105
106#[derive(Debug, Clone, PartialEq, Serialize)]
107pub struct RwMaterial {
108    pub color: RwColor,
109    pub is_textured: bool,
110    pub ambient: Option<f32>,
111    pub specular: Option<f32>,
112    pub diffuse: Option<f32>,
113    pub texture: Option<RwTexture>,
114}
115
116#[derive(Debug, Clone, PartialEq, Serialize)]
117pub struct RwMaterialList {
118    pub material_instance_count: u32,
119    pub material_data: Vec<RwMaterial>,
120}
121
122#[derive(Debug, Clone, PartialEq, Serialize)]
123pub struct RwGeometry {
124    pub vertex_color_information: Vec<RwColor>,
125    pub texture_coordinates_count: u8,
126    pub texture_mapping_information: Vec<Vec<RwTextureCoordinate>>,
127    pub has_vertices: bool,
128    pub has_normals: bool,
129    pub triangle_information: Vec<RwTriangle>,
130    pub vertex_information: Vec<RwVector3>,
131    pub normal_information: Vec<RwVector3>,
132    pub bounding_sphere: Option<RwSphere>,
133    pub material_list: RwMaterialList,
134    pub bin_mesh: RwBinMesh,
135    pub skin: Option<RwSkin>,
136}
137
138#[derive(Debug, Clone, PartialEq, Serialize)]
139pub struct RwGeometryList {
140    pub geometric_object_count: u32,
141    pub geometries: Vec<RwGeometry>,
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
145pub struct RwAtomic {
146    pub frame_index: u32,
147    pub geometry_index: u32,
148    pub flags: u32,
149}
150
151#[derive(Debug, Clone, PartialEq, Serialize)]
152pub struct RwBinMesh {
153    pub mesh_count: u32,
154    pub meshes: Vec<RwMesh>,
155}
156
157#[derive(Debug, Clone, PartialEq, Serialize)]
158pub struct RwSkin {
159    pub bone_count: u8,
160    pub used_bone_count: u8,
161    pub max_weights_per_vertex: u8,
162    pub bone_vertex_indices: Vec<Vec<u8>>,
163    pub vertex_weights: Vec<Vec<f32>>,
164    pub inverse_bone_matrices: Vec<RwMatrix4>,
165}
166
167#[derive(Debug, Clone, PartialEq, Serialize)]
168pub struct RwMesh {
169    pub material_index: u32,
170    pub index_count: u32,
171    pub indices: Vec<u32>,
172}
173
174/// The main parser for DFF files.
175///
176/// This struct holds the file buffer and provides the `parse` method to
177/// deserialize the DFF data.
178pub struct DffParser<'a> {
179    file: RwFile<'a>,
180}
181
182impl<'a> DffParser<'a> {
183    /// Creates a new `DffParser` instance with the given file buffer.
184    ///
185    /// # Arguments
186    ///
187    /// * `buffer` - A byte slice containing the raw DFF file data.
188    pub fn new(buffer: &'a [u8]) -> Self {
189        DffParser {
190            file: RwFile::new(buffer),
191        }
192    }
193
194    /// Parses the entire DFF file buffer.
195    ///
196    /// This method iterates through the RenderWare sections in the file,
197    /// deserializing each part and assembling the final `RwDff` struct.
198    ///
199    /// # Returns
200    ///
201    /// A `Result` containing the parsed `RwDff` data or an `std::io::Error`
202    /// if the parsing fails.
203    pub fn parse(&mut self) -> Result<RwDff> {
204        let mut version: Option<String> = None;
205        let mut version_number: Option<u32> = None;
206        let mut atomics: Vec<u32> = Vec::new();
207        let mut dummies: Vec<String> = Vec::new();
208        let mut anim_nodes: Vec<RwAnimNode> = Vec::new();
209        let mut geometry_list: Option<RwGeometryList> = None;
210        let mut frame_list: Option<RwFrameList> = None;
211
212        while self.file.get_stream().get_position() < self.file.get_stream().get_size() {
213            let header = self.file.read_section_header()?;
214
215            if header.section_type == 0 {
216                break;
217            }
218
219            if header.section_size == 0 {
220                continue;
221            }
222
223            let section_type_enum: Option<RwSections> = FromPrimitive::from_u32(header.section_type);
224
225            match section_type_enum {
226                Some(RwSections::RwClump) => {
227                    version_number = Some(unpack_version(header.version_number));
228                    version = Some(RwVersion::new().get_version_string(version_number.unwrap()).unwrap_or_default());
229                }
230                Some(RwSections::RwFrameList) => {
231                    frame_list = Some(self.read_frame_list()?);
232                }
233                Some(RwSections::RwExtension) => {
234                    let extension_header = self.file.read_section_header()?;
235                    let extension_type_enum: Option<RwSections> = FromPrimitive::from_u32(extension_header.section_type);
236                    match extension_type_enum {
237                        Some(RwSections::RwNodeName) => {
238                            dummies.push(self.file.get_stream().read_string(extension_header.section_size as usize)?);
239                        }
240                        Some(RwSections::RwAnim) => {
241                            anim_nodes.push(self.read_anim_node()?);
242                        }
243                        _ => {
244                            self.file.get_stream().skip(extension_header.section_size as u64)?;
245                        }
246                    }
247                }
248                Some(RwSections::RwGeometryList) => {
249                    geometry_list = Some(self.read_geometry_list()?);
250                }
251                Some(RwSections::RwAtomic) => {
252                    let atomic = self.read_atomic()?;
253                    if atomics.len() <= atomic.geometry_index as usize {
254                        atomics.resize(atomic.geometry_index as usize + 1, 0);
255                    }
256                    atomics[atomic.geometry_index as usize] = atomic.frame_index;
257                }
258                Some(RwSections::RwNodeName) => {
259                    dummies.push(self.file.get_stream().read_string(header.section_size as usize)?);
260                }
261                Some(RwSections::RwAnim) => {
262                    anim_nodes.push(self.read_anim_node()?);
263                }
264                _ => {
265                    self.file.get_stream().skip(header.section_size as u64)?;
266                }
267            }
268        }
269
270        let model_type = if geometry_list.as_ref().map_or(false, |g| g.geometries.iter().any(|geo| geo.skin.is_some())) {
271            DffModelType::Skin
272        } else if dummies.iter().any(|d| d.to_lowercase().contains("wheel") || d.to_lowercase().contains("chassis")) {
273            DffModelType::Vehicle
274        } else {
275            DffModelType::Generic
276        };
277
278        Ok(RwDff {
279            model_type,
280            version: version.unwrap_or_default(),
281            version_number: version_number.unwrap_or_default(),
282            geometry_list,
283            frame_list,
284            atomics,
285            dummies,
286            anim_nodes,
287        })
288    }
289
290    fn read_frame_list(&mut self) -> Result<RwFrameList> {
291        self.file.read_section_header()?; // Struct
292
293        let frame_count = self.file.get_stream().read_u32()?;
294        let mut frames = Vec::with_capacity(frame_count as usize);
295
296        for _ in 0..frame_count {
297            let rotation_matrix = RwMatrix3 {
298                right: RwVector3 {
299                    x: self.file.get_stream().read_f32()?,
300                    y: self.file.get_stream().read_f32()?,
301                    z: self.file.get_stream().read_f32()?,
302                },
303                up: RwVector3 {
304                    x: self.file.get_stream().read_f32()?,
305                    y: self.file.get_stream().read_f32()?,
306                    z: self.file.get_stream().read_f32()?,
307                },
308                at: RwVector3 {
309                    x: self.file.get_stream().read_f32()?,
310                    y: self.file.get_stream().read_f32()?,
311                    z: self.file.get_stream().read_f32()?,
312                },
313            };
314
315            let coordinates_offset = RwVector3 {
316                x: self.file.get_stream().read_f32()?,
317                y: self.file.get_stream().read_f32()?,
318                z: self.file.get_stream().read_f32()?,
319            };
320
321            let parent_frame = self.file.get_stream().read_i32()?;
322            self.file.get_stream().skip(4)?; // Skip matrix creation internal flags
323
324            frames.push(RwFrame {
325                rotation_matrix,
326                coordinates_offset,
327                parent_frame,
328            });
329        }
330
331        Ok(RwFrameList {
332            frame_count,
333            frames,
334        })
335    }
336
337    fn read_atomic(&mut self) -> Result<RwAtomic> {
338        self.file.read_section_header()?; // Struct
339
340        let frame_index = self.file.get_stream().read_u32()?;
341        let geometry_index = self.file.get_stream().read_u32()?;
342        let flags = self.file.get_stream().read_u32()?;
343
344        self.file.get_stream().skip(4)?; // Skip unused bytes
345
346        Ok(RwAtomic {
347            frame_index,
348            geometry_index,
349            flags,
350        })
351    }
352    
353    fn read_geometry_list(&mut self) -> Result<RwGeometryList> {
354        let header = self.file.read_section_header()?; // Struct
355
356        let geometric_object_count = self.file.get_stream().read_u32()?;
357        let mut geometries = Vec::with_capacity(geometric_object_count as usize);
358
359        for _ in 0..geometric_object_count {
360            self.file.read_section_header()?; // Geometry
361            self.file.read_section_header()?; // Struct
362            let version_number = unpack_version(header.version_number);
363            geometries.push(self.read_geometry(version_number)?);
364        }
365
366        Ok(RwGeometryList {
367            geometric_object_count,
368            geometries,
369        })
370    }
371
372    fn read_geometry(&mut self, version_number: u32) -> Result<RwGeometry> {
373        let flags = self.file.get_stream().read_u16()?;
374        let texture_coordinates_count = self.file.get_stream().read_u8()?;
375        let _native_geometry_flags = self.file.get_stream().read_u8()?;
376        let triangle_count = self.file.get_stream().read_u32()?;
377        let vertex_count = self.file.get_stream().read_u32()?;
378        let _morph_target_count = self.file.get_stream().read_u32()?;
379
380        if version_number < 0x34000 {
381            self.file.get_stream().skip(12)?; // ambient, specular, diffuse
382        }
383
384        let is_textured_uv1 = (flags & (1 << 2)) != 0;
385        let is_geometry_prelit = (flags & (1 << 3)) != 0;
386        let is_textured_uv2 = (flags & (1 << 7)) != 0;
387
388        let mut vertex_color_information = Vec::new();
389        if is_geometry_prelit {
390            for _ in 0..vertex_count {
391                vertex_color_information.push(RwColor {
392                    r: self.file.get_stream().read_u8()?,
393                    g: self.file.get_stream().read_u8()?,
394                    b: self.file.get_stream().read_u8()?,
395                    a: self.file.get_stream().read_u8()?,
396                });
397            }
398        }
399
400        let mut texture_mapping_information = Vec::new();
401        if is_textured_uv1 || is_textured_uv2 {
402            for _ in 0..texture_coordinates_count {
403                let mut tex_coords = Vec::new();
404                for _ in 0..vertex_count {
405                    tex_coords.push(RwTextureCoordinate {
406                        u: self.file.get_stream().read_f32()?,
407                        v: self.file.get_stream().read_f32()?,
408                    });
409                }
410                texture_mapping_information.push(tex_coords);
411            }
412        }
413
414        let mut triangle_information = Vec::new();
415        for _ in 0..triangle_count {
416            let vertex2 = self.file.get_stream().read_u16()?;
417            let vertex1 = self.file.get_stream().read_u16()?;
418            let material_id = self.file.get_stream().read_u16()?;
419            let vertex3 = self.file.get_stream().read_u16()?;
420            triangle_information.push(RwTriangle {
421                vector: RwVector3 {
422                    x: vertex1 as f32,
423                    y: vertex2 as f32,
424                    z: vertex3 as f32,
425                },
426                material_id,
427            });
428        }
429
430        let bounding_sphere = Some(RwSphere {
431            vector: RwVector3 {
432                x: self.file.get_stream().read_f32()?,
433                y: self.file.get_stream().read_f32()?,
434                z: self.file.get_stream().read_f32()?,
435            },
436            radius: self.file.get_stream().read_f32()?,
437        });
438
439        let has_vertices = self.file.get_stream().read_u32()? != 0;
440        let has_normals = self.file.get_stream().read_u32()? != 0;
441
442        let mut vertex_information = Vec::new();
443        if has_vertices {
444            for _ in 0..vertex_count {
445                vertex_information.push(RwVector3 {
446                    x: self.file.get_stream().read_f32()?,
447                    y: self.file.get_stream().read_f32()?,
448                    z: self.file.get_stream().read_f32()?,
449                });
450            }
451        }
452
453        let mut normal_information = Vec::new();
454        if has_normals {
455            for _ in 0..vertex_count {
456                normal_information.push(RwVector3 {
457                    x: self.file.get_stream().read_f32()?,
458                    y: self.file.get_stream().read_f32()?,
459                    z: self.file.get_stream().read_f32()?,
460                });
461            }
462        }
463
464        let material_list = self.read_material_list()?;
465        let section_size = self.file.read_section_header()?.section_size;
466        let position = self.file.get_stream().get_position();
467        let bin_mesh = self.read_bin_mesh()?;
468        
469        let mut skin = None;
470        let next_header = self.file.read_section_header()?;
471        if next_header.section_type == RwSections::RwSkin as u32 {
472            skin = Some(self.read_skin(vertex_count)?);
473        }
474
475        self.file.get_stream().set_position(position + section_size as u64);
476
477        Ok(RwGeometry {
478            vertex_color_information,
479            texture_coordinates_count,
480            texture_mapping_information,
481            has_vertices,
482            has_normals,
483            triangle_information,
484            vertex_information,
485            normal_information,
486            bounding_sphere,
487            material_list,
488            bin_mesh,
489            skin,
490        })
491    }
492
493    fn read_material_list(&mut self) -> Result<RwMaterialList> {
494        self.file.read_section_header()?; // Struct
495        self.file.read_section_header()?; // MaterialList
496
497        let material_instance_count = self.file.get_stream().read_u32()?;
498        let mut material_indices = Vec::with_capacity(material_instance_count as usize);
499        for _ in 0..material_instance_count {
500            material_indices.push(self.file.get_stream().read_i32()?);
501        }
502
503        let mut material_data = Vec::with_capacity(material_instance_count as usize);
504        for i in 0..material_instance_count {
505            let material_index = material_indices[i as usize];
506            if material_index == -1 {
507                material_data.push(self.read_material()?);
508            } else {
509                material_data.push(material_data[material_index as usize].clone());
510            }
511        }
512
513        Ok(RwMaterialList {
514            material_instance_count,
515            material_data,
516        })
517    }
518
519    fn read_material(&mut self) -> Result<RwMaterial> {
520        self.file.read_section_header()?; // Struct
521        let header = self.file.read_section_header()?; // Material
522
523        self.file.get_stream().skip(4)?; // Flags
524
525        let color = RwColor {
526            r: self.file.get_stream().read_u8()?,
527            g: self.file.get_stream().read_u8()?,
528            b: self.file.get_stream().read_u8()?,
529            a: self.file.get_stream().read_u8()?,
530        };
531
532        self.file.get_stream().skip(4)?; // Unknown
533
534        let is_textured = self.file.get_stream().read_u32()? > 0;
535
536        let mut ambient = None;
537        let mut specular = None;
538        let mut diffuse = None;
539
540        if header.version_number > 0x30400 {
541            ambient = Some(self.file.get_stream().read_f32()?);
542            specular = Some(self.file.get_stream().read_f32()?);
543            diffuse = Some(self.file.get_stream().read_f32()?);
544        }
545
546        let mut texture = None;
547        if is_textured {
548            texture = Some(self.read_texture()?);
549        }
550
551        let size = self.file.read_section_header()?.section_size;
552        self.file.get_stream().skip(size as u64)?;
553
554        Ok(RwMaterial {
555            color,
556            is_textured,
557            ambient,
558            specular,
559            diffuse,
560            texture,
561        })
562    }
563
564    fn read_texture(&mut self) -> Result<RwTexture> {
565        self.file.read_section_header()?; // Struct
566        self.file.read_section_header()?; // Texture
567
568        let texture_data = self.file.get_stream().read_u32()?;
569        let texture_filtering = (texture_data & 0xFF) as u8;
570        let u_addressing = ((texture_data & 0xF00) >> 8) as u8;
571        let v_addressing = ((texture_data & 0xF000) >> 12) as u8;
572        let uses_mip_levels = (texture_data & (1 << 16)) != 0;
573
574        let texture_name_size = self.file.read_section_header()?.section_size;
575        let texture_name = self.file.get_stream().read_string(texture_name_size as usize)?;
576
577        let size1 = self.file.read_section_header()?.section_size;
578        self.file.get_stream().skip(size1 as u64)?;
579        let size2 = self.file.read_section_header()?.section_size;
580        self.file.get_stream().skip(size2 as u64)?;
581
582        Ok(RwTexture {
583            texture_filtering,
584            u_addressing,
585            v_addressing,
586            uses_mip_levels,
587            texture_name,
588        })
589    }
590
591    fn read_bin_mesh(&mut self) -> Result<RwBinMesh> {
592        self.file.read_section_header()?; // Struct
593
594        self.file.get_stream().skip(4)?; // Flags
595        let mesh_count = self.file.get_stream().read_u32()?;
596        self.file.get_stream().skip(4)?; // Total number of indices
597
598        let mut meshes = Vec::with_capacity(mesh_count as usize);
599        for _ in 0..mesh_count {
600            meshes.push(self.read_mesh()?);
601        }
602
603        Ok(RwBinMesh {
604            mesh_count,
605            meshes,
606        })
607    }
608
609    fn read_mesh(&mut self) -> Result<RwMesh> {
610        let index_count = self.file.get_stream().read_u32()?;
611        let material_index = self.file.get_stream().read_u32()?;
612
613        let mut indices = Vec::with_capacity(index_count as usize);
614        for _ in 0..index_count {
615            indices.push(self.file.get_stream().read_u32()?);
616        }
617
618        Ok(RwMesh {
619            index_count,
620            material_index,
621            indices,
622        })
623    }
624
625    fn read_skin(&mut self, vertex_count: u32) -> Result<RwSkin> {
626        let bone_count = self.file.get_stream().read_u8()?;
627        let used_bone_count = self.file.get_stream().read_u8()?;
628        let max_weights_per_vertex = self.file.get_stream().read_u8()?;
629
630        self.file.get_stream().skip(1)?; // Padding
631        self.file.get_stream().skip(used_bone_count as u64)?; // Skipping special indices
632
633        let mut bone_vertex_indices = Vec::with_capacity(vertex_count as usize);
634        for _ in 0..vertex_count {
635            let mut indices = Vec::with_capacity(4);
636            for _ in 0..4 {
637                indices.push(self.file.get_stream().read_u8()?);
638            }
639            bone_vertex_indices.push(indices);
640        }
641
642        let mut vertex_weights = Vec::with_capacity(vertex_count as usize);
643        for _ in 0..vertex_count {
644            let mut weights = Vec::with_capacity(4);
645            for _ in 0..4 {
646                weights.push(self.file.get_stream().read_f32()?);
647            }
648            vertex_weights.push(weights);
649        }
650
651        let mut inverse_bone_matrices = Vec::with_capacity(bone_count as usize);
652        for _ in 0..bone_count {
653            inverse_bone_matrices.push(RwMatrix4 {
654                right: RwVector4 {
655                    x: self.file.get_stream().read_f32()?,
656                    y: self.file.get_stream().read_f32()?,
657                    z: self.file.get_stream().read_f32()?,
658                    t: self.file.get_stream().read_f32()?,
659                },
660                up: RwVector4 {
661                    x: self.file.get_stream().read_f32()?,
662                    y: self.file.get_stream().read_f32()?,
663                    z: self.file.get_stream().read_f32()?,
664                    t: self.file.get_stream().read_f32()?,
665                },
666                at: RwVector4 {
667                    x: self.file.get_stream().read_f32()?,
668                    y: self.file.get_stream().read_f32()?,
669                    z: self.file.get_stream().read_f32()?,
670                    t: self.file.get_stream().read_f32()?,
671                },
672                transform: RwVector4 {
673                    x: self.file.get_stream().read_f32()?,
674                    y: self.file.get_stream().read_f32()?,
675                    z: self.file.get_stream().read_f32()?,
676                    t: self.file.get_stream().read_f32()?,
677                },
678            });
679        }
680        
681        Ok(RwSkin {
682            bone_count,
683            used_bone_count,
684            max_weights_per_vertex,
685            bone_vertex_indices,
686            vertex_weights,
687            inverse_bone_matrices,
688        })
689    }
690
691    fn read_anim_node(&mut self) -> Result<RwAnimNode> {
692        self.file.get_stream().skip(4)?; // Skipping AnimVersion property (0x100)
693        let bone_id = self.file.get_stream().read_i32()?;
694        let bone_count = self.file.get_stream().read_i32()?;
695        let mut bones = Vec::with_capacity(bone_count as usize);
696
697        if bone_id == 0 {
698            self.file.get_stream().skip(8)?; // Skipping flags and keyFrameSize properties
699        }
700
701        if bone_count > 0 {
702            for _ in 0..bone_count {
703                bones.push(RwBone {
704                    bone_id: self.file.get_stream().read_i32()?,
705                    bone_index: self.file.get_stream().read_i32()?,
706                    flags: self.file.get_stream().read_i32()?,
707                });
708            }
709        }
710
711        Ok(RwAnimNode {
712            bone_id,
713            bones_count: bone_count,
714            bones,
715        })
716    }
717}