rw_parser_rs/renderware/ifp/
ifp_parser.rs

1//! # IFP Parser
2//!
3//! A parser for RenderWare IFP (Animation Package) files, used for character
4//! animations in Grand Theft Auto 3, Vice City, and San Andreas. This module
5//! supports both `ANP3` (GTA3/VC) and `ANPK` (SA) formats.
6//!
7//! ## Features
8//!
9//! - Parses animation names, bone keyframes, and hierarchy.
10//! - Automatically detects and handles `ANP3` and `ANPK` versions.
11//! - Deserializes animation data into a structured `RwIfp` format.
12//!
13//! ## Example
14//!
15//! ```no_run
16//! use rw_parser_rs::renderware::ifp::ifp_parser::IfpParser;
17//! use std::fs;
18//!
19//! let file_data = fs::read("path/to/your/animation.ifp").unwrap();
20//! let mut parser = IfpParser::new(&file_data);
21//! let ifp_data = parser.parse().unwrap();
22//!
23//! println!("Animation package name: {}", ifp_data.name);
24//! ```
25
26use crate::renderware::rw_file::RwFile;
27use crate::renderware::common::types::{RwVector3, RwQuaternion};
28use std::io::Result;
29
30use serde::Serialize;
31
32/// Represents the version of the IFP file.
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
34pub enum IfpVersion {
35    /// GTA3 / Vice City format.
36    ANP3,
37    /// San Andreas format.
38    ANPK,
39    /// An unsupported or unknown format.
40    UNSUPPORTED,
41}
42
43/// Represents the top-level structure of a parsed IFP file.
44///
45/// This struct contains the package name and a list of all animations
46/// included in the file.
47#[derive(Debug, Clone, PartialEq, Serialize)]
48pub struct RwIfp {
49    /// The format version of the IFP file (`ANP3` or `ANPK`).
50    pub version: IfpVersion,
51    /// The name of the animation package.
52    pub name: String,
53    /// A list of animations contained within the package.
54    pub animations: Vec<RwIfpAnimation>,
55}
56
57#[derive(Debug, Clone, PartialEq, Serialize)]
58pub struct RwIfpAnimation {
59    pub name: String,
60    pub bones: Vec<RwIfpBone>,
61}
62
63#[derive(Debug, Clone, PartialEq, Serialize)]
64pub struct RwIfpBone {
65    pub name: String,
66    pub keyframe_type: String,
67    pub use_bone_id: bool,
68    pub bone_id: i32,
69    pub keyframes: Vec<RwIfpKeyframe>,
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
73pub struct RwIfpKeyframe {
74    pub time: f32,
75    pub position: RwVector3,
76    pub rotation: RwQuaternion,
77    pub scale: RwVector3,
78}
79
80/// The main parser for IFP files.
81///
82/// This struct holds the file buffer and provides the `parse` method to
83/// deserialize the IFP animation data.
84pub struct IfpParser<'a> {
85    file: RwFile<'a>,
86}
87
88impl<'a> IfpParser<'a> {
89    /// Creates a new `IfpParser` instance with the given file buffer.
90    ///
91    /// # Arguments
92    ///
93    /// * `buffer` - A byte slice containing the raw IFP file data.
94    pub fn new(buffer: &'a [u8]) -> Self {
95        IfpParser {
96            file: RwFile::new(buffer),
97        }
98    }
99
100    /// Parses the entire IFP file buffer.
101    ///
102    /// This method detects the IFP version (`ANP3` or `ANPK`) based on the
103    /// file signature and calls the appropriate internal parsing method.
104    ///
105    /// # Returns
106    ///
107    /// A `Result` containing the parsed `RwIfp` data or an `std::io::Error`
108    /// if the file format is not supported or a parsing error occurs.
109    pub fn parse(&mut self) -> Result<RwIfp> {
110        let file_signature = self.file.get_stream().read_string(4)?;
111        self.file.get_stream().set_position(0);
112
113        match file_signature.as_str() {
114            "ANP3" => self.read_anp3(),
115            "ANPK" => self.read_anpk(),
116            _ => Err(std::io::Error::new(std::io::ErrorKind::Other, "Unsupported IFP version")),
117        }
118    }
119
120    fn read_anp3(&mut self) -> Result<RwIfp> {
121        self.file.get_stream().skip(4)?; // ANP3
122        let _size = self.file.get_stream().read_u32()?;
123        let name = self.file.get_stream().read_string(24)?;
124        let animations_count = self.file.get_stream().read_u32()?;
125        let mut animations = Vec::with_capacity(animations_count as usize);
126
127        for _ in 0..animations_count {
128            animations.push(self.read_anp3_animation()?);
129        }
130
131        Ok(RwIfp {
132            version: IfpVersion::ANP3,
133            name,
134            animations,
135        })
136    }
137
138    fn read_anp3_animation(&mut self) -> Result<RwIfpAnimation> {
139        let name = self.file.get_stream().read_string(24)?;
140        let bones_count = self.file.get_stream().read_u32()?;
141        self.file.get_stream().skip(8)?; // keyframes_size, unk
142        let mut bones = Vec::with_capacity(bones_count as usize);
143
144        for _ in 0..bones_count {
145            bones.push(self.read_anp3_bone()?);
146        }
147
148        Ok(RwIfpAnimation { name, bones })
149    }
150
151    fn read_anp3_bone(&mut self) -> Result<RwIfpBone> {
152        let name = self.file.get_stream().read_string(24)?;
153        let keyframe_type_num = self.file.get_stream().read_u32()?;
154        let keyframes_count = self.file.get_stream().read_u32()?;
155        let keyframe_type = if keyframe_type_num == 4 { "KRT0" } else { "KR00" }.to_string();
156        let bone_id = self.file.get_stream().read_i32()?;
157        let mut keyframes = Vec::with_capacity(keyframes_count as usize);
158
159        for _ in 0..keyframes_count {
160            let qx = self.file.get_stream().read_i16()? as f32 / 4096.0;
161            let qy = self.file.get_stream().read_i16()? as f32 / 4096.0;
162            let qz = self.file.get_stream().read_i16()? as f32 / 4096.0;
163            let qw = self.file.get_stream().read_i16()? as f32 / 4096.0;
164            let time = self.file.get_stream().read_i16()? as f32;
165
166            let (px, py, pz) = if keyframe_type.as_bytes()[2] == b'T' {
167                (
168                    self.file.get_stream().read_i16()? as f32 / 1024.0,
169                    self.file.get_stream().read_i16()? as f32 / 1024.0,
170                    self.file.get_stream().read_i16()? as f32 / 1024.0,
171                )
172            } else {
173                (0.0, 0.0, 0.0)
174            };
175            
176            keyframes.push(RwIfpKeyframe {
177                time,
178                position: RwVector3 { x: px, y: py, z: pz },
179                rotation: RwQuaternion { w: qw, x: qx, y: qy, z: qz },
180                scale: RwVector3 { x: 1.0, y: 1.0, z: 1.0 },
181            });
182        }
183
184        Ok(RwIfpBone {
185            name,
186            keyframe_type,
187            use_bone_id: true,
188            bone_id,
189            keyframes,
190        })
191    }
192
193    fn read_anpk(&mut self) -> Result<RwIfp> {
194        self.file.get_stream().skip(4)?; // ANPK
195        let _size = self.file.get_stream().read_u32()?;
196        self.file.get_stream().skip(4)?; // INFO
197        let info_len = self.file.get_stream().read_u32()?;
198        let animations_count = self.file.get_stream().read_u32()?;
199        let name = self.file.get_stream().read_string((info_len - 4) as usize)?;
200        let name_align_len = (4 - info_len % 4) % 4;
201        self.file.get_stream().skip(name_align_len as u64)?;
202
203        let mut animations = Vec::with_capacity(animations_count as usize);
204        for _ in 0..animations_count {
205            animations.push(self.read_anpk_animation()?);
206        }
207
208        Ok(RwIfp {
209            version: IfpVersion::ANPK,
210            name,
211            animations,
212        })
213    }
214
215    fn read_anpk_animation(&mut self) -> Result<RwIfpAnimation> {
216        self.file.get_stream().skip(4)?; // NAME
217        let name_len = self.file.get_stream().read_u32()?;
218        let name = self.file.get_stream().read_string(name_len as usize)?;
219        self.file.get_stream().skip(((4 - name_len % 4) % 4) as u64)?;
220        self.file.get_stream().skip(16)?; // DGAN, animation_size, INFO, unk_size
221        let bones_count = self.file.get_stream().read_u32()?;
222        self.file.get_stream().skip(4)?; // unk
223        
224        let mut bones = Vec::with_capacity(bones_count as usize);
225        for _ in 0..bones_count {
226            bones.push(self.read_anpk_bone()?);
227        }
228
229        Ok(RwIfpAnimation { name, bones })
230    }
231
232    fn read_anpk_bone(&mut self) -> Result<RwIfpBone> {
233        self.file.get_stream().skip(8)?; // CPAN, bone_len
234        self.file.get_stream().skip(4)?; // ANIM
235        let anim_len = self.file.get_stream().read_u32()?;
236        let name = self.file.get_stream().read_string(28)?;
237        let keyframes_count = self.file.get_stream().read_u32()?;
238        self.file.get_stream().skip(8)?; // unk
239
240        let use_bone_id = anim_len == 44;
241        let bone_id = if use_bone_id {
242            self.file.get_stream().read_i32()?
243        } else {
244            self.file.get_stream().skip(8)?;
245            0
246        };
247
248        let mut keyframe_type = "K000".to_string();
249        let mut keyframes = Vec::new();
250
251        if keyframes_count > 0 {
252            keyframe_type = self.file.get_stream().read_string(4)?;
253            self.file.get_stream().skip(4)?; // keyframes_len
254
255            for _ in 0..keyframes_count {
256                let qx = self.file.get_stream().read_f32()?;
257                let qy = self.file.get_stream().read_f32()?;
258                let qz = self.file.get_stream().read_f32()?;
259                let qw = self.file.get_stream().read_f32()?;
260
261                let (px, py, pz) = if keyframe_type.as_bytes()[2] == b'T' {
262                    (self.file.get_stream().read_f32()?, self.file.get_stream().read_f32()?, self.file.get_stream().read_f32()?)
263                } else {
264                    (0.0, 0.0, 0.0)
265                };
266
267                let (sx, sy, sz) = if keyframe_type.as_bytes()[3] == b'S' {
268                    (self.file.get_stream().read_f32()?, self.file.get_stream().read_f32()?, self.file.get_stream().read_f32()?)
269                } else {
270                    (1.0, 1.0, 1.0)
271                };
272
273                let time = self.file.get_stream().read_f32()?;
274
275                keyframes.push(RwIfpKeyframe {
276                    time,
277                    position: RwVector3 { x: px, y: py, z: pz },
278                    rotation: RwQuaternion { w: qw, x: qx, y: qy, z: qz },
279                    scale: RwVector3 { x: sx, y: sy, z: sz },
280                });
281            }
282        }
283
284        Ok(RwIfpBone {
285            name,
286            keyframe_type,
287            use_bone_id,
288            bone_id,
289            keyframes,
290        })
291    }
292}