ds_rom/
str.rs

1use std::fmt::Display;
2
3use bytemuck::{Pod, Zeroable};
4use serde::{de, Deserialize, Serialize};
5use snafu::{Backtrace, Snafu};
6
7/// A fixed-size ASCII string.
8#[derive(Clone, Copy)]
9pub struct AsciiArray<const N: usize>(pub [u8; N]);
10
11/// Errors related to [`AsciiArray`].
12#[derive(Debug, Snafu)]
13pub enum AsciiArrayError {
14    /// Occurs when an input character is not in ASCII.
15    #[snafu(display("the provided string '{string}' contains one or more non-ASCII characters:\n{backtrace}"))]
16    NotAscii {
17        /// The invalid string.
18        string: String,
19        /// Backtrace to the source of the error.
20        backtrace: Backtrace,
21    },
22}
23
24impl<const N: usize> AsciiArray<N> {
25    /// Loads from a `&str`.
26    ///
27    /// # Errors
28    ///
29    /// This function will return an error if the string contains a non-ASCII character.
30    pub fn from_str(string: &str) -> Result<Self, AsciiArrayError> {
31        let mut chars = [0u8; N];
32        for (i, ch) in string.chars().take(N).enumerate() {
33            if !ch.is_ascii() {
34                return NotAsciiSnafu { string: string.to_string() }.fail();
35            }
36            chars[i] = ch as u8;
37        }
38        Ok(Self(chars))
39    }
40}
41
42impl AsciiArray<4> {
43    /// Converts a four-character ASCII string to a `u32`.
44    pub fn to_le_u32(&self) -> u32 {
45        u32::from_le_bytes(self.0)
46    }
47}
48
49impl<const N: usize> Display for AsciiArray<N> {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        for ch in self.0 {
52            if ch == 0 {
53                break;
54            }
55            write!(f, "{}", ch as char)?;
56        }
57        Ok(())
58    }
59}
60
61impl<const N: usize> Serialize for AsciiArray<N> {
62    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
63    where
64        S: serde::Serializer,
65    {
66        serializer.serialize_str(&self.to_string())
67    }
68}
69
70impl<'de, const N: usize> Deserialize<'de> for AsciiArray<N> {
71    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
72    where
73        D: serde::Deserializer<'de>,
74    {
75        let string: String = Deserialize::deserialize(deserializer)?;
76        Ok(Self::from_str(&string).map_err(de::Error::custom)?)
77    }
78}
79
80/// A fixed-size 16-bit Unicode string.
81#[derive(Clone, Copy)]
82pub struct Unicode16Array<const N: usize>(pub [u16; N]);
83
84unsafe impl<const N: usize> Zeroable for Unicode16Array<N> {}
85unsafe impl<const N: usize> Pod for Unicode16Array<N> {}
86
87impl<const N: usize> Unicode16Array<N> {
88    /// Loads from a `&str`.
89    pub fn from_str(string: &str) -> Self {
90        let mut chars = [0u16; N];
91        let mut i = 0;
92        for ch in string.chars() {
93            let mut codepoints = [0u16; 2];
94            ch.encode_utf16(&mut codepoints);
95
96            let len = if codepoints[1] != 0 { 2 } else { 1 };
97            if i + len >= N {
98                break;
99            }
100
101            for j in 0..len {
102                chars[i] = codepoints[j];
103                i += 1;
104            }
105        }
106        Self(chars)
107    }
108}
109
110impl<const N: usize> Display for Unicode16Array<N> {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        for ch in self.0 {
113            if ch == 0 {
114                break;
115            }
116            let Some(ch) = char::from_u32(ch as u32) else {
117                break;
118            };
119            write!(f, "{ch}")?;
120        }
121        Ok(())
122    }
123}
124
125pub(crate) struct BlobSize(pub usize);
126
127impl Display for BlobSize {
128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        let size = self.0;
130        match size {
131            0..=0x3ff => write!(f, "{}B", size),
132            0x400..=0xfffff => write!(f, "{:.1}kB", size as f32 / 0x400 as f32),
133            0x100000.. => write!(f, "{:.1}MB", size as f32 / 0x100000 as f32),
134        }
135    }
136}
137
138/// For debugging purposes.
139#[allow(unused)]
140pub(crate) fn write_hex(f: &mut std::fmt::Formatter<'_>, data: &[u8]) -> std::fmt::Result {
141    for (offset, chunk) in data.chunks(16).enumerate() {
142        write!(f, "{:08x} ", offset * 16)?;
143        for byte in chunk {
144            write!(f, " {byte:02x}")?;
145        }
146        writeln!(f)?;
147    }
148    writeln!(f)?;
149    Ok(())
150}
151
152/// For debugging purposes.
153#[allow(unused)]
154pub(crate) fn print_hex(data: &[u8]) {
155    for (offset, chunk) in data.chunks(16).enumerate() {
156        print!("{:08x} ", offset * 16);
157        for byte in chunk {
158            print!(" {byte:02x}");
159        }
160        println!();
161    }
162    println!();
163}