use crate::bits::{BitReader, BitWriter};
use crate::error::Result;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum QualityData {
Inferior {
file_index: u8,
},
Superior {
file_index: u8,
},
Magic {
prefix: u16,
suffix: u16,
},
SetUnique {
file_index: u16,
},
RareCraft(RareCraftData),
Tempered {
name_prefix: u8,
name_suffix: u8,
},
NormalCharm {
has_prefix: bool,
prefix: u16,
suffix: u16,
},
NormalBodyPart {
file_index: u16,
},
NormalScrollBook {
spell_type: u8,
},
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RareCraftData {
pub name_prefix: u8,
pub name_suffix: u8,
pub prefixes: [u16; 3],
pub suffixes: [u16; 3],
}
fn read_prefix(r: &mut BitReader<'_>, offset: i32) -> Result<u16> {
let raw = r.read_bits(11)?;
Ok(if raw != 0 { (raw as i32).wrapping_add(offset) as u16 } else { 0 })
}
fn write_prefix(w: &mut BitWriter<'_>, prefix: u16, offset: i32) -> Result<()> {
let prefix = prefix as i32;
let raw = if prefix > offset { prefix - offset } else { prefix };
w.write_bits(raw.clamp(0, 2047) as u32, 11)
}
impl QualityData {
pub fn file_index(&self) -> Option<u16> {
match *self {
QualityData::Inferior { file_index } | QualityData::Superior { file_index } => Some(file_index as u16),
QualityData::SetUnique { file_index } | QualityData::NormalBodyPart { file_index } => Some(file_index),
_ => None,
}
}
pub(crate) fn read_inferior(r: &mut BitReader<'_>) -> Result<Self> {
Ok(QualityData::Inferior { file_index: r.read_bits(3)? as u8 })
}
pub(crate) fn read_superior(r: &mut BitReader<'_>) -> Result<Self> {
Ok(QualityData::Superior { file_index: r.read_bits(3)? as u8 })
}
pub(crate) fn read_magic(r: &mut BitReader<'_>, offset: i32) -> Result<Self> {
Ok(QualityData::Magic { prefix: read_prefix(r, offset)?, suffix: r.read_bits(11)? as u16 })
}
pub(crate) fn read_set_unique(r: &mut BitReader<'_>) -> Result<Self> {
Ok(QualityData::SetUnique { file_index: r.read_bits(12)? as u16 })
}
pub(crate) fn read_rare_craft(r: &mut BitReader<'_>, offset: i32) -> Result<Self> {
let mut data = RareCraftData { name_prefix: r.read_u8()?, name_suffix: r.read_u8()?, ..Default::default() };
for i in 0..3 {
if r.read_bool()? {
data.prefixes[i] = read_prefix(r, offset)?;
}
if r.read_bool()? {
data.suffixes[i] = r.read_bits(11)? as u16;
}
}
Ok(QualityData::RareCraft(data))
}
pub(crate) fn read_tempered(r: &mut BitReader<'_>) -> Result<Self> {
Ok(QualityData::Tempered { name_prefix: r.read_u8()?, name_suffix: r.read_u8()? })
}
pub(crate) fn read_charm(r: &mut BitReader<'_>, offset: i32) -> Result<Self> {
let has_prefix = r.read_bool()?;
Ok(if has_prefix {
QualityData::NormalCharm { has_prefix, prefix: read_prefix(r, offset)?, suffix: 0 }
} else {
QualityData::NormalCharm { has_prefix, prefix: 0, suffix: r.read_bits(11)? as u16 }
})
}
pub(crate) fn read_body_part(r: &mut BitReader<'_>) -> Result<Self> {
Ok(QualityData::NormalBodyPart { file_index: r.read_bits(10)? as u16 })
}
pub(crate) fn read_scroll_book(r: &mut BitReader<'_>) -> Result<Self> {
Ok(QualityData::NormalScrollBook { spell_type: r.read_bits(5)? as u8 })
}
pub fn write(&self, w: &mut BitWriter<'_>, magic_prefix_offset: i32) -> Result<()> {
match self {
QualityData::Inferior { file_index } | QualityData::Superior { file_index } => {
w.write_bits((*file_index).min(7) as u32, 3)
}
QualityData::Magic { prefix, suffix } => {
write_prefix(w, *prefix, magic_prefix_offset)?;
w.write_bits((*suffix).min(2047) as u32, 11)
}
QualityData::SetUnique { file_index } => w.write_bits((*file_index).min(4095) as u32, 12),
QualityData::RareCraft(data) => {
w.write_u8(data.name_prefix)?;
w.write_u8(data.name_suffix)?;
for i in 0..3 {
let prefix = data.prefixes[i];
w.write_bool(prefix != 0)?;
if prefix != 0 {
write_prefix(w, prefix, magic_prefix_offset)?;
}
let suffix = data.suffixes[i];
w.write_bool(suffix != 0)?;
if suffix != 0 {
w.write_bits(suffix.min(2047) as u32, 11)?;
}
}
Ok(())
}
QualityData::Tempered { name_prefix, name_suffix } => {
w.write_u8(*name_prefix)?;
w.write_u8(*name_suffix)
}
QualityData::NormalCharm { has_prefix, prefix, suffix } => {
w.write_bool(*has_prefix)?;
if *has_prefix {
write_prefix(w, *prefix, magic_prefix_offset)
} else {
w.write_bits((*suffix).min(2047) as u32, 11)
}
}
QualityData::NormalBodyPart { file_index } => w.write_bits((*file_index).min(1023) as u32, 10),
QualityData::NormalScrollBook { spell_type } => w.write_bits((*spell_type).min(31) as u32, 5),
}
}
}