classicube_sys/
string.rs

1use core::{
2    borrow::Borrow,
3    ffi::CStr,
4    fmt::{self, Display},
5    slice,
6};
7
8use crate::{
9    bindings::{cc_codepoint, cc_string, cc_uint16, cc_unichar, STRING_SIZE},
10    std_types::{c_char, c_int, Box, CString, String, ToString, Vec},
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    String_Init(
136        data.as_ptr() as *mut c_char,
137        length as c_int,
138        STRING_SIZE as c_int,
139    )
140}
141
142#[test]
143fn test_get_string() {
144    unsafe {
145        let mut s =
146            b"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl0000000000000000"
147                .to_vec();
148        s.resize(STRING_SIZE as usize, 0);
149        assert_eq!(
150            UNSAFE_GetString(&s).to_string(),
151            "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl"
152        );
153    }
154}
155
156pub const controlChars: &[cc_unichar] = &[
157    0x0000, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25D8, 0x25CB, 0x25D9, 0x2642,
158    0x2640, 0x266A, 0x266B, 0x263C, 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8,
159    0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC,
160];
161
162pub const extendedChars: &[cc_unichar] = &[
163    0x2302, 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8,
164    0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB,
165    0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, 0x00E1, 0x00ED, 0x00F3,
166    0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB,
167    0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551,
168    0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E,
169    0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565,
170    0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590,
171    0x2580, 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9,
172    0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7,
173    0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0,
174];
175
176pub fn Convert_CP437ToUnicode(raw: u8) -> cc_unichar {
177    if raw < 0x20 {
178        controlChars[raw as usize]
179    } else if raw < 0x7F {
180        raw as cc_unichar
181    } else {
182        extendedChars[raw as usize - 0x7F]
183    }
184}
185
186pub fn Convert_CodepointToCP437(cp: cc_codepoint) -> u8 {
187    let mut c: u8 = 0;
188    Convert_TryCodepointToCP437(cp, &mut c);
189    c
190}
191
192fn ReduceEmoji(cp: cc_codepoint) -> cc_codepoint {
193    if cp == 0x1F31E {
194        return 0x263C;
195    }
196    if cp == 0x1F3B5 {
197        return 0x266B;
198    }
199    if cp == 0x1F642 {
200        return 0x263A;
201    }
202
203    if cp == 0x1F600 || cp == 0x1F601 || cp == 0x1F603 {
204        return 0x263A;
205    }
206    if cp == 0x1F604 || cp == 0x1F606 || cp == 0x1F60A {
207        return 0x263A;
208    }
209    cp
210}
211
212fn Convert_TryCodepointToCP437(mut cp: cc_codepoint, c: &mut u8) -> bool {
213    if (0x20..0x7F).contains(&cp) {
214        *c = cp as u8;
215        return true;
216    }
217    if cp >= 0x1F000 {
218        cp = ReduceEmoji(cp);
219    }
220
221    for (i, &chr) in controlChars.iter().enumerate() {
222        if chr as cc_codepoint == cp {
223            *c = i as u8;
224            return true;
225        }
226    }
227
228    for (i, &chr) in extendedChars.iter().enumerate() {
229        if chr as cc_codepoint == cp {
230            *c = (i + 0x7F) as u8;
231            return true;
232        }
233    }
234
235    *c = b'?';
236    false
237}
238
239#[test]
240fn test_cp_437_conversion() {
241    let bytes: &[u8] = &[97, 236, 236]; // "a∞∞"
242
243    let c_str = CString::new(bytes).unwrap();
244    let a = cc_string {
245        buffer: c_str.as_ptr() as *mut c_char,
246        length: bytes.len() as cc_uint16,
247        capacity: bytes.len() as cc_uint16,
248    };
249    assert_eq!(a.to_string(), "a∞∞");
250
251    let str = "a∞∞";
252    let s = OwnedString::new(str);
253    unsafe {
254        assert_eq!(
255            slice::from_raw_parts(
256                s.as_cc_string().buffer as *const u8,
257                s.as_cc_string().length as usize
258            ),
259            bytes
260        );
261    }
262}