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