use once_cell::sync::Lazy;
const LIMITED_ALPHABET: &str = "ABCDEFGHJKLMNPQRSTUVWXYZ"; const FULL_ALPHABET: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static NUMERIC_MAPPINGS: Lazy<Vec<NumericMapping>> = Lazy::new(|| {
vec![
NumericMapping::new(0x140000, 0, 100000, String::from("RA-00000")),
NumericMapping::new(0x0B03E8, 1000, 1000, String::from("CU-T0000")),
]
});
static STRIDE_MAPPINGS: Lazy<Vec<StrideMapping>> = Lazy::new(|| {
vec![
StrideMapping::new(
0x380000,
1024,
32,
String::from("F-B"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x388000,
1024,
32,
String::from("F-I"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x390000,
1024,
32,
String::from("F-G"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x398000,
1024,
32,
String::from("F-H"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x3a0000,
1024,
32,
String::from("F-O"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x3C4421,
1024,
32,
String::from("D-A"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("OZZ"),
),
StrideMapping::new(
0x3C0001,
26 * 26,
26,
String::from("D-A"),
String::from(FULL_ALPHABET),
String::from("PAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x3C8421,
1024,
32,
String::from("D-B"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("OZZ"),
),
StrideMapping::new(
0x3C2001,
26 * 26,
26,
String::from("D-B"),
String::from(FULL_ALPHABET),
String::from("PAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x3CC000,
26 * 26,
26,
String::from("D-C"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x3D04A8,
26 * 26,
26,
String::from("D-E"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x3D4950,
26 * 26,
26,
String::from("D-F"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x3D8DF8,
26 * 26,
26,
String::from("D-G"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x3DD2A0,
26 * 26,
26,
String::from("D-H"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x3E1748,
26 * 26,
26,
String::from("D-I"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x448421,
1024,
32,
String::from("OO-"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x458421,
1024,
32,
String::from("OY-"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x460000,
26 * 26,
26,
String::from("OH-"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x468421,
1024,
32,
String::from("SX-"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x490421,
1024,
32,
String::from("CS-"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x4A0421,
1024,
32,
String::from("YR-"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x4B8421,
1024,
32,
String::from("TC-"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x740421,
1024,
32,
String::from("JY-"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x760421,
1024,
32,
String::from("AP-"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x768421,
1024,
32,
String::from("9V-"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x778421,
1024,
32,
String::from("YK-"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0x7C0000,
36 * 36,
36,
String::from("VH-"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0xC00001,
26 * 26,
26,
String::from("C-F"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0xC044A9,
26 * 26,
26,
String::from("C-G"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
StrideMapping::new(
0xE01041,
4096,
64,
String::from("LV-"),
String::from(FULL_ALPHABET),
String::from("AAA"),
String::from("ZZZ"),
),
]
});
struct StrideMapping {
start: u32,
s1: u32,
s2: u32,
prefix: String,
alphabet: String,
offset: u32,
end: u32,
}
impl StrideMapping {
fn new(
start: u32,
s1: u32,
s2: u32,
prefix: String,
alphabet: String,
first: String,
last: String,
) -> Self {
let offset = {
let c1 = alphabet
.chars()
.position(|c| c == first.chars().nth(0).unwrap())
.unwrap_or(0) as u32;
let c2 = alphabet
.chars()
.position(|c| c == first.chars().nth(1).unwrap())
.unwrap_or(0) as u32;
let c3 = alphabet
.chars()
.position(|c| c == first.chars().nth(2).unwrap())
.unwrap_or(0) as u32;
c1 * s1 + c2 * s2 + c3
};
let end = {
let c1 = alphabet
.chars()
.position(|c| c == last.chars().nth(0).unwrap())
.unwrap_or(alphabet.len() - 1) as u32;
let c2 = alphabet
.chars()
.position(|c| c == last.chars().nth(1).unwrap())
.unwrap_or(alphabet.len() - 1) as u32;
let c3 = alphabet
.chars()
.position(|c| c == last.chars().nth(2).unwrap())
.unwrap_or(alphabet.len() - 1) as u32;
start - offset + c1 * s1 + c2 * s2 + c3
};
StrideMapping {
start,
s1,
s2,
prefix,
alphabet,
offset,
end,
}
}
}
pub fn stride_reg(hexid: u32) -> Option<String> {
for mapping in STRIDE_MAPPINGS.iter() {
if hexid >= mapping.start && hexid <= mapping.end {
let mut offset = hexid - mapping.start + mapping.offset;
let i1 = (offset / mapping.s1) as usize;
offset %= mapping.s1;
let i2 = (offset / mapping.s2) as usize;
offset %= mapping.s2;
let i3 = offset as usize;
if i1 < mapping.alphabet.len()
&& i2 < mapping.alphabet.len()
&& i3 < mapping.alphabet.len()
{
return Some(format!(
"{}{}{}{}",
mapping.prefix,
mapping.alphabet.chars().nth(i1).unwrap(),
mapping.alphabet.chars().nth(i2).unwrap(),
mapping.alphabet.chars().nth(i3).unwrap()
));
}
}
}
None
}
struct NumericMapping {
start: u32,
first: u32,
template: String,
end: u32,
}
impl NumericMapping {
fn new(start: u32, first: u32, count: u32, template: String) -> Self {
let end = start + count - 1;
NumericMapping {
start,
first,
template,
end,
}
}
}
pub fn numeric_reg(hexid: u32) -> Option<String> {
for mapping in NUMERIC_MAPPINGS.iter() {
if hexid >= mapping.start && hexid <= mapping.end {
let reg = (hexid - mapping.start + mapping.first).to_string();
return Some(format!(
"{}{}",
&mapping.template[..(mapping.template.len() - reg.len())],
reg
));
}
}
None
}
pub fn hl_reg(hexid: u32) -> Option<String> {
if (0x71BA00..=0x71BF99).contains(&hexid) {
return Some(format!("HL{:X}", hexid - 0x71BA00 + 0x7200));
}
if (0x71C000..=0x71C099).contains(&hexid) {
return Some(format!("HL{:X}", hexid - 0x71C000 + 0x8000));
}
if (0x71C200..=0x71C299).contains(&hexid) {
return Some(format!("HL{:X}", hexid - 0x71C200 + 0x8200));
}
None
}
pub fn ja_reg(hexid: u32) -> Option<String> {
let offset = hexid.wrapping_sub(0x840000);
if offset >= 229840 {
return None;
}
let mut reg = String::from("JA");
let digit1 = offset / 22984;
if digit1 > 9 {
return None;
}
reg.push_str(&digit1.to_string());
let offset = offset % 22984;
let digit2 = offset / 916;
if digit2 > 9 {
return None;
}
reg.push_str(&digit2.to_string());
let mut offset = offset % 916;
if offset < 340 {
let digit3 = offset / 34;
reg.push_str(&digit3.to_string());
offset %= 34;
if offset < 10 {
return Some(reg + &offset.to_string());
}
offset -= 10;
return Some(
reg + &LIMITED_ALPHABET
.chars()
.nth(offset as usize)
.unwrap()
.to_string(),
);
}
offset -= 340;
let letter3 = offset / 24;
Some(
reg + &LIMITED_ALPHABET
.chars()
.nth(letter3 as usize)
.unwrap()
.to_string()
+ &LIMITED_ALPHABET
.chars()
.nth((offset % 24) as usize)
.unwrap()
.to_string(),
)
}
fn n_letters(mut rem: u32) -> String {
if rem == 0 {
return String::new();
}
rem -= 1;
format!(
"{}{}",
LIMITED_ALPHABET.chars().nth((rem / 25) as usize).unwrap(),
n_letter(rem % 25)
)
}
fn n_letter(mut rem: u32) -> String {
if rem == 0 {
return String::new();
}
rem -= 1;
LIMITED_ALPHABET
.chars()
.nth(rem as usize)
.unwrap()
.to_string()
}
pub fn n_reg(hexid: u32) -> Option<String> {
let offset = hexid.wrapping_sub(0xA00001);
if offset >= 915399 {
return None;
}
let digit1 = (offset / 101711) + 1;
let mut reg = format!("N{digit1}");
let mut offset = offset % 101711;
if offset <= 600 {
return Some(reg + &n_letters(offset));
}
offset -= 601;
let digit2 = offset / 10111;
reg.push_str(&digit2.to_string());
let mut offset = offset % 10111;
if offset <= 600 {
return Some(reg + &n_letters(offset));
}
offset -= 601;
let digit3 = offset / 951;
reg.push_str(&digit3.to_string());
let mut offset = offset % 951;
if offset <= 600 {
return Some(reg + &n_letters(offset));
}
offset -= 601;
let digit4 = offset / 35;
reg.push_str(&digit4.to_string());
let mut offset = offset % 35;
if offset <= 24 {
return Some(reg + &n_letter(offset));
}
offset -= 25;
Some(reg + &offset.to_string())
}
pub fn tail(hexid: u32) -> Option<String> {
if let Some(n_reg) = n_reg(hexid) {
return Some(n_reg);
}
if let Some(ja_reg) = ja_reg(hexid) {
return Some(ja_reg);
}
if let Some(hl_reg) = hl_reg(hexid) {
return Some(hl_reg);
}
if let Some(numeric_reg) = numeric_reg(hexid) {
return Some(numeric_reg);
}
if let Some(stride_reg) = stride_reg(hexid) {
return Some(stride_reg);
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_korea() {
assert_eq!(hl_reg(0x71bd54), Some("HL7554".to_string()));
assert_eq!(hl_reg(0x71c025), Some("HL8025".to_string()));
}
#[test]
fn test_japan() {
assert_eq!(ja_reg(0x869232), Some("JA788A".to_string()));
assert_eq!(ja_reg(0x86dcc4), Some("JA841J".to_string()));
assert_eq!(ja_reg(0x847c18), Some("JA19JJ".to_string()));
assert_eq!(ja_reg(0x3949f9), None);
}
#[test]
fn test_us() {
assert_eq!(n_reg(0xa43e7f), Some("N37263".to_string()));
assert_eq!(n_reg(0xa44533), Some("N3741S".to_string()));
assert_eq!(n_reg(0xad7701), Some("N967JT".to_string()));
}
#[test]
fn test_ra() {
assert_eq!(numeric_reg(0x140b3a), Some("RA-02874".to_string()));
}
#[test]
fn test_f() {
assert_eq!(stride_reg(0x39b415), Some("F-HNAV".to_string()));
assert_eq!(stride_reg(0x39cf09), Some("F-HTYJ".to_string()));
assert_eq!(stride_reg(0x3949f9), Some("F-GSPZ".to_string()));
}
}