physis/
cmp.rs

1// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use std::io::{Cursor, Seek, SeekFrom};
5
6use crate::ByteSpan;
7use binrw::BinRead;
8use binrw::binrw;
9
10#[binrw]
11#[br(little)]
12#[repr(C)]
13#[derive(Debug, Clone, Copy)]
14pub struct RacialScalingParameters {
15    /// Male minimum height
16    pub male_min_size: f32,
17    /// Male maximum height
18    pub male_max_size: f32,
19
20    /// Male minimum tail size
21    pub male_min_tail: f32,
22    /// Male maximum tail size
23    pub male_max_tail: f32,
24
25    /// Female minimum height
26    pub female_min_size: f32,
27    /// Female maximum height
28    pub female_max_size: f32,
29
30    /// Female minimum tail size
31    pub female_min_tail: f32,
32    /// Female maximum tail size
33    pub female_max_tail: f32,
34
35    /// Minimum bust size on the X-axis
36    pub bust_min_x: f32,
37    /// Minimum bust size on the Y-axis
38    pub bust_min_y: f32,
39    /// Minimum bust size on the Z-axis
40    pub bust_min_z: f32,
41
42    /// Maximum bust size on the X-axis
43    pub bust_max_x: f32,
44    /// Maximum bust size on the Y-axis
45    pub bust_max_y: f32,
46    /// Maximum bust size on the Z-axis
47    pub bust_max_z: f32,
48}
49
50#[derive(Debug)]
51pub struct CMP {
52    /// The racial scaling parameters
53    pub parameters: Vec<RacialScalingParameters>,
54}
55
56impl CMP {
57    /// Parses an existing CMP file.
58    pub fn from_existing(buffer: ByteSpan) -> Option<CMP> {
59        let mut cursor = Cursor::new(buffer);
60
61        cursor.seek(SeekFrom::Start(0x2a800)).ok()?;
62
63        let rem = buffer.len() - cursor.position() as usize;
64        let entries = rem / std::mem::size_of::<RacialScalingParameters>();
65
66        let mut parameters = vec![];
67
68        for _ in 0..entries {
69            parameters.push(RacialScalingParameters::read(&mut cursor).ok()?);
70        }
71
72        Some(CMP { parameters })
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use std::fs::read;
79    use std::path::PathBuf;
80
81    use super::*;
82
83    #[test]
84    fn test_invalid() {
85        let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
86        d.push("resources/tests");
87        d.push("random");
88
89        // Feeding it invalid data should not panic
90        CMP::from_existing(&read(d).unwrap());
91    }
92}