classicube_sys/
string.rs

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