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 .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 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 }
110
111pub 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]; 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}