sky_save/
encoding.rs

1//! Handles strings represented by the PMD character encoding.
2//!
3//! Save file strings have one byte per character and are encoded using a custom character encoding, which is a mix of ASCII, Unicode and special sequences.
4//! Special sequences are wrapped in square brackets.
5//!
6//! Example: The byte representation for the sequence `Abcd[END]` will be `[0x41, 0x62, 0x63, 0x64, 0x00]`.
7//! The character `[` is not a valid PMD character, making parsing special sequences easier.
8//!
9//! See <https://projectpokemon.org/home/docs/mystery-dungeon-nds/explorers-of-sky-save-structure-r62> for more information.
10
11use crate::EncodingError;
12use arrayvec::ArrayVec;
13use bitvec::field::BitField;
14use bitvec::order::Lsb0;
15use bitvec::prelude::BitSlice;
16use std::fmt::Display;
17
18/// A single PMD-encoded character.
19/// Holds both the PMD encoded byte and its UTF-8 representation.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
21pub struct PmdChar {
22    /// The PMD encoded byte.
23    pub pmd: u8,
24    /// The UTF-8 representation of the character.
25    pub utf8: char,
26}
27
28impl PmdChar {
29    /// Parses a single character or a special sequence into a `PmdChar`.
30    pub fn from_sequence(seq: &str) -> Result<Self, EncodingError> {
31        let pmd = pmd_seq_to_byte(seq)?;
32
33        let utf8 = match seq.chars().next() {
34            Some('[') => pmd as char,
35            Some(c) => c,
36            _ => unreachable!(), // This is safe, seq is valid and not empty at this point.
37        };
38
39        Ok(PmdChar { pmd, utf8 })
40    }
41
42    /// Converts a PMD character to its sequence representation.
43    pub fn to_sequence(&self) -> String {
44        byte_to_pmd_seq(self.pmd).unwrap().to_string()
45    }
46}
47
48/// Converts a PMD-encoded byte to a PMD character.
49impl From<u8> for PmdChar {
50    fn from(value: u8) -> Self {
51        let pmd = byte_to_pmd_seq(value).unwrap();
52        PmdChar::from_sequence(pmd).unwrap()
53    }
54}
55
56/// A string represented by the PMD character encoding, backed by an `ArrayVec`.
57/// Save file strings (team names, Pokémon names) have 10 byte memory location.
58/// The game stops displaying the strings when it reaches a null byte.
59#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
60pub struct PmdString(ArrayVec<PmdChar, 10>);
61
62impl PmdString {
63    pub fn new() -> Self {
64        Self(ArrayVec::new())
65    }
66
67    /// Converts the string to a sequence of PMD characters.
68    pub fn to_sequence(&self) -> String {
69        self.0.iter().map(|&c| c.to_sequence()).collect()
70    }
71
72    /// Converts to a 10-byte array of PMD encoded bytes.
73    pub fn to_save_bytes(&self) -> [u8; 10] {
74        self.0
75            .iter()
76            .enumerate()
77            .fold([0; 10], |mut result, (i, c)| {
78                result[i] = c.pmd;
79                result
80            })
81    }
82
83    pub fn to_string_until_nul(&self) -> String {
84        self.0
85            .iter()
86            .map_while(|&c| (c.pmd != 0).then_some(c.utf8))
87            .collect()
88    }
89}
90
91/// Displays a UTF-8 representation of the `PmdString`.
92/// Does not ignore null bytes.
93impl Display for PmdString {
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        write!(f, "{}", self.0.iter().map(|&c| c.utf8).collect::<String>())
96    }
97}
98
99impl Default for PmdString {
100    fn default() -> Self {
101        PmdString::new()
102    }
103}
104
105/// Converts a PMD-encoded byte slice to a `PmdString`.
106impl From<&[u8]> for PmdString {
107    fn from(value: &[u8]) -> Self {
108        let mut result = PmdString::new();
109        for &b in value {
110            result.0.push(PmdChar::from(b));
111        }
112
113        result
114    }
115}
116
117impl From<&BitSlice<u8, Lsb0>> for PmdString {
118    fn from(value: &BitSlice<u8, Lsb0>) -> Self {
119        let bytes: u128 = value.load_le();
120        let bytes = bytes.to_le_bytes();
121
122        PmdString::from(&bytes.as_slice()[0..10])
123    }
124}
125
126/// Converts a PMD-encoded string to a byte vector.
127/// Does not ignore null bytes.
128/// Does not fill the vector to 10 bytes.
129impl From<PmdString> for Vec<u8> {
130    fn from(value: PmdString) -> Self {
131        value.0.iter().map(|c| c.pmd).collect::<Vec<_>>()
132    }
133}
134
135/// Parses a sequence of PMD characters to a `PmdString`.
136impl TryFrom<&str> for PmdString {
137    type Error = EncodingError;
138
139    fn try_from(value: &str) -> Result<Self, Self::Error> {
140        let mut result = PmdString::new();
141        let mut chars_iter = value.chars().peekable();
142
143        while let Some(c) = chars_iter.next() {
144            match c {
145                '[' => {
146                    let seq: String = chars_iter.by_ref().take_while(|&c| c != ']').collect();
147                    let pmd = pmd_seq_to_byte(&format!("[{}]", seq))?;
148                    result
149                        .0
150                        .try_push(PmdChar {
151                            utf8: pmd as char,
152                            pmd,
153                        })
154                        .map_err(|_| EncodingError::InvalidPmdStringLen)?;
155                }
156                _ => {
157                    let mut buf = [0; 4];
158                    let seq = c.encode_utf8(&mut buf);
159                    let pmd = pmd_seq_to_byte(seq)?;
160                    result
161                        .0
162                        .try_push(PmdChar { utf8: c, pmd })
163                        .map_err(|_| EncodingError::InvalidPmdStringLen)?;
164                }
165            }
166        }
167
168        Ok(result)
169    }
170}
171
172fn pmd_seq_to_byte(s: &str) -> Result<u8, EncodingError> {
173    match s {
174        "[END]" => Ok(0x00),
175        "[$01]" => Ok(0x01),
176        "[$02]" => Ok(0x02),
177        "[$03]" => Ok(0x03),
178        "[$04]" => Ok(0x04),
179        "[$05]" => Ok(0x05),
180        "[$06]" => Ok(0x06),
181        "[$07]" => Ok(0x07),
182        "[$08]" => Ok(0x08),
183        "[$09]" => Ok(0x09),
184        "[$0A]" => Ok(0x0A),
185        "[$0B]" => Ok(0x0B),
186        "[$0C]" => Ok(0x0C),
187        "[$0D]" => Ok(0x0D),
188        "[$0E]" => Ok(0x0E),
189        "[$0F]" => Ok(0x0F),
190        "[$10]" => Ok(0x10),
191        "[$11]" => Ok(0x11),
192        "[$12]" => Ok(0x12),
193        "[$13]" => Ok(0x13),
194        "[$14]" => Ok(0x14),
195        "[$15]" => Ok(0x15),
196        "[$16]" => Ok(0x16),
197        "[$17]" => Ok(0x17),
198        "[$18]" => Ok(0x18),
199        "[$19]" => Ok(0x19),
200        "[$1A]" => Ok(0x1A),
201        "[$1B]" => Ok(0x1B),
202        "[$1C]" => Ok(0x1C),
203        "[$1D]" => Ok(0x1D),
204        "[$1E]" => Ok(0x1E),
205        "[$1F]" => Ok(0x1F),
206        " " => Ok(0x20),
207        "!" => Ok(0x21),
208        "\"" => Ok(0x22),
209        "#" => Ok(0x23),
210        "$" => Ok(0x24),
211        "%" => Ok(0x25),
212        "&" => Ok(0x26),
213        "'" => Ok(0x27),
214        "(" => Ok(0x28),
215        ")" => Ok(0x29),
216        "*" => Ok(0x2A),
217        "+" => Ok(0x2B),
218        "," => Ok(0x2C),
219        "-" => Ok(0x2D),
220        "." => Ok(0x2E),
221        "/" => Ok(0x2F),
222        "0" => Ok(0x30),
223        "1" => Ok(0x31),
224        "2" => Ok(0x32),
225        "3" => Ok(0x33),
226        "4" => Ok(0x34),
227        "5" => Ok(0x35),
228        "6" => Ok(0x36),
229        "7" => Ok(0x37),
230        "8" => Ok(0x38),
231        "9" => Ok(0x39),
232        ":" => Ok(0x3A),
233        ";" => Ok(0x3B),
234        "<" => Ok(0x3C),
235        "=" => Ok(0x3D),
236        ">" => Ok(0x3E),
237        "?" => Ok(0x3F),
238        "@" => Ok(0x40),
239        "A" => Ok(0x41),
240        "B" => Ok(0x42),
241        "C" => Ok(0x43),
242        "D" => Ok(0x44),
243        "E" => Ok(0x45),
244        "F" => Ok(0x46),
245        "G" => Ok(0x47),
246        "H" => Ok(0x48),
247        "I" => Ok(0x49),
248        "J" => Ok(0x4A),
249        "K" => Ok(0x4B),
250        "L" => Ok(0x4C),
251        "M" => Ok(0x4D),
252        "N" => Ok(0x4E),
253        "O" => Ok(0x4F),
254        "P" => Ok(0x50),
255        "Q" => Ok(0x51),
256        "R" => Ok(0x52),
257        "S" => Ok(0x53),
258        "T" => Ok(0x54),
259        "U" => Ok(0x55),
260        "V" => Ok(0x56),
261        "W" => Ok(0x57),
262        "X" => Ok(0x58),
263        "Y" => Ok(0x59),
264        "Z" => Ok(0x5A),
265        "[$5B]" => Ok(0x5B),
266        "\\" => Ok(0x5C),
267        "]" => Ok(0x5D),
268        "^" => Ok(0x5E),
269        "_" => Ok(0x5F),
270        "`" => Ok(0x60),
271        "a" => Ok(0x61),
272        "b" => Ok(0x62),
273        "c" => Ok(0x63),
274        "d" => Ok(0x64),
275        "e" => Ok(0x65),
276        "f" => Ok(0x66),
277        "g" => Ok(0x67),
278        "h" => Ok(0x68),
279        "i" => Ok(0x69),
280        "j" => Ok(0x6A),
281        "k" => Ok(0x6B),
282        "l" => Ok(0x6C),
283        "m" => Ok(0x6D),
284        "n" => Ok(0x6E),
285        "o" => Ok(0x6F),
286        "p" => Ok(0x70),
287        "q" => Ok(0x71),
288        "r" => Ok(0x72),
289        "s" => Ok(0x73),
290        "t" => Ok(0x74),
291        "u" => Ok(0x75),
292        "v" => Ok(0x76),
293        "w" => Ok(0x77),
294        "x" => Ok(0x78),
295        "y" => Ok(0x79),
296        "z" => Ok(0x7A),
297        "{" => Ok(0x7B),
298        "|" => Ok(0x7C),
299        "}" => Ok(0x7D),
300        "[$7E]" => Ok(0x7E),
301        "[$7F]" => Ok(0x7F),
302        "€" => Ok(0x80),
303        "[$81]" => Ok(0x81),
304        "[$82]" => Ok(0x82),
305        "[$83]" => Ok(0x83),
306        "[$84]" => Ok(0x84),
307        "…" => Ok(0x85),
308        "†" => Ok(0x86),
309        "[$87]" => Ok(0x87),
310        "ˆ" => Ok(0x88),
311        "‰" => Ok(0x89),
312        "Š" => Ok(0x8A),
313        "‹" => Ok(0x8B),
314        "Œ" => Ok(0x8C),
315        "[e]" => Ok(0x8D),
316        "Ž" => Ok(0x8E),
317        "[è]" => Ok(0x8F),
318        // "•" => Ok(0x90), // Duplicate
319        "‘" => Ok(0x91),
320        "’" => Ok(0x92),
321        "“" => Ok(0x93),
322        "”" => Ok(0x94),
323        // "•" => Ok(0x95), // Duplicate
324        "[er]" => Ok(0x96),
325        "[re]" => Ok(0x97),
326        "~" => Ok(0x98),
327        "™" => Ok(0x99),
328        "š" => Ok(0x9A),
329        "›" => Ok(0x9B),
330        "œ" => Ok(0x9C),
331        "•" => Ok(0x9D),
332        "ž" => Ok(0x9E),
333        "Ÿ" => Ok(0x9F),
334        // " " => Ok(0xA0), // Duplicate
335        "¡" => Ok(0xA1),
336        "¢" => Ok(0xA2),
337        "£" => Ok(0xA3),
338        "¤" => Ok(0xA4),
339        "¥" => Ok(0xA5),
340        "¦" => Ok(0xA6),
341        "§" => Ok(0xA7),
342        "¨" => Ok(0xA8),
343        "©" => Ok(0xA9),
344        "ª" => Ok(0xAA),
345        "«" => Ok(0xAB),
346        "¬" => Ok(0xAC),
347        "\u{00AD}" => Ok(0xAD),
348        "®" => Ok(0xAE),
349        "¯" => Ok(0xAF),
350        "°" => Ok(0xB0),
351        "±" => Ok(0xB1),
352        "²" => Ok(0xB2),
353        "³" => Ok(0xB3),
354        "´" => Ok(0xB4),
355        "µ" => Ok(0xB5),
356        "¶" => Ok(0xB6),
357        "„" => Ok(0xB7),
358        "‚" => Ok(0xB8),
359        "¹" => Ok(0xB9),
360        "º" => Ok(0xBA),
361        "»" => Ok(0xBB),
362        "←" => Ok(0xBC),
363        "♂" => Ok(0xBD),
364        "♀" => Ok(0xBE),
365        "¿" => Ok(0xBF),
366        "À" => Ok(0xC0),
367        "Á" => Ok(0xC1),
368        "Â" => Ok(0xC2),
369        "Ã" => Ok(0xC3),
370        "Ä" => Ok(0xC4),
371        "Å" => Ok(0xC5),
372        "Æ" => Ok(0xC6),
373        "Ç" => Ok(0xC7),
374        "È" => Ok(0xC8),
375        "É" => Ok(0xC9),
376        "Ê" => Ok(0xCA),
377        "Ë" => Ok(0xCB),
378        "Ì" => Ok(0xCC),
379        "Í" => Ok(0xCD),
380        "Î" => Ok(0xCE),
381        "Ï" => Ok(0xCF),
382        "Ð" => Ok(0xD0),
383        "Ñ" => Ok(0xD1),
384        "Ò" => Ok(0xD2),
385        "Ó" => Ok(0xD3),
386        "Ô" => Ok(0xD4),
387        "Õ" => Ok(0xD5),
388        "Ö" => Ok(0xD6),
389        "×" => Ok(0xD7),
390        "Ø" => Ok(0xD8),
391        "Ù" => Ok(0xD9),
392        "Ú" => Ok(0xDA),
393        "Û" => Ok(0xDB),
394        "Ü" => Ok(0xDC),
395        "Ý" => Ok(0xDD),
396        "Þ" => Ok(0xDE),
397        "ß" => Ok(0xDF),
398        "à" => Ok(0xE0),
399        "á" => Ok(0xE1),
400        "â" => Ok(0xE2),
401        "ã" => Ok(0xE3),
402        "ä" => Ok(0xE4),
403        "å" => Ok(0xE5),
404        "æ" => Ok(0xE6),
405        "ç" => Ok(0xE7),
406        "è" => Ok(0xE8),
407        "é" => Ok(0xE9),
408        "ê" => Ok(0xEA),
409        "ë" => Ok(0xEB),
410        "ì" => Ok(0xEC),
411        "í" => Ok(0xED),
412        "î" => Ok(0xEE),
413        "ï" => Ok(0xEF),
414        "ð" => Ok(0xF0),
415        "ñ" => Ok(0xF1),
416        "ò" => Ok(0xF2),
417        "ó" => Ok(0xF3),
418        "ô" => Ok(0xF4),
419        "õ" => Ok(0xF5),
420        "ö" => Ok(0xF6),
421        "÷" => Ok(0xF7),
422        "ø" => Ok(0xF8),
423        "ù" => Ok(0xF9),
424        "ú" => Ok(0xFA),
425        "û" => Ok(0xFB),
426        "ü" => Ok(0xFC),
427        "ý" => Ok(0xFD),
428        "þ" => Ok(0xFE),
429        "ÿ" => Ok(0xFF),
430        _ => Err(EncodingError::InvalidPmdCharacter(s.to_string())),
431    }
432}
433
434fn byte_to_pmd_seq(byte: u8) -> Result<&'static str, EncodingError> {
435    match byte {
436        0x00 => Ok("[END]"),
437        0x01 => Ok("[$01]"),
438        0x02 => Ok("[$02]"),
439        0x03 => Ok("[$03]"),
440        0x04 => Ok("[$04]"),
441        0x05 => Ok("[$05]"),
442        0x06 => Ok("[$06]"),
443        0x07 => Ok("[$07]"),
444        0x08 => Ok("[$08]"),
445        0x09 => Ok("[$09]"),
446        0x0A => Ok("[$0A]"),
447        0x0B => Ok("[$0B]"),
448        0x0C => Ok("[$0C]"),
449        0x0D => Ok("[$0D]"),
450        0x0E => Ok("[$0E]"),
451        0x0F => Ok("[$0F]"),
452        0x10 => Ok("[$10]"),
453        0x11 => Ok("[$11]"),
454        0x12 => Ok("[$12]"),
455        0x13 => Ok("[$13]"),
456        0x14 => Ok("[$14]"),
457        0x15 => Ok("[$15]"),
458        0x16 => Ok("[$16]"),
459        0x17 => Ok("[$17]"),
460        0x18 => Ok("[$18]"),
461        0x19 => Ok("[$19]"),
462        0x1A => Ok("[$1A]"),
463        0x1B => Ok("[$1B]"),
464        0x1C => Ok("[$1C]"),
465        0x1D => Ok("[$1D]"),
466        0x1E => Ok("[$1E]"),
467        0x1F => Ok("[$1F]"),
468        0x20 => Ok(" "),
469        0x21 => Ok("!"),
470        0x22 => Ok("\""),
471        0x23 => Ok("#"),
472        0x24 => Ok("$"),
473        0x25 => Ok("%"),
474        0x26 => Ok("&"),
475        0x27 => Ok("'"),
476        0x28 => Ok("("),
477        0x29 => Ok(")"),
478        0x2A => Ok("*"),
479        0x2B => Ok("+"),
480        0x2C => Ok(","),
481        0x2D => Ok("-"),
482        0x2E => Ok("."),
483        0x2F => Ok("/"),
484        0x30 => Ok("0"),
485        0x31 => Ok("1"),
486        0x32 => Ok("2"),
487        0x33 => Ok("3"),
488        0x34 => Ok("4"),
489        0x35 => Ok("5"),
490        0x36 => Ok("6"),
491        0x37 => Ok("7"),
492        0x38 => Ok("8"),
493        0x39 => Ok("9"),
494        0x3A => Ok(":"),
495        0x3B => Ok(";"),
496        0x3C => Ok("<"),
497        0x3D => Ok("="),
498        0x3E => Ok(">"),
499        0x3F => Ok("?"),
500        0x40 => Ok("@"),
501        0x41 => Ok("A"),
502        0x42 => Ok("B"),
503        0x43 => Ok("C"),
504        0x44 => Ok("D"),
505        0x45 => Ok("E"),
506        0x46 => Ok("F"),
507        0x47 => Ok("G"),
508        0x48 => Ok("H"),
509        0x49 => Ok("I"),
510        0x4A => Ok("J"),
511        0x4B => Ok("K"),
512        0x4C => Ok("L"),
513        0x4D => Ok("M"),
514        0x4E => Ok("N"),
515        0x4F => Ok("O"),
516        0x50 => Ok("P"),
517        0x51 => Ok("Q"),
518        0x52 => Ok("R"),
519        0x53 => Ok("S"),
520        0x54 => Ok("T"),
521        0x55 => Ok("U"),
522        0x56 => Ok("V"),
523        0x57 => Ok("W"),
524        0x58 => Ok("X"),
525        0x59 => Ok("Y"),
526        0x5A => Ok("Z"),
527        0x5B => Ok("[$5B]"),
528        0x5C => Ok("\\"),
529        0x5D => Ok("]"),
530        0x5E => Ok("^"),
531        0x5F => Ok("_"),
532        0x60 => Ok("`"),
533        0x61 => Ok("a"),
534        0x62 => Ok("b"),
535        0x63 => Ok("c"),
536        0x64 => Ok("d"),
537        0x65 => Ok("e"),
538        0x66 => Ok("f"),
539        0x67 => Ok("g"),
540        0x68 => Ok("h"),
541        0x69 => Ok("i"),
542        0x6A => Ok("j"),
543        0x6B => Ok("k"),
544        0x6C => Ok("l"),
545        0x6D => Ok("m"),
546        0x6E => Ok("n"),
547        0x6F => Ok("o"),
548        0x70 => Ok("p"),
549        0x71 => Ok("q"),
550        0x72 => Ok("r"),
551        0x73 => Ok("s"),
552        0x74 => Ok("t"),
553        0x75 => Ok("u"),
554        0x76 => Ok("v"),
555        0x77 => Ok("w"),
556        0x78 => Ok("x"),
557        0x79 => Ok("y"),
558        0x7A => Ok("z"),
559        0x7B => Ok("{"),
560        0x7C => Ok("|"),
561        0x7D => Ok("}"),
562        0x7E => Ok("[$7E]"),
563        0x7F => Ok("[$7F]"),
564        0x80 => Ok("€"),
565        0x81 => Ok("[$81]"),
566        0x82 => Ok("[$82]"),
567        0x83 => Ok("[$83]"),
568        0x84 => Ok("[$84]"),
569        0x85 => Ok("…"),
570        0x86 => Ok("†"),
571        0x87 => Ok("[$87]"),
572        0x88 => Ok("ˆ"),
573        0x89 => Ok("‰"),
574        0x8A => Ok("Š"),
575        0x8B => Ok("‹"),
576        0x8C => Ok("Œ"),
577        0x8D => Ok("[e]"),
578        0x8E => Ok("Ž"),
579        0x8F => Ok("[è]"),
580        0x90 => Ok("•"),
581        0x91 => Ok("‘"),
582        0x92 => Ok("’"),
583        0x93 => Ok("“"),
584        0x94 => Ok("”"),
585        0x95 => Ok("•"),
586        0x96 => Ok("[er]"),
587        0x97 => Ok("[re]"),
588        0x98 => Ok("~"),
589        0x99 => Ok("™"),
590        0x9A => Ok("š"),
591        0x9B => Ok("›"),
592        0x9C => Ok("œ"),
593        0x9D => Ok("•"),
594        0x9E => Ok("ž"),
595        0x9F => Ok("Ÿ"),
596        0xA0 => Ok(" "),
597        0xA1 => Ok("¡"),
598        0xA2 => Ok("¢"),
599        0xA3 => Ok("£"),
600        0xA4 => Ok("¤"),
601        0xA5 => Ok("¥"),
602        0xA6 => Ok("¦"),
603        0xA7 => Ok("§"),
604        0xA8 => Ok("¨"),
605        0xA9 => Ok("©"),
606        0xAA => Ok("ª"),
607        0xAB => Ok("«"),
608        0xAC => Ok("¬"),
609        0xAD => Ok("\u{00AD}"),
610        0xAE => Ok("®"),
611        0xAF => Ok("¯"),
612        0xB0 => Ok("°"),
613        0xB1 => Ok("±"),
614        0xB2 => Ok("²"),
615        0xB3 => Ok("³"),
616        0xB4 => Ok("´"),
617        0xB5 => Ok("µ"),
618        0xB6 => Ok("¶"),
619        0xB7 => Ok("„"),
620        0xB8 => Ok("‚"),
621        0xB9 => Ok("¹"),
622        0xBA => Ok("º"),
623        0xBB => Ok("»"),
624        0xBC => Ok("←"),
625        0xBD => Ok("♂"),
626        0xBE => Ok("♀"),
627        0xBF => Ok("¿"),
628        0xC0 => Ok("À"),
629        0xC1 => Ok("Á"),
630        0xC2 => Ok("Â"),
631        0xC3 => Ok("Ã"),
632        0xC4 => Ok("Ä"),
633        0xC5 => Ok("Å"),
634        0xC6 => Ok("Æ"),
635        0xC7 => Ok("Ç"),
636        0xC8 => Ok("È"),
637        0xC9 => Ok("É"),
638        0xCA => Ok("Ê"),
639        0xCB => Ok("Ë"),
640        0xCC => Ok("Ì"),
641        0xCD => Ok("Í"),
642        0xCE => Ok("Î"),
643        0xCF => Ok("Ï"),
644        0xD0 => Ok("Ð"),
645        0xD1 => Ok("Ñ"),
646        0xD2 => Ok("Ò"),
647        0xD3 => Ok("Ó"),
648        0xD4 => Ok("Ô"),
649        0xD5 => Ok("Õ"),
650        0xD6 => Ok("Ö"),
651        0xD7 => Ok("×"),
652        0xD8 => Ok("Ø"),
653        0xD9 => Ok("Ù"),
654        0xDA => Ok("Ú"),
655        0xDB => Ok("Û"),
656        0xDC => Ok("Ü"),
657        0xDD => Ok("Ý"),
658        0xDE => Ok("Þ"),
659        0xDF => Ok("ß"),
660        0xE0 => Ok("à"),
661        0xE1 => Ok("á"),
662        0xE2 => Ok("â"),
663        0xE3 => Ok("ã"),
664        0xE4 => Ok("ä"),
665        0xE5 => Ok("å"),
666        0xE6 => Ok("æ"),
667        0xE7 => Ok("ç"),
668        0xE8 => Ok("è"),
669        0xE9 => Ok("é"),
670        0xEA => Ok("ê"),
671        0xEB => Ok("ë"),
672        0xEC => Ok("ì"),
673        0xED => Ok("í"),
674        0xEE => Ok("î"),
675        0xEF => Ok("ï"),
676        0xF0 => Ok("ð"),
677        0xF1 => Ok("ñ"),
678        0xF2 => Ok("ò"),
679        0xF3 => Ok("ó"),
680        0xF4 => Ok("ô"),
681        0xF5 => Ok("õ"),
682        0xF6 => Ok("ö"),
683        0xF7 => Ok("÷"),
684        0xF8 => Ok("ø"),
685        0xF9 => Ok("ù"),
686        0xFA => Ok("ú"),
687        0xFB => Ok("û"),
688        0xFC => Ok("ü"),
689        0xFD => Ok("ý"),
690        0xFE => Ok("þ"),
691        0xFF => Ok("ÿ"),
692    }
693}
694
695#[test]
696fn test_char_round_trip() {
697    let ch = PmdChar::from_sequence("A").unwrap();
698    assert_eq!(ch.utf8, 'A');
699    assert_eq!(ch.pmd, 0x41);
700    let pmd = PmdChar::from(0x41);
701    assert_eq!(ch, pmd);
702}
703
704#[test]
705fn test_char_special_trip() {
706    let ch = PmdChar::from_sequence("[er]").unwrap();
707    assert_eq!(ch.utf8, '\u{96}');
708    assert_eq!(ch.pmd, 0x96);
709    let pmd = PmdChar::from(0x96);
710    assert_eq!(ch, pmd);
711}
712
713#[test]
714#[should_panic]
715
716fn test_char_invalid_sequence() {
717    PmdChar::from_sequence("[LOL]").unwrap();
718}
719
720#[test]
721fn test_char_to_sequence() {
722    let ch = PmdChar::from(0x8D);
723    assert_eq!(ch.to_sequence(), "[e]");
724}
725
726#[test]
727fn test_pmd_string_parse() {
728    let seq = "Oak[END]";
729    PmdString::try_from(seq).unwrap();
730}
731
732#[test]
733#[should_panic]
734fn test_pmd_string_invalid_len() {
735    PmdString::try_from("OakOakOakOak").unwrap();
736}
737
738#[test]
739fn test_pmd_string_to_sequence() {
740    let pmd = PmdString::from([0x00, 0x00, 0x00, 0xC4, 0x88, 0x7E].as_slice());
741    assert_eq!(pmd.to_sequence(), "[END][END][END]Ĉ[$7E]");
742}
743
744#[test]
745fn test_pmd_string_to_string() {
746    let pmd = PmdString::from([0x00, 0x00, 0x00, 0xC4, 0x88, 0x7E].as_slice());
747    assert_eq!(pmd.to_string(), "\0\0\0Ĉ~");
748}
749
750#[test]
751fn test_pmd_string_to_save_bytes() {
752    let pmd = PmdString::from([0x00, 0x00, 0x00, 0xC4, 0x88, 0x7E].as_slice());
753    assert_eq!(
754        pmd.to_save_bytes(),
755        [0x00, 0x00, 0x00, 0xC4, 0x88, 0x7E, 0x00, 0x00, 0x00, 0x00]
756    );
757}
758
759#[test]
760fn test_pmd_string_to_vec() {
761    let pmd = PmdString::from([0xC4, 0x88, 0x7E].as_slice());
762    let vec = Vec::from(pmd);
763
764    assert_eq!(vec.as_slice(), &[0xC4, 0x88, 0x7E]);
765}