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 .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 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 }
111
112pub 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#[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#[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
149pub 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]; 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}