use crate::constants::save_data::SaveData;
use crate::constants::status::Flags;
use crate::constants::text::KANA_TABLE;
use crate::constants::text::build_password_map;
use crate::utility::binary_utils::{validate_6bit_array, validate_120bit};
pub fn decode_password_string(s: &str) -> Result<Vec<u8>, String> {
if s.chars().count() != 20 {
return Err("ふっかつのじゅもんは20文字である必要があります".to_string());
}
let password_map = build_password_map();
let mut bits = Vec::with_capacity(20);
for c in s.chars() {
match password_map.get(&c) {
Some(&v) => bits.push(v),
None => return Err(format!("未対応の文字が含まれています: {}", c)),
}
}
Ok(bits)
}
pub fn undo_password_addition(values: &[u8]) -> Result<Vec<u8>, String> {
validate_6bit_array(values)?;
let mut result = vec![0u8; 20];
for i in 0..20 {
result[i] = if i == 0 {
(values[0] + 64 - 4) % 64
} else {
let current = values[i] as i16;
let prev = values[i - 1] as i16;
((current - 4 - prev + 64 * 2) % 64) as u8
};
}
Ok(result)
}
pub fn reorder_blocks_back(bits: &[u8]) -> Result<Vec<u8>, String> {
validate_6bit_array(bits)?;
let mut result = Vec::with_capacity(20);
for block_i in 0..5 {
let block_6bit = &bits[block_i * 4..(block_i + 1) * 4];
let reversed_6bit = block_6bit.iter().rev().cloned().collect::<Vec<_>>();
let mut combined: u32 = 0;
for (i, &b) in reversed_6bit.iter().enumerate() {
combined |= (b as u32) << (18 - i * 6);
}
let byte1 = ((combined >> 16) & 0xFF) as u8;
let byte2 = ((combined >> 8) & 0xFF) as u8;
let byte3 = (combined & 0xFF) as u8;
result.push(byte3);
result.push(byte2);
result.push(byte1);
}
Ok(result)
}
pub fn extract_name_from_bits(bits: &[u8]) -> Result<String, String> {
validate_120bit(bits)?;
let mut indices = [0u8; 4];
indices[0] = (bits[5] >> 2) & 0b0011_1111;
indices[1] = (bits[13] >> 1) & 0b0011_1111;
indices[2] = bits[2] & 0b0011_1111;
indices[3] = bits[7] & 0b0011_1111;
let name = indices
.iter()
.map(|&i| KANA_TABLE.get(i as usize).copied().unwrap_or(' '))
.collect();
Ok(name)
}
pub fn extract_experience_from_bits(bits: &[u8]) -> Result<u16, String> {
validate_120bit(bits)?;
let lower = bits[1] as u16;
let upper = bits[12] as u16;
Ok((upper << 8) | lower)
}
pub fn extract_gold_from_bits(bits: &[u8]) -> Result<u16, String> {
validate_120bit(bits)?;
let lower = bits[4] as u16;
let upper = bits[9] as u16;
Ok((upper << 8) | lower)
}
pub fn extract_weapon_from_bits(bits: &[u8]) -> Result<u8, String> {
validate_120bit(bits)?;
Ok((bits[8] >> 5) & 0b0000_0111)
}
pub fn extract_armor_from_bits(bits: &[u8]) -> Result<u8, String> {
validate_120bit(bits)?;
Ok((bits[8] >> 2) & 0b0000_0111)
}
pub fn extract_shield_from_bits(bits: &[u8]) -> Result<u8, String> {
validate_120bit(bits)?;
Ok(bits[8] & 0b0000_0011)
}
pub fn extract_items_from_bits(bits: &[u8]) -> Result<[u8; 8], String> {
validate_120bit(bits)?;
Ok([
bits[14] & 0x0F,
(bits[14] >> 4) & 0x0F,
bits[3] & 0x0F,
(bits[3] >> 4) & 0x0F,
bits[11] & 0x0F,
(bits[11] >> 4) & 0x0F,
bits[6] & 0x0F,
(bits[6] >> 4) & 0x0F,
])
}
pub fn extract_flags_from_bits(bits: &[u8]) -> Result<Flags, String> {
validate_120bit(bits)?;
Ok(Flags {
has_dragon_scale: ((bits[13] >> 7) & 1) == 1,
has_warrior_ring: (bits[13] & 1) == 1,
has_cursed_necklace: ((bits[2] >> 6) & 1) == 1,
defeated_dragon: ((bits[7] >> 6) & 1) == 1,
defeated_golem: ((bits[5] >> 1) & 1) == 1,
})
}
pub fn extract_pattern_from_bits(bits: &[u8]) -> Result<u8, String> {
validate_120bit(bits)?;
let b0 = ((bits[7] >> 7) & 1) << 0;
let b1 = (bits[5] & 1) << 1;
let b2 = ((bits[2] >> 7) & 1) << 2;
Ok(b0 | b1 | b2)
}
pub fn extract_herbs_and_keys_from_bits(bits: &[u8]) -> Result<(u8, u8), String> {
validate_120bit(bits)?;
let herbs = bits[10] & 0x0F;
let keys = (bits[10] >> 4) & 0x0F;
Ok((herbs, keys))
}
pub fn parse_bitstring_to_save_data(bits: &[u8]) -> Result<SaveData, String> {
validate_120bit(bits)?;
let flags = extract_flags_from_bits(&bits)?;
let (herbs, keys) = extract_herbs_and_keys_from_bits(&bits)?;
Ok(SaveData {
name: extract_name_from_bits(&bits)?,
experience: extract_experience_from_bits(&bits)?,
gold: extract_gold_from_bits(&bits)?,
weapon: extract_weapon_from_bits(&bits)?,
armor: extract_armor_from_bits(&bits)?,
shield: extract_shield_from_bits(&bits)?,
items: extract_items_from_bits(&bits)?,
herbs,
keys,
flags,
pattern: extract_pattern_from_bits(&bits)?,
})
}
pub fn decode_from_password_string(password: &str) -> Result<SaveData, String> {
let encoded = decode_password_string(password)?;
let raw = undo_password_addition(&encoded)?;
let bit_block = reorder_blocks_back(&raw)?;
parse_bitstring_to_save_data(&bit_block)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_password_string() {
let kana = "あいうえおかきくけこさしすせそたちつてと";
let result = decode_password_string(kana).unwrap();
assert_eq!(result.len(), 20);
assert_eq!(result[0], 0); assert_eq!(result[1], 1); assert_eq!(result[4], 4); assert_eq!(result[15], 15); }
#[test]
fn test_password_too_short() {
let short = "あいうえお"; let err = decode_password_string(short).unwrap_err();
assert!(err.contains("20文字"));
}
#[test]
fn test_invalid_character() {
let bad = "あいうえおかきくけこさしすせそたちつて💥"; let err = decode_password_string(bad).unwrap_err();
assert!(err.contains("未対応の文字"));
}
#[test]
fn test_undo_password_addition_normal_case() {
let raw = vec![
10, 20, 30, 40, 50, 12, 18, 24, 30, 36, 15, 23, 31, 39, 47, 5, 10, 20, 30, 40,
];
let mut encoded = vec![0u8; 20];
for i in 0..20 {
encoded[i] = (raw[i] + 4 + if i > 0 { encoded[i - 1] } else { 0 }) % 64;
}
let decoded = undo_password_addition(&encoded).unwrap();
assert_eq!(decoded, raw);
}
#[test]
fn test_undo_password_addition_invalid_length() {
let too_short = vec![1, 2, 3];
let result = undo_password_addition(&too_short);
assert!(result.is_err());
assert!(result.unwrap_err().contains("20個のビット列"));
}
#[test]
fn test_reorder_blocks_back_roundtrip() {
let original_bytes: Vec<u8> = (0..15).collect();
let mut encoded_bits = Vec::with_capacity(20);
for chunk in original_bytes.chunks(3) {
let byte1 = chunk[0];
let byte2 = chunk[1];
let byte3 = chunk[2];
let block = ((byte3 as u32) << 16) | ((byte2 as u32) << 8) | (byte1 as u32);
let b1 = ((block >> 18) & 0x3F) as u8;
let b2 = ((block >> 12) & 0x3F) as u8;
let b3 = ((block >> 6) & 0x3F) as u8;
let b4 = (block & 0x3F) as u8;
encoded_bits.push(b4);
encoded_bits.push(b3);
encoded_bits.push(b2);
encoded_bits.push(b1);
}
let decoded = reorder_blocks_back(&encoded_bits).unwrap();
assert_eq!(decoded, original_bytes);
}
#[test]
fn test_reorder_blocks_back_invalid_length() {
let invalid_bits = vec![1, 2, 3]; let result = reorder_blocks_back(&invalid_bits);
assert!(result.is_err());
assert!(result.unwrap_err().contains("20"));
}
}