diff --git a/pkstrings/src/lib.rs b/pkstrings/src/lib.rs
index 0d0906e..8d69eb8 100644
--- a/pkstrings/src/lib.rs
+++ b/pkstrings/src/lib.rs
@@ -13,218 +13,9 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-use std::convert::TryFrom;
-use std::fmt;
-use std::ops::Deref;
-
-const fn strtohex(chr: &char) -> Option {
- Some(match chr {
- cap @ 'A'..='Z' => 0x80 - ('A' as u8) + (*cap as u8),
- '(' => 0x9a,
- ')' => 0x9b,
- ':' => 0x9c,
- ';' => 0x9d,
- '[' => 0x9e,
- ']' => 0x9f,
- low @ 'a'..='z' => 0xa0 - ('a' as u8) + (*low as u8),
- '\'' => 0xe0,
- '-' => 0xe3,
- '?' => 0xe6,
- '!' => 0xe7,
- '.' => 0xe8,
- '▷' => 0xec,
- '▶' => 0xed,
- '▼' => 0xee,
- '♂' => 0xef,
- '×' => 0xf1,
- // TODO: Pattern currently unreachable. Figure something out.
- // In the Japanese games (as can be seen below), 0xF2 is distinguishable from 0xE8, with
- // the former meant as a decimal point while the latter is punctuation. Presumably this
- // intention was largely inherited when the English games were made, as most of the game's
- // script uses 0xE8 exclusively; however, 0xF2 appears in the character table for user
- // input, meaning it may appear in user-input names (and, conversely, 0xE8 never should).
- // Source: https://bulbapedia.bulbagarden.net/wiki/Character_encoding_in_Generation_I
- // '.' => 0xf2,
- '/' => 0xf3,
- ',' => 0xf4,
- '♀' => 0xf5,
- num @ '0'..='9' => 0xf6 - ('0' as u8) + (*num as u8),
- ' ' => 0x7f,
- '@' => 0x50,
- _ => return None,
- })
-}
-
-const fn hextostr(hex: u8) -> Option {
- Some(match hex {
- cap @ 0x80..=0x99 => (('A' as u8) + (cap - 0x80)) as char,
- 0x9a => '(',
- 0x9b => ')',
- 0x9c => ':',
- 0x9d => ';',
- 0x9e => '[',
- 0x9f => ']',
- low @ 0xa0..=0xb9 => (('a' as u8) + (low - 0xa0)) as char,
- 0xe0 => '\'',
- 0xe3 => '-',
- 0xe6 => '?',
- 0xe7 => '!',
- 0xe8 => '.',
- 0xec => '▷',
- 0xed => '▶',
- 0xee => '▼',
- 0xef => '♂',
- 0xf1 => '×',
- 0xf2 => '.',
- 0xf3 => '/',
- 0xf4 => ',',
- 0xf5 => '♀',
- num @ 0xf6..=0xff => (('0' as u8) + (num - 0xf6)) as char,
- 0x7f => ' ',
- 0x50 => '@',
- _ => return None,
- })
-}
-
-#[derive(Debug, Eq, PartialEq)]
-pub enum Error {
- InvalidCharacter,
-}
-
-impl fmt::Display for Error {
- fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
- match self {
- Error::InvalidCharacter => write!(f, "Invalid character"),
- }
- }
-}
-
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub struct PKString(String);
-
-impl PKString {
- pub fn into_string(self) -> String {
- self.0
- }
-}
-
-impl Deref for PKString {
- type Target = String;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-impl fmt::Display for PKString {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_fmt(format_args!("{}", &self.0))
- }
-}
-
-impl TryFrom for PKString {
- type Error = Error;
-
- fn try_from(ord: u8) -> Result {
- let mut buf = String::with_capacity(1);
-
- match hextostr(ord) {
- Some(chr) => buf.push(chr.clone()),
- None => return Err(Error::InvalidCharacter),
- }
-
- Ok(PKString(buf))
- }
-}
-
-impl TryFrom<&[u8]> for PKString {
- type Error = Error;
-
- fn try_from(data: &[u8]) -> Result {
- let mut buf = String::with_capacity(data.len());
- let placeholder = '_';
-
- for ord in data {
- if let Some(chr) = hextostr(*ord) {
- buf.push(chr.clone());
- } else {
- buf.push(placeholder);
- }
- }
-
- Ok(PKString(buf))
- }
-}
-
-impl Into> for PKString {
- fn into(self) -> Vec {
- let mut vec = Vec::with_capacity(self.len());
-
- for chr in self.0.chars() {
- if let Some(ord) = strtohex(&chr) {
- vec.push(ord);
- } else {
- // TODO: Change this.
- panic!();
- }
- }
-
- vec
- }
-}
-
-impl TryFrom<&str> for PKString {
- type Error = Error;
-
- fn try_from(data: &str) -> Result {
- PKString::try_from(String::from(data))
- }
-}
-
-impl TryFrom for PKString {
- type Error = Error;
-
- fn try_from(data: String) -> Result {
- for chr in data.chars() {
- if strtohex(&chr).is_none() {
- return Err(Error::InvalidCharacter);
- }
- }
-
- Ok(PKString(data))
- }
-}
+pub mod pkstring;
#[cfg(test)]
-mod tests {
- use crate::PKString;
- use std::convert::TryFrom;
+mod tests;
- #[test]
- fn test_from_ord() {
- let gary: &[u8] = &[0x86, 0x80, 0x91, 0x98];
- assert_eq!(PKString::try_from(gary), Ok(PKString(String::from("GARY"))));
-
- let party_nicks: &[u8] = &[
- 0x8a, 0x80, 0x83, 0x80, 0x81, 0x91, 0x80, 0x50, 0x50, 0x50, 0x50, 0x8d, 0x88, 0x83,
- 0x8e, 0x8a, 0x88, 0x8d, 0x86, 0x50, 0x50, 0x50, 0x81, 0x8b, 0x80, 0x92, 0x93, 0x8e,
- 0x88, 0x92, 0x84, 0x50, 0x50, 0x8e, 0x83, 0x83, 0x88, 0x92, 0x87, 0x50, 0x50, 0x50,
- 0x50, 0x50, 0x8f, 0x88, 0x83, 0x86, 0x84, 0x98, 0x50, 0x50, 0x50, 0x50, 0x50, 0x82,
- 0x87, 0x80, 0x91, 0x8c, 0x80, 0x8d, 0x83, 0x84, 0x91, 0x50,
- ];
- let result =
- String::from("KADABRA@@@@NIDOKING@@@BLASTOISE@@ODDISH@@@@@PIDGEY@@@@@CHARMANDER@");
- assert_eq!(PKString::try_from(party_nicks), Ok(PKString(result)));
- }
-
- #[test]
- fn test_from_chr() {
- let gary: Vec = vec![0x86, 0x80, 0x91, 0x98];
- let result: Vec = PKString(String::from("GARY")).into();
- assert_eq!(result, gary);
-
- let nidoranf: Vec = vec![0x8d, 0x88, 0x83, 0x8e, 0x91, 0x80, 0x8d, 0xef];
- let result: Vec = PKString(String::from("NIDORAN♂")).into();
- assert_eq!(result, nidoranf);
- }
-}
+pub use pkstring::PKString;
diff --git a/pkstrings/src/pkstring.rs b/pkstrings/src/pkstring.rs
new file mode 100644
index 0000000..82b6a48
--- /dev/null
+++ b/pkstrings/src/pkstring.rs
@@ -0,0 +1,208 @@
+// Copyright (C) 2021 "Maxime “pep” Buquet "
+//
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU Affero General Public License as published by the
+// Free Software Foundation, either version 3 of the License, or (at your
+// option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
+// for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+use std::convert::TryFrom;
+use std::fmt;
+use std::ops::Deref;
+
+const fn strtohex(chr: &char) -> Option {
+ Some(match chr {
+ cap @ 'A'..='Z' => 0x80 - ('A' as u8) + (*cap as u8),
+ '(' => 0x9a,
+ ')' => 0x9b,
+ ':' => 0x9c,
+ ';' => 0x9d,
+ '[' => 0x9e,
+ ']' => 0x9f,
+ low @ 'a'..='z' => 0xa0 - ('a' as u8) + (*low as u8),
+ '\'' => 0xe0,
+ '-' => 0xe3,
+ '?' => 0xe6,
+ '!' => 0xe7,
+ '.' => 0xe8,
+ '▷' => 0xec,
+ '▶' => 0xed,
+ '▼' => 0xee,
+ '♂' => 0xef,
+ '×' => 0xf1,
+ // TODO: Pattern currently unreachable. Figure something out.
+ // In the Japanese games (as can be seen below), 0xF2 is distinguishable from 0xE8, with
+ // the former meant as a decimal point while the latter is punctuation. Presumably this
+ // intention was largely inherited when the English games were made, as most of the game's
+ // script uses 0xE8 exclusively; however, 0xF2 appears in the character table for user
+ // input, meaning it may appear in user-input names (and, conversely, 0xE8 never should).
+ // Source: https://bulbapedia.bulbagarden.net/wiki/Character_encoding_in_Generation_I
+ // '.' => 0xf2,
+ '/' => 0xf3,
+ ',' => 0xf4,
+ '♀' => 0xf5,
+ num @ '0'..='9' => 0xf6 - ('0' as u8) + (*num as u8),
+ ' ' => 0x7f,
+ '@' => 0x50,
+ _ => return None,
+ })
+}
+
+const fn hextostr(hex: u8) -> Option {
+ Some(match hex {
+ cap @ 0x80..=0x99 => (('A' as u8) + (cap - 0x80)) as char,
+ 0x9a => '(',
+ 0x9b => ')',
+ 0x9c => ':',
+ 0x9d => ';',
+ 0x9e => '[',
+ 0x9f => ']',
+ low @ 0xa0..=0xb9 => (('a' as u8) + (low - 0xa0)) as char,
+ 0xe0 => '\'',
+ 0xe3 => '-',
+ 0xe6 => '?',
+ 0xe7 => '!',
+ 0xe8 => '.',
+ 0xec => '▷',
+ 0xed => '▶',
+ 0xee => '▼',
+ 0xef => '♂',
+ 0xf1 => '×',
+ 0xf2 => '.',
+ 0xf3 => '/',
+ 0xf4 => ',',
+ 0xf5 => '♀',
+ num @ 0xf6..=0xff => (('0' as u8) + (num - 0xf6)) as char,
+ 0x7f => ' ',
+ 0x50 => '@',
+ _ => return None,
+ })
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub enum Error {
+ InvalidCharacter,
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ match self {
+ Error::InvalidCharacter => write!(f, "Invalid character"),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct PKString(String);
+
+impl Deref for PKString {
+ type Target = String;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl fmt::Display for PKString {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_fmt(format_args!("{}", &self.0))
+ }
+}
+
+impl TryFrom for PKString {
+ type Error = Error;
+
+ fn try_from(ord: u8) -> Result {
+ let mut buf = String::with_capacity(1);
+
+ match hextostr(ord) {
+ Some(chr) => buf.push(chr.clone()),
+ None => return Err(Error::InvalidCharacter),
+ }
+
+ Ok(PKString(buf))
+ }
+}
+
+impl TryFrom<&[u8]> for PKString {
+ type Error = Error;
+
+ fn try_from(data: &[u8]) -> Result {
+ let mut buf = String::with_capacity(data.len());
+
+ for ord in data {
+ match hextostr(*ord) {
+ Some(chr) => buf.push(chr.clone()),
+ None => return Err(Error::InvalidCharacter),
+ }
+ }
+
+ Ok(PKString(buf))
+ }
+}
+
+impl TryFrom> for PKString {
+ type Error = Error;
+
+ fn try_from(data: Vec) -> Result {
+ PKString::try_from(data.as_slice())
+ }
+}
+
+impl TryFrom<&str> for PKString {
+ type Error = Error;
+
+ fn try_from(data: &str) -> Result {
+ PKString::try_from(String::from(data))
+ }
+}
+
+impl TryFrom for PKString {
+ type Error = Error;
+
+ fn try_from(data: String) -> Result {
+ for chr in data.chars() {
+ if strtohex(&chr).is_none() {
+ return Err(Error::InvalidCharacter);
+ }
+ }
+
+ Ok(PKString(data))
+ }
+}
+
+impl From for Vec {
+ fn from(pkstr: PKString) -> Vec {
+ let mut vec = Vec::with_capacity(pkstr.len());
+
+ for chr in pkstr.0.chars() {
+ if let Some(ord) = strtohex(&chr) {
+ vec.push(ord);
+ } else {
+ // TODO: Change this.
+ panic!();
+ }
+ }
+
+ vec
+ }
+}
+
+impl From for String {
+ fn from(pkstr: PKString) -> String {
+ pkstr.0
+ }
+}
+
+// impl PKString {
+// fn as_slice(&self) -> &[u8] {
+// self.0.as_slice()
+// }
+// }
diff --git a/pkstrings/src/tests.rs b/pkstrings/src/tests.rs
new file mode 100644
index 0000000..4664808
--- /dev/null
+++ b/pkstrings/src/tests.rs
@@ -0,0 +1,120 @@
+// Copyright (C) 2021 "Maxime “pep” Buquet "
+//
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU Affero General Public License as published by the
+// Free Software Foundation, either version 3 of the License, or (at your
+// option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
+// for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+use crate::PKString;
+use std::convert::TryFrom;
+
+const GARY_SLICE_U8: &[u8] = &[0x86, 0x80, 0x91, 0x98];
+const GARY_STR: &str = "GARY";
+
+#[test]
+fn test_try_from_u8() {
+ match PKString::try_from(0x80) { // 'A'
+ Ok(pkstr) => assert_eq!(pkstr, PKString::try_from("A").unwrap()),
+ Err(_) => panic!(),
+ }
+
+ match PKString::try_from(0x79) { // Invalid
+ Ok(_) => panic!(),
+ Err(_) => (),
+ }
+}
+
+#[test]
+fn test_try_from_slice_u8() {
+ match PKString::try_from(GARY_SLICE_U8) {
+ Ok(pkstr) => assert_eq!(pkstr, PKString::try_from(GARY_STR).unwrap()),
+ Err(_) => panic!(),
+ }
+
+ let invalid: &[u8] = &[
+ 0x86,
+ 0x80,
+ 0x79, // Invalid
+ 0x98,
+ ];
+ match PKString::try_from(invalid) {
+ Ok(_) => panic!(),
+ Err(_) => (),
+ }
+}
+
+#[test]
+fn test_try_from_vec_u8() {
+ let gary: Vec = GARY_SLICE_U8.to_vec();
+ match PKString::try_from(gary) {
+ Ok(pkstr) => assert_eq!(pkstr, PKString::try_from(GARY_STR).unwrap()),
+ Err(_) => panic!(),
+ }
+
+ let invalid: Vec = vec![
+ 0x86,
+ 0x80,
+ 0x79, // Invalid
+ 0x98,
+ ];
+ match PKString::try_from(invalid) {
+ Ok(_) => panic!(),
+ Err(_) => (),
+ }
+}
+
+#[test]
+fn test_from_ord() {
+ assert_eq!(PKString::try_from(GARY_SLICE_U8), Ok(PKString::try_from(GARY_STR).unwrap()));
+
+ let party_nicks: &[u8] = &[
+ 0x8a, 0x80, 0x83, 0x80, 0x81, 0x91, 0x80, 0x50, 0x50, 0x50, 0x50, 0x8d, 0x88, 0x83,
+ 0x8e, 0x8a, 0x88, 0x8d, 0x86, 0x50, 0x50, 0x50, 0x81, 0x8b, 0x80, 0x92, 0x93, 0x8e,
+ 0x88, 0x92, 0x84, 0x50, 0x50, 0x8e, 0x83, 0x83, 0x88, 0x92, 0x87, 0x50, 0x50, 0x50,
+ 0x50, 0x50, 0x8f, 0x88, 0x83, 0x86, 0x84, 0x98, 0x50, 0x50, 0x50, 0x50, 0x50, 0x82,
+ 0x87, 0x80, 0x91, 0x8c, 0x80, 0x8d, 0x83, 0x84, 0x91, 0x50,
+ ];
+ let result = "KADABRA@@@@NIDOKING@@@BLASTOISE@@ODDISH@@@@@PIDGEY@@@@@CHARMANDER@";
+ assert_eq!(PKString::try_from(party_nicks), Ok(PKString::try_from(result).unwrap()));
+}
+
+#[test]
+fn test_from_chr() {
+ let result: Vec = PKString::try_from(GARY_STR).unwrap().into();
+ assert_eq!(result, GARY_SLICE_U8);
+
+ let nidoranf: Vec = vec![0x8d, 0x88, 0x83, 0x8e, 0x91, 0x80, 0x8d, 0xef];
+ let result: Vec = PKString::try_from("NIDORAN♂").unwrap().into();
+ assert_eq!(result, nidoranf);
+}
+
+#[test]
+fn test_into_vec_u8() {
+ let pkstr: PKString = PKString::try_from(GARY_SLICE_U8).unwrap();
+ let res: Vec = pkstr.into();
+ assert_eq!(res, GARY_SLICE_U8);
+}
+
+#[test]
+fn test_from_string() {
+ match PKString::try_from(String::from(GARY_STR)) {
+ Ok(pkstr) => assert_eq!(pkstr, PKString::try_from(GARY_SLICE_U8).unwrap()),
+ Err(_) => panic!(),
+ }
+}
+
+// #[test]
+// fn test_as_slice_u8() {
+// let gary: Vec = vec![0x86, 0x80, 0x91, 0x98];
+// let pkstr: PKString = PKString::try_from(gary.clone()).unwrap();
+// let res: &[u8] = pkstr.as_slice();
+// assert_eq!(res, gary.as_slice());
+// }