1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
use crate::C3dParseError;

/// Processor type enum for determining endianess of the bytes during parsing and writing.
/// Older C3D files may be stored in Dec or SgiMips format. Most modern C3D files are stored
/// in Intel format. A parser that supports all three formats is required to read all C3D files.
///
/// c3dio supports reading and writing all three formats.
#[derive(Debug, Copy, Clone, PartialEq, Default)]
pub enum Processor {
    /// Dec (Digital Equipment Corporation) is the default format for data created on a DEC computer.
    /// Traditionally, this data was produced on a VAX or RSX-11M operating system.
    Dec,
    /// Intel is the default format for data created on an Intel-based computer running Windows or Linux.
    #[default]
    Intel,
    /// SgiMips (Silicon Graphics MIPS) is the default format for data created on a Silicon Graphics RISC-based computer.
    /// These systems are not in common use today.
    SgiMips,
}

impl ToString for Processor {
    fn to_string(&self) -> String {
        match self {
            Processor::Intel => "Intel",
            Processor::Dec => "DEC",
            Processor::SgiMips => "SGI MIPS",
        }
        .to_string()
    }
}

/// Processor is used to conveniently calculate the value of specific bytes
/// based on the processor type. C3D files can be created on different
/// processors and the bytes are stored differently based on the processor.
/// The processor type is stored in the parameter start block.
impl Processor {
    #[allow(dead_code)]
    pub(crate) fn new() -> Processor {
        Processor::default()
    }

    /// Convenience function to create a Processor from the parameter start block.
    ///
    /// # Errors
    ///
    /// Returns an error if the processor type is not valid. Acceptable values are:
    /// 0x54 - Intel
    /// 0x55 - Dec
    /// 0x56 - SgiMips
    pub(crate) fn from_parameter_start_block(
        parameter_start_block: [u8; 512],
    ) -> Result<Processor, C3dParseError> {
        match parameter_start_block[3] {
            0x54 => Ok(Processor::Intel),
            0x55 => Ok(Processor::Dec),
            0x56 => Ok(Processor::SgiMips),
            _ => Err(C3dParseError::InvalidProcessorType),
        }
    }

    /// Calculates the u16 value from the bytes based on the processor type.
    pub(crate) fn u16(self, bytes: [u8; 2]) -> u16 {
        match self {
            Processor::Intel => intel_u16(bytes),
            Processor::Dec => dec_u16(bytes),
            Processor::SgiMips => sgi_mips_u16(bytes),
        }
    }

    /// Calculates the i16 value from the bytes based on the processor type.
    pub(crate) fn i16(self, bytes: [u8; 2]) -> i16 {
        match self {
            Processor::Intel => intel_i16(bytes) as i16,
            Processor::Dec => dec_i16(bytes) as i16,
            Processor::SgiMips => sgi_mips_i16(bytes) as i16,
        }
    }

    /// Calculates the f32 value from the bytes based on the processor type.
    pub(crate) fn f32(self, bytes: [u8; 4]) -> f32 {
        match self {
            Processor::Intel => intel_f32(bytes),
            Processor::Dec => dec_f32(bytes),
            Processor::SgiMips => sgi_mips_f32(bytes),
        }
    }

    /// Calculates the bytes from the u16 value based on the processor type.
    pub(crate) fn u16_to_bytes(self, value: u16) -> [u8; 2] {
        match self {
            Processor::Intel => value.to_le_bytes(),
            Processor::Dec => value.to_le_bytes(),
            Processor::SgiMips => value.to_be_bytes(),
        }
    }

    pub(crate) fn i16_to_bytes(self, value: i16) -> [u8; 2] {
        match self {
            Processor::Intel => value.to_le_bytes(),
            Processor::Dec => value.to_le_bytes(),
            Processor::SgiMips => value.to_be_bytes(),
        }
    }

    /// Calculates the bytes from the f32 value based on the processor type.
    pub(crate) fn f32_to_bytes(self, value: f32) -> [u8; 4] {
        match self {
            Processor::Intel => value.to_le_bytes(),
            Processor::Dec => {
                let temp = value.to_le_bytes();
                if temp[3] == 255 {
                    [temp[2], temp[3], temp[0], temp[1]]
                } else {
                    [temp[2], temp[3] + 1, temp[0], temp[1]]
                }
            }
            Processor::SgiMips => value.to_be_bytes(),
        }
    }
}

/// Conversion of the raw bytes into intel u16 format
fn intel_u16(bytes: [u8; 2]) -> u16 {
    u16::from_le_bytes(bytes)
}

/// Conversion of the raw bytes into dec u16 format
fn dec_u16(bytes: [u8; 2]) -> u16 {
    u16::from_le_bytes(bytes)
}

/// Conversion of the raw bytes into sgi_mips u16 format
fn sgi_mips_u16(bytes: [u8; 2]) -> u16 {
    u16::from_be_bytes(bytes)
}

/// Conversion of the raw bytes into intel i16 format
fn intel_i16(bytes: [u8; 2]) -> i16 {
    i16::from_le_bytes(bytes)
}

/// Conversion of the raw bytes into dec i16 format
fn dec_i16(bytes: [u8; 2]) -> i16 {
    i16::from_le_bytes(bytes)
}

/// Conversion of the raw bytes into sgi_mips i16 format
fn sgi_mips_i16(bytes: [u8; 2]) -> i16 {
    i16::from_be_bytes(bytes)
}

/// Conversion of the raw bytes into intel f32 format
fn intel_f32(bytes: [u8; 4]) -> f32 {
    f32::from_le_bytes(bytes)
}

/// Conversion of the raw bytes into dec f32 format based on the following:
/// https://stackoverflow.com/questions/64760137/how-to-display-dec-floating-point-format-given-32-bits-in-ieee-standard
fn dec_f32(bytes: [u8; 4]) -> f32 {
    if bytes[1] == 0x00 {
        let bytes = [bytes[2], bytes[3], bytes[0], bytes[1]];
        f32::from_le_bytes(bytes)
    } else {
        let bytes = [bytes[2], bytes[3], bytes[0], bytes[1] - 1];
        f32::from_le_bytes(bytes)
    }
}

/// Conversion of the raw bytes into sgi_mips f32 format
fn sgi_mips_f32(bytes: [u8; 4]) -> f32 {
    f32::from_be_bytes(bytes)
}