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, String_CopyToRaw,
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/// Calls `String_CopyToRaw` on a compile time array.
150///
151/// # Panics
152///
153/// The function will panic if `buffer.len()` is outside the range of `c_int`.
154pub fn String_CopyToRawArray(buffer: &mut [c_char], src: &cc_string) {
155    unsafe {
156        String_CopyToRaw(
157            buffer.as_mut_ptr(),
158            c_int::try_from(buffer.len()).unwrap(),
159            src,
160        );
161    }
162}
163
164#[must_use]
165pub unsafe fn UNSAFE_GetString(data: &[u8]) -> cc_string {
166    let mut length = 0;
167    for i in (0..STRING_SIZE).rev() {
168        let code = data[i as usize];
169        if code == b'\0' || code == b' ' {
170            continue;
171        } else {
172            length = i + 1;
173            break;
174        }
175    }
176
177    unsafe {
178        String_Init(
179            data.as_ptr() as *mut c_char,
180            length as c_int,
181            STRING_SIZE as c_int,
182        )
183    }
184}
185
186#[test]
187fn test_get_string() {
188    unsafe {
189        let mut s =
190            b"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl0000000000000000"
191                .to_vec();
192        s.resize(STRING_SIZE as usize, 0);
193        assert_eq!(
194            UNSAFE_GetString(&s).to_string(),
195            "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl"
196        );
197    }
198}
199
200pub const controlChars: &[cc_unichar] = &[
201    0x0000, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25D8, 0x25CB, 0x25D9, 0x2642,
202    0x2640, 0x266A, 0x266B, 0x263C, 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8,
203    0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC,
204];
205
206pub const extendedChars: &[cc_unichar] = &[
207    0x2302, 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8,
208    0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB,
209    0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, 0x00E1, 0x00ED, 0x00F3,
210    0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB,
211    0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551,
212    0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E,
213    0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565,
214    0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590,
215    0x2580, 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9,
216    0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7,
217    0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0,
218];
219
220pub fn Convert_CP437ToUnicode(raw: u8) -> cc_unichar {
221    if raw < 0x20 {
222        controlChars[raw as usize]
223    } else if raw < 0x7F {
224        raw as cc_unichar
225    } else {
226        extendedChars[raw as usize - 0x7F]
227    }
228}
229
230pub fn Convert_CodepointToCP437(cp: cc_codepoint) -> u8 {
231    let mut c: u8 = 0;
232    Convert_TryCodepointToCP437(cp, &mut c);
233    c
234}
235
236fn ReduceEmoji(cp: cc_codepoint) -> cc_codepoint {
237    if cp == 0x1F31E {
238        return 0x263C;
239    }
240    if cp == 0x1F3B5 {
241        return 0x266B;
242    }
243    if cp == 0x1F642 {
244        return 0x263A;
245    }
246
247    if cp == 0x1F600 || cp == 0x1F601 || cp == 0x1F603 {
248        return 0x263A;
249    }
250    if cp == 0x1F604 || cp == 0x1F606 || cp == 0x1F60A {
251        return 0x263A;
252    }
253    cp
254}
255
256fn Convert_TryCodepointToCP437(mut cp: cc_codepoint, c: &mut u8) -> bool {
257    if (0x20..0x7F).contains(&cp) {
258        *c = cp as u8;
259        return true;
260    }
261    if cp >= 0x1F000 {
262        cp = ReduceEmoji(cp);
263    }
264
265    for (i, &chr) in controlChars.iter().enumerate() {
266        if chr as cc_codepoint == cp {
267            *c = i as u8;
268            return true;
269        }
270    }
271
272    for (i, &chr) in extendedChars.iter().enumerate() {
273        if chr as cc_codepoint == cp {
274            *c = (i + 0x7F) as u8;
275            return true;
276        }
277    }
278
279    *c = b'?';
280    false
281}
282
283#[test]
284fn test_cp_437_conversion() {
285    let bytes: &[u8] = &[97, 236, 236]; // "a∞∞"
286
287    let c_str = CString::new(bytes).unwrap();
288    let a = cc_string {
289        buffer: c_str.as_ptr() as *mut c_char,
290        length: bytes.len() as cc_uint16,
291        capacity: bytes.len() as cc_uint16,
292    };
293    assert_eq!(a.to_string(), "a∞∞");
294
295    let str = "a∞∞";
296    let s = OwnedString::new(str);
297    unsafe {
298        assert_eq!(
299            slice::from_raw_parts(
300                s.as_cc_string().buffer as *const u8,
301                s.as_cc_string().length as usize
302            ),
303            bytes
304        );
305    }
306}