classicube_sys/
string.rs

1use core::{
2    borrow::Borrow,
3    ffi::CStr,
4    fmt::{self, Display},
5    slice,
6};
7
8use crate::{
9    bindings::{STRING_SIZE, cc_codepoint, cc_string, cc_uint16, cc_unichar},
10    std_types::{Box, CString, String, ToString, Vec, c_char, c_int},
11};
12
13impl cc_string {
14    pub fn as_slice(&self) -> &[u8] {
15        let len = self.length as usize;
16        let data = self.buffer as *const u8;
17        if len == 0 || data.is_null() {
18            return &[];
19        }
20        unsafe { slice::from_raw_parts(data, len) }
21    }
22}
23
24impl Display for cc_string {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        String::from_utf16_lossy(
27            &self
28                .as_slice()
29                .iter()
30                .map(|c| Convert_CP437ToUnicode(*c))
31                .collect::<Vec<_>>(),
32        )
33        .fmt(f)
34    }
35}
36
37impl From<cc_string> for String {
38    fn from(cc_string: cc_string) -> Self {
39        cc_string.to_string()
40    }
41}
42
43pub struct OwnedString {
44    cc_string: cc_string,
45
46    #[allow(dead_code)]
47    c_str: Box<CStr>,
48}
49
50impl OwnedString {
51    pub fn new<S: Into<String>>(s: S) -> Self {
52        let bytes = s
53            .into()
54            // TODO is chars() "codepoints" in cc's definition?
55            .chars()
56            .map(|c| c as cc_codepoint)
57            .map(Convert_CodepointToCP437)
58            .collect::<Vec<_>>();
59        let length = bytes.len();
60        let capacity = bytes.len();
61
62        let c_str = CString::new(bytes).unwrap().into_boxed_c_str();
63        let buffer: *const c_char = c_str.as_ptr();
64
65        Self {
66            c_str,
67            cc_string: cc_string {
68                buffer: buffer as *mut c_char,
69                length: length as cc_uint16,
70                capacity: capacity as cc_uint16,
71            },
72        }
73    }
74
75    pub fn as_cc_string(&self) -> &cc_string {
76        &self.cc_string
77    }
78
79    /// # Safety
80    ///
81    /// The `OwnedString` needs to live longer than the `cc_string` return here.
82    pub unsafe fn get_cc_string(&self) -> cc_string {
83        cc_string { ..self.cc_string }
84    }
85}
86
87impl Borrow<cc_string> for OwnedString {
88    fn borrow(&self) -> &cc_string {
89        self.as_cc_string()
90    }
91}
92
93#[test]
94fn test_owned_string() {
95    let owned_string = OwnedString::new("hello");
96
97    fn use_cc_string<T: Borrow<cc_string>>(s: T) {
98        #[cfg(not(feature = "no_std"))]
99        {
100            println!("{:?}", s.borrow());
101        }
102    }
103
104    use_cc_string(owned_string.as_cc_string());
105
106    use_cc_string(owned_string);
107
108    // let s: cc_string = owned_string.into();
109}
110
111/// # Safety
112///
113/// The `buffer` needs to live longer than the `cc_string`.
114pub unsafe fn String_Init(buffer: *mut c_char, length: c_int, capacity: c_int) -> cc_string {
115    cc_string {
116        buffer,
117        length: length as _,
118        capacity: capacity as _,
119    }
120}
121
122#[allow(clippy::missing_safety_doc)]
123pub unsafe fn UNSAFE_GetString(data: &[u8]) -> cc_string {
124    let mut length = 0;
125    for i in (0..STRING_SIZE).rev() {
126        let code = data[i as usize];
127        if code == b'\0' || code == b' ' {
128            continue;
129        } else {
130            length = i + 1;
131            break;
132        }
133    }
134
135    unsafe {
136        String_Init(
137            data.as_ptr() as *mut c_char,
138            length as c_int,
139            STRING_SIZE as c_int,
140        )
141    }
142}
143
144#[test]
145fn test_get_string() {
146    unsafe {
147        let mut s =
148            b"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl0000000000000000"
149                .to_vec();
150        s.resize(STRING_SIZE as usize, 0);
151        assert_eq!(
152            UNSAFE_GetString(&s).to_string(),
153            "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl"
154        );
155    }
156}
157
158pub const controlChars: &[cc_unichar] = &[
159    0x0000, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25D8, 0x25CB, 0x25D9, 0x2642,
160    0x2640, 0x266A, 0x266B, 0x263C, 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8,
161    0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC,
162];
163
164pub const extendedChars: &[cc_unichar] = &[
165    0x2302, 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8,
166    0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB,
167    0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, 0x00E1, 0x00ED, 0x00F3,
168    0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB,
169    0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551,
170    0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E,
171    0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565,
172    0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590,
173    0x2580, 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9,
174    0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7,
175    0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0,
176];
177
178pub fn Convert_CP437ToUnicode(raw: u8) -> cc_unichar {
179    if raw < 0x20 {
180        controlChars[raw as usize]
181    } else if raw < 0x7F {
182        raw as cc_unichar
183    } else {
184        extendedChars[raw as usize - 0x7F]
185    }
186}
187
188pub fn Convert_CodepointToCP437(cp: cc_codepoint) -> u8 {
189    let mut c: u8 = 0;
190    Convert_TryCodepointToCP437(cp, &mut c);
191    c
192}
193
194fn ReduceEmoji(cp: cc_codepoint) -> cc_codepoint {
195    if cp == 0x1F31E {
196        return 0x263C;
197    }
198    if cp == 0x1F3B5 {
199        return 0x266B;
200    }
201    if cp == 0x1F642 {
202        return 0x263A;
203    }
204
205    if cp == 0x1F600 || cp == 0x1F601 || cp == 0x1F603 {
206        return 0x263A;
207    }
208    if cp == 0x1F604 || cp == 0x1F606 || cp == 0x1F60A {
209        return 0x263A;
210    }
211    cp
212}
213
214fn Convert_TryCodepointToCP437(mut cp: cc_codepoint, c: &mut u8) -> bool {
215    if (0x20..0x7F).contains(&cp) {
216        *c = cp as u8;
217        return true;
218    }
219    if cp >= 0x1F000 {
220        cp = ReduceEmoji(cp);
221    }
222
223    for (i, &chr) in controlChars.iter().enumerate() {
224        if chr as cc_codepoint == cp {
225            *c = i as u8;
226            return true;
227        }
228    }
229
230    for (i, &chr) in extendedChars.iter().enumerate() {
231        if chr as cc_codepoint == cp {
232            *c = (i + 0x7F) as u8;
233            return true;
234        }
235    }
236
237    *c = b'?';
238    false
239}
240
241#[test]
242fn test_cp_437_conversion() {
243    let bytes: &[u8] = &[97, 236, 236]; // "a∞∞"
244
245    let c_str = CString::new(bytes).unwrap();
246    let a = cc_string {
247        buffer: c_str.as_ptr() as *mut c_char,
248        length: bytes.len() as cc_uint16,
249        capacity: bytes.len() as cc_uint16,
250    };
251    assert_eq!(a.to_string(), "a∞∞");
252
253    let str = "a∞∞";
254    let s = OwnedString::new(str);
255    unsafe {
256        assert_eq!(
257            slice::from_raw_parts(
258                s.as_cc_string().buffer as *const u8,
259                s.as_cc_string().length as usize
260            ),
261            bytes
262        );
263    }
264}