physis/
common_file_operations.rs

1// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use binrw::{BinReaderExt, BinResult, binread};
5use half::f16;
6use std::ffi::CString;
7use std::io::SeekFrom;
8
9pub(crate) fn read_bool_from<T: std::convert::From<u8> + std::cmp::PartialEq>(x: T) -> bool {
10    x == T::from(1u8)
11}
12
13pub(crate) fn write_bool_as<T: std::convert::From<u8>>(x: &bool) -> T {
14    if *x { T::from(1u8) } else { T::from(0u8) }
15}
16
17pub(crate) fn read_string(byte_stream: Vec<u8>) -> String {
18    let str = String::from_utf8(byte_stream).unwrap();
19    str.trim_matches(char::from(0)).to_string() // trim \0 from the end of strings
20}
21
22pub(crate) fn write_string(str: &String) -> Vec<u8> {
23    let c_string = CString::new(&**str).unwrap();
24    c_string.as_bytes_with_nul().to_vec()
25}
26
27pub(crate) fn get_string_len(str: &String) -> usize {
28    let c_string = CString::new(&**str).unwrap();
29    c_string.count_bytes() + 1 // for the nul terminator
30}
31
32#[binrw::parser(reader)]
33pub(crate) fn strings_parser(
34    base_offset: u64,
35    strings_offset: &Vec<u16>,
36) -> BinResult<Vec<String>> {
37    let mut strings: Vec<String> = vec![];
38
39    for offset in strings_offset {
40        let string_offset = base_offset + *offset as u64;
41
42        let mut string = String::new();
43
44        reader.seek(SeekFrom::Start(string_offset))?;
45        let mut next_char = reader.read_le::<u8>().unwrap() as char;
46        while next_char != '\0' {
47            string.push(next_char);
48            next_char = reader.read_le::<u8>().unwrap() as char;
49        }
50
51        strings.push(string);
52    }
53
54    Ok(strings)
55}
56
57fn read_half1(data: [u16; 1]) -> Half1 {
58    Half1 {
59        value: f16::from_bits(data[0]),
60    }
61}
62
63#[binread]
64#[derive(Debug, Default, Clone, Copy)]
65#[br(map = read_half1)]
66pub(crate) struct Half1 {
67    pub value: f16,
68}
69
70fn read_half2(data: [u16; 2]) -> Half2 {
71    Half2 {
72        x: f16::from_bits(data[0]),
73        y: f16::from_bits(data[0]),
74    }
75}
76
77#[binread]
78#[derive(Debug, Default, Clone, Copy)]
79#[br(map = read_half2)]
80pub(crate) struct Half2 {
81    pub x: f16,
82    pub y: f16,
83}
84
85fn read_half3(data: [u16; 3]) -> Half3 {
86    Half3 {
87        r: f16::from_bits(data[0]),
88        g: f16::from_bits(data[0]),
89        b: f16::from_bits(data[0]),
90    }
91}
92
93#[binread]
94#[derive(Debug, Default, Clone, Copy)]
95#[br(map = read_half3)]
96pub(crate) struct Half3 {
97    pub r: f16,
98    pub g: f16,
99    pub b: f16,
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    const DATA: [u8; 2] = [0u8, 1u8];
107
108    // TODO: add tests for u16
109
110    #[test]
111    fn read_bool_u8() {
112        assert!(!read_bool_from::<u8>(DATA[0]));
113        assert!(read_bool_from::<u8>(DATA[1]));
114    }
115
116    #[test]
117    fn write_bool_u8() {
118        assert_eq!(write_bool_as::<u8>(&false), DATA[0]);
119        assert_eq!(write_bool_as::<u8>(&true), DATA[1]);
120    }
121
122    // "FOO\0"
123    const STRING_DATA: [u8; 4] = [0x46u8, 0x4Fu8, 0x4Fu8, 0x0u8];
124
125    #[test]
126    fn read_string() {
127        // The nul terminator is supposed to be removed
128        assert_eq!(
129            crate::common_file_operations::read_string(STRING_DATA.to_vec()),
130            "FOO".to_string()
131        );
132    }
133
134    #[test]
135    fn write_string() {
136        // Supposed to include the nul terminator
137        assert_eq!(
138            crate::common_file_operations::write_string(&"FOO".to_string()),
139            STRING_DATA.to_vec()
140        );
141    }
142
143    #[test]
144    fn get_string_len() {
145        // Supposed to include the nul terminator
146        assert_eq!(
147            crate::common_file_operations::get_string_len(&"FOO".to_string()),
148            4
149        );
150    }
151}