use crate::code_page::CodePage;
use crate::field::*;
use crate::script_data::*;
use crate::serde_helpers::*;
use crate::strings::*;
use base64::Engine;
use base64::engine::general_purpose::STANDARD as base64_STANDARD;
use either::{Either, Left, Right};
use flate2::Compression;
use flate2::write::{ZlibDecoder, ZlibEncoder};
use nameof::name_of;
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::ser::Error as ser_Error;
use serde::ser::{SerializeMap, SerializeSeq};
use serde::de::{self, DeserializeSeed, Unexpected, VariantAccess};
use serde::de::Error as de_Error;
use serde_serialize_seed::{SerializeSeed, ValueWithSeed, VecSerde};
use std::fmt::{self, Debug, Formatter};
use std::io::Write;
bitflags_ext! {
pub struct RecordFlags: u64 {
PERSIST = 0x40000000000,
BLOCKED = 0x200000000000,
DELETED = 0x2000000000,
}
}
enum_serde!(RecordFlags, "record flags", u64, bits(), try from_bits, Unsigned, u64);
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Record {
pub tag: Tag,
pub flags: RecordFlags,
pub fields: Vec<(Tag, Field)>,
}
impl Record {
pub fn fit(&mut self) {
let mut prev_tag = META;
for &mut (field_tag, ref mut field) in self.fields.iter_mut() {
field.fit(self.tag, prev_tag, field_tag);
prev_tag = field_tag;
}
}
}
struct FieldBodySerializer<'a> {
code_page: Option<CodePage>,
record_tag: Tag,
prev_tag: Tag,
field_tag: Tag,
field: &'a Field
}
impl<'a> Serialize for FieldBodySerializer<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
match FieldType::from_tags(self.record_tag, self.prev_tag, self.field_tag) {
FieldType::String(len) => if let Field::String(s) = self.field {
ValueWithSeed(s.as_str(), StringSerde {
code_page: self.code_page,
len: len.map(|x| x.try_into().unwrap())
}).serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have string type", self.record_tag, self.field_tag)))
},
FieldType::StringZ => if let Field::StringZ(s) = self.field {
ValueWithSeed(s, StringZSerde { code_page: self.code_page }).serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have zero-terminated string type", self.record_tag, self.field_tag)))
},
FieldType::Multiline(newline) => if let Field::StringList(s) = self.field {
ValueWithSeed(&s[..], StringListSerde {
code_page: self.code_page, separator: newline.as_str(), len: None
}).serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have string list type", self.record_tag, self.field_tag)))
},
FieldType::StringZList => if let Field::StringZList(s) = self.field {
ValueWithSeed(s, StringZListSerde { code_page: self.code_page }).serialize(serializer)
} else {
Err(S::Error::custom(
format!("{} {} field should have zero-terminated string list type", self.record_tag, self.field_tag)
))
},
FieldType::U8List => if let Field::U8List(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have byte list type", self.record_tag, self.field_tag)))
},
FieldType::ScriptData => if let Field::ScriptData(data) = self.field {
ValueWithSeed(data, ScriptDataSerde { code_page: self.code_page }).serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have script data type", self.record_tag, self.field_tag)))
},
FieldType::U8ListZip => if let Field::U8List(v) = self.field {
if serializer.is_human_readable() {
base64_STANDARD.encode(v).serialize(serializer)
} else {
let uncompressed = (|| {
let mut decoder = ZlibDecoder::new(Vec::new());
decoder.write_all(&v[..])?;
decoder.finish()
})().map_err(|_| S::Error::custom("invalid compressed data"))?;
uncompressed.serialize(serializer)
}
} else {
Err(S::Error::custom(format!("{} {} field should have byte list type", self.record_tag, self.field_tag)))
},
FieldType::CurrentTime => if let Field::CurrentTime(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have current time type", self.record_tag, self.field_tag)))
},
FieldType::Time => if let Field::Time(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have time type", self.record_tag, self.field_tag)))
},
FieldType::Item => if let Field::Item(v) = self.field {
ValueWithSeed(v, ItemSerde { code_page: self.code_page }).serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have item type", self.record_tag, self.field_tag)))
},
FieldType::Skill => if let Field::Skill(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have skill type", self.record_tag, self.field_tag)))
},
FieldType::EffectArg => if let Field::EffectArg(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have effect arg type", self.record_tag, self.field_tag)))
},
FieldType::Ingredient => if let Field::Ingredient(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have ingredient type", self.record_tag, self.field_tag)))
},
FieldType::ScriptMetadata => if let Field::ScriptMetadata(v) = self.field {
ValueWithSeed(v, ScriptMetadataSerde { code_page: self.code_page }).serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have script metadata type", self.record_tag, self.field_tag)))
},
FieldType::ScriptVars => if let Field::ScriptVars(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have script vars type", self.record_tag, self.field_tag)))
},
FieldType::FileMetadata => if let Field::FileMetadata(v) = self.field {
ValueWithSeed(v, FileMetadataSerde { code_page: self.code_page }).serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have file metadata type", self.record_tag, self.field_tag)))
},
FieldType::NpcState => if let Field::NpcState(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have saved npc type", self.record_tag, self.field_tag)))
},
FieldType::Effect => if let Field::Effect(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have effect type", self.record_tag, self.field_tag)))
},
FieldType::Npc => if let Field::Npc(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have NPC type", self.record_tag, self.field_tag)))
},
FieldType::DialogMetadata => match self.field {
&Field::DialogType(v) => DialogTypeOption::Some(v).serialize(serializer),
&Field::I32(v) => DialogTypeOption::None(v).serialize(serializer),
_ => Err(S::Error::custom(format!("{} {} field should have dialog or 32-bit int type", self.record_tag, self.field_tag)))
},
FieldType::PosRotOrCell => match &self.field {
&Field::PosRot(v) => PosRotOrCell::PosRot(v.clone()).serialize(serializer),
&Field::Cell(v) => PosRotOrCell::Cell(v.clone()).serialize(serializer),
_ => Err(S::Error::custom(format!("{} {} field should have pos_rot or cell type", self.record_tag, self.field_tag)))
},
FieldType::Spell => if let Field::Spell(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have spell type", self.record_tag, self.field_tag)))
},
FieldType::Ai => if let Field::Ai(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have AI type", self.record_tag, self.field_tag)))
},
FieldType::AiWander => if let Field::AiWander(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have AI wander type", self.record_tag, self.field_tag)))
},
FieldType::AiTravel => if let Field::AiTravel(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have AI travel type", self.record_tag, self.field_tag)))
},
FieldType::AiTarget => if let Field::AiTarget(v) = self.field {
ValueWithSeed(v, AiTargetSerde { code_page: self.code_page }).serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have AI target type", self.record_tag, self.field_tag)))
},
FieldType::AiActivate => if let Field::AiActivate(v) = self.field {
ValueWithSeed(v, AiActivateSerde { code_page: self.code_page }).serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have AI activate type", self.record_tag, self.field_tag)))
},
FieldType::NpcFlags => if let Field::NpcFlags(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have NPC flags type", self.record_tag, self.field_tag)))
},
FieldType::CreatureFlags => if let Field::CreatureFlags(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have creature flags type", self.record_tag, self.field_tag)))
},
FieldType::Book => if let Field::Book(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have book type", self.record_tag, self.field_tag)))
},
FieldType::Info => if let Field::Info(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have info type", self.record_tag, self.field_tag)))
},
FieldType::Tool => if let Field::Tool(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have tool type", self.record_tag, self.field_tag)))
},
FieldType::RepairItem => if let Field::Tool(v) = self.field {
if serializer.is_human_readable() {
v.serialize(serializer)
} else {
RepairItem::from(v.clone()).serialize(serializer)
}
} else {
Err(S::Error::custom(format!("{} {} field should have tool type", self.record_tag, self.field_tag)))
},
FieldType::Creature => if let Field::Creature(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have creature type", self.record_tag, self.field_tag)))
},
FieldType::ContainerFlags => if let Field::ContainerFlags(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have container flags type", self.record_tag, self.field_tag)))
},
FieldType::Light => if let Field::Light(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have light type", self.record_tag, self.field_tag)))
},
FieldType::Interior => if let Field::Interior(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have interior type", self.record_tag, self.field_tag)))
},
FieldType::MiscItem => if let Field::MiscItem(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have misc item type", self.record_tag, self.field_tag)))
},
FieldType::Apparatus => if let Field::Apparatus(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have apparatus type", self.record_tag, self.field_tag)))
},
FieldType::Weapon => if let Field::Weapon(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have weapon type", self.record_tag, self.field_tag)))
},
FieldType::Armor => if let Field::Armor(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have armor type", self.record_tag, self.field_tag)))
},
FieldType::Pos => if let Field::Pos(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have pos type", self.record_tag, self.field_tag)))
},
FieldType::PosRot => if let Field::PosRot(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have pos_rot type", self.record_tag, self.field_tag)))
},
FieldType::BipedObject => if let Field::BipedObject(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have biped object type", self.record_tag, self.field_tag)))
},
FieldType::BodyPart => if let Field::BodyPart(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have body part type", self.record_tag, self.field_tag)))
},
FieldType::Clothing => if let Field::Clothing(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have clothing type", self.record_tag, self.field_tag)))
},
FieldType::Enchantment => if let Field::Enchantment(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have enchantment type", self.record_tag, self.field_tag)))
},
FieldType::Weather => if let Field::Weather(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have weather type", self.record_tag, self.field_tag)))
},
FieldType::SoundChance => if let Field::SoundChance(v) = self.field {
ValueWithSeed(v, SoundChanceSerde { code_page: self.code_page }).serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have sound chance type", self.record_tag, self.field_tag)))
},
FieldType::Color => if let Field::Color(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have color type", self.record_tag, self.field_tag)))
},
FieldType::Sound => if let Field::Sound(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have sound type", self.record_tag, self.field_tag)))
},
FieldType::Potion => if let Field::Potion(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have potion type", self.record_tag, self.field_tag)))
},
FieldType::Class => if let Field::Class(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have class type", self.record_tag, self.field_tag)))
},
FieldType::Attributes => if let Field::Attributes(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have attributes type", self.record_tag, self.field_tag)))
},
FieldType::Skills => if let Field::Skills(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have skills type", self.record_tag, self.field_tag)))
},
FieldType::Grid => if let Field::Grid(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have grid type", self.record_tag, self.field_tag)))
},
FieldType::PathGrid => if let Field::PathGrid(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have path grid type", self.record_tag, self.field_tag)))
},
FieldType::SoundGen => if let Field::SoundGen(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have sound gen type", self.record_tag, self.field_tag)))
},
FieldType::Tag => if let Field::Tag(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have tag type", self.record_tag, self.field_tag)))
},
FieldType::EffectIndex => if let Field::EffectIndex(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have effect index type", self.record_tag, self.field_tag)))
},
FieldType::EffectMetadata => if let Field::EffectMetadata(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have effect metadata type", self.record_tag, self.field_tag)))
},
FieldType::Race => if let Field::Race(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have race type", self.record_tag, self.field_tag)))
},
FieldType::Faction => if let Field::Faction(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have faction type", self.record_tag, self.field_tag)))
},
FieldType::SkillMetadata => if let Field::SkillMetadata(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have skill metadata type", self.record_tag, self.field_tag)))
},
FieldType::F32 => if let Field::F32(v) = self.field {
ValueWithSeed(v, F32AsIsSerde).serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have 32-bit float type", self.record_tag, self.field_tag)))
},
FieldType::MarkerU8(none) => if let Field::None = self.field {
ValueWithSeed(&(), NoneU8Serde { none }).serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have none type", self.record_tag, self.field_tag)))
},
FieldType::Bool8 => if let Field::Bool(v) = self.field {
ValueWithSeed(v, BoolU8Serde).serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have bool type", self.record_tag, self.field_tag)))
},
FieldType::Bool32 => if let Field::Bool(v) = self.field {
ValueWithSeed(v, BoolU32Serde).serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have bool type", self.record_tag, self.field_tag)))
},
FieldType::I32 => if let &Field::I32(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have 32-bit int type", self.record_tag, self.field_tag)))
},
FieldType::I16 => if let &Field::I16(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have 16-bit int type", self.record_tag, self.field_tag)))
},
FieldType::I64 => if let &Field::I64(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have 64-bit int type", self.record_tag, self.field_tag)))
},
FieldType::F32List => if let Field::F32List(v) = self.field {
ValueWithSeed(&v[..], VecSerde(F32AsIsSerde)).serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have 32-bit float list type", self.record_tag, self.field_tag)))
},
FieldType::I32List => if let Field::I32List(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have 32-bit int list type", self.record_tag, self.field_tag)))
},
FieldType::I16List => if let Field::I16List(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have 16-bit int list type", self.record_tag, self.field_tag)))
},
FieldType::U8 => if let &Field::U8(v) = self.field {
v.serialize(serializer)
} else {
Err(S::Error::custom(format!("{} {} field should have byte type", self.record_tag, self.field_tag)))
},
}
}
}
struct FieldSerializer<'a>(Option<CodePage>, Tag, Tag, Either<RecordFlags, (Tag, &'a Field)>);
impl<'a> Serialize for FieldSerializer<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
let is_human_readable = serializer.is_human_readable();
let mut serializer = serializer.serialize_map(Some(1))?;
match self.3 {
Left(flags) => serializer.serialize_entry(&META, &flags)?,
Right((field_tag, field)) => {
if is_human_readable && field_tag == META {
return Err(S::Error::custom("META tag is reserved"));
}
serializer.serialize_entry(&field_tag, &FieldBodySerializer {
code_page: self.0,
record_tag: self.1, prev_tag: self.2, field_tag, field
})?;
}
};
serializer.end()
}
}
struct RecordBodySerializer<'a>(Option<CodePage>, &'a Record);
impl<'a> Serialize for RecordBodySerializer<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
let has_flags = serializer.is_human_readable() && !self.1.flags.is_empty();
let entry_count = self.1.fields.len() + if has_flags { 1 } else { 0 };
let mut serializer = serializer.serialize_seq(Some(entry_count))?;
if has_flags {
serializer.serialize_element(&FieldSerializer(self.0, self.1.tag, META, Left(self.1.flags)))?;
}
let mut prev_tag = META;
for &(field_tag, ref field) in &self.1.fields {
serializer.serialize_element(&FieldSerializer(self.0, self.1.tag, prev_tag, Right((field_tag, field))))?;
prev_tag = field_tag;
}
serializer.end()
}
}
#[derive(Clone)]
pub struct RecordSerde {
pub code_page: Option<CodePage>
}
impl SerializeSeed for RecordSerde {
type Value = Record;
fn serialize<S>(&self, value: &Self::Value, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
let is_human_readable = serializer.is_human_readable();
let mut serializer = serializer.serialize_map(Some(1))?;
if is_human_readable {
serializer.serialize_entry(&value.tag, &RecordBodySerializer(self.code_page, value))?;
} else {
serializer.serialize_entry(&(value.tag, value.flags), &RecordBodySerializer(self.code_page, value))?;
}
serializer.end()
}
}
struct Base64DeVisitor;
impl<'de> de::Visitor<'de> for Base64DeVisitor {
type Value = Vec<u8>;
fn expecting(&self, f: &mut Formatter) -> fmt::Result { write!(f, "base64") }
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: de::Error {
base64_STANDARD.decode(s).map_err(|_| E::invalid_value(Unexpected::Str(s), &self))
}
}
struct ZlibEncoderDeVisitor;
impl<'de> de::Visitor<'de> for ZlibEncoderDeVisitor {
type Value = Vec<u8>;
fn expecting(&self, f: &mut Formatter) -> fmt::Result { write!(f, "bytes") }
fn visit_bytes<E>(self, bytes: &[u8]) -> Result<Self::Value, E> where E: de::Error {
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::new(5));
encoder.write_all(bytes).unwrap();
Ok(encoder.finish().unwrap())
}
}
struct FieldBodyDeserializer {
code_page: Option<CodePage>,
record_tag: Tag,
prev_tag: Tag,
field_tag: Tag
}
impl<'de> DeserializeSeed<'de> for FieldBodyDeserializer {
type Value = Either<RecordFlags, (Tag, Field)>;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error> where
D: Deserializer<'de> {
if deserializer.is_human_readable() && self.field_tag == META {
RecordFlags::deserialize(deserializer).map(Left)
} else {
match FieldType::from_tags(self.record_tag, self.prev_tag, self.field_tag) {
FieldType::String(len) =>
StringSerde {
code_page: self.code_page, len: len.map(|x| x.try_into().unwrap())
}.deserialize(deserializer).map(Field::String),
FieldType::StringZ =>
StringZSerde { code_page: self.code_page }.deserialize(deserializer).map(Field::StringZ),
FieldType::Multiline(newline) =>
StringListSerde {
code_page: self.code_page, separator: newline.as_str(), len: None
}.deserialize(deserializer).map(Field::StringList),
FieldType::StringZList =>
StringZListSerde { code_page: self.code_page }.deserialize(deserializer).map(Field::StringZList),
FieldType::U8List => <Vec<u8>>::deserialize(deserializer).map(Field::U8List),
FieldType::ScriptData =>
ScriptDataSerde { code_page: self.code_page }.deserialize(deserializer).map(Field::ScriptData),
FieldType::U8ListZip => if deserializer.is_human_readable() {
deserializer.deserialize_str(Base64DeVisitor)
} else {
deserializer.deserialize_bytes(ZlibEncoderDeVisitor)
}.map(Field::U8List),
FieldType::Info => Info::deserialize(deserializer).map(Field::Info),
FieldType::Item => ItemSerde { code_page: self.code_page }.deserialize(deserializer).map(Field::Item),
FieldType::CurrentTime => CurrentTime::deserialize(deserializer).map(Field::CurrentTime),
FieldType::Time => Time::deserialize(deserializer).map(Field::Time),
FieldType::Ingredient => Ingredient::deserialize(deserializer).map(Field::Ingredient),
FieldType::ScriptMetadata =>
ScriptMetadataSerde { code_page: self.code_page }.deserialize(deserializer).map(Field::ScriptMetadata),
FieldType::ScriptVars => ScriptVars::deserialize(deserializer).map(Field::ScriptVars),
FieldType::FileMetadata =>
FileMetadataSerde { code_page: self.code_page }.deserialize(deserializer).map(Field::FileMetadata),
FieldType::NpcState => NpcState::deserialize(deserializer).map(Field::NpcState),
FieldType::Effect => Effect::deserialize(deserializer).map(Field::Effect),
FieldType::Potion => Potion::deserialize(deserializer).map(Field::Potion),
FieldType::Npc => Npc::deserialize(deserializer).map(Field::Npc),
FieldType::DialogMetadata => DialogTypeOption::deserialize(deserializer).map(|x| x.into()),
FieldType::PosRotOrCell => PosRotOrCell::deserialize(deserializer).map(|x| x.into()),
FieldType::Spell => Spell::deserialize(deserializer).map(Field::Spell),
FieldType::Pos => Pos::deserialize(deserializer).map(Field::Pos),
FieldType::PosRot => PosRot::deserialize(deserializer).map(Field::PosRot),
FieldType::Sound => Sound::deserialize(deserializer).map(Field::Sound),
FieldType::Skill => Skill::deserialize(deserializer).map(Field::Skill),
FieldType::EffectArg => EffectArg::deserialize(deserializer).map(Field::EffectArg),
FieldType::EffectIndex => EffectIndex::deserialize(deserializer).map(Field::EffectIndex),
FieldType::Tag => Tag::deserialize(deserializer).map(Field::Tag),
FieldType::EffectMetadata => EffectMetadata::deserialize(deserializer).map(Field::EffectMetadata),
FieldType::Ai => Ai::deserialize(deserializer).map(Field::Ai),
FieldType::AiWander => AiWander::deserialize(deserializer).map(Field::AiWander),
FieldType::AiTravel => AiTravel::deserialize(deserializer).map(Field::AiTravel),
FieldType::AiTarget =>
AiTargetSerde { code_page: self.code_page }.deserialize(deserializer).map(Field::AiTarget),
FieldType::AiActivate =>
AiActivateSerde { code_page: self.code_page }.deserialize(deserializer).map(Field::AiActivate),
FieldType::NpcFlags => <FlagsAndBlood<NpcFlags>>::deserialize(deserializer).map(Field::NpcFlags),
FieldType::CreatureFlags => <FlagsAndBlood<CreatureFlags>>::deserialize(deserializer).map(Field::CreatureFlags),
FieldType::SoundChance =>
SoundChanceSerde { code_page: self.code_page }.deserialize(deserializer).map(Field::SoundChance),
FieldType::SkillMetadata => SkillMetadata::deserialize(deserializer).map(Field::SkillMetadata),
FieldType::Color => Color::deserialize(deserializer).map(Field::Color),
FieldType::Interior => Interior::deserialize(deserializer).map(Field::Interior),
FieldType::Book => Book::deserialize(deserializer).map(Field::Book),
FieldType::SoundGen => SoundGen::deserialize(deserializer).map(Field::SoundGen),
FieldType::Tool => Tool::deserialize(deserializer).map(Field::Tool),
FieldType::RepairItem => if deserializer.is_human_readable() {
Tool::deserialize(deserializer)
} else {
RepairItem::deserialize(deserializer).map(|x| x.into())
}.map(Field::Tool),
FieldType::Weather => Weather::deserialize(deserializer).map(Field::Weather),
FieldType::Light => Light::deserialize(deserializer).map(Field::Light),
FieldType::Race => Race::deserialize(deserializer).map(Field::Race),
FieldType::Faction => Faction::deserialize(deserializer).map(Field::Faction),
FieldType::MiscItem => MiscItem::deserialize(deserializer).map(Field::MiscItem),
FieldType::Apparatus => Apparatus::deserialize(deserializer).map(Field::Apparatus),
FieldType::Weapon => Weapon::deserialize(deserializer).map(Field::Weapon),
FieldType::Armor => Armor::deserialize(deserializer).map(Field::Armor),
FieldType::BipedObject => BipedObject::deserialize(deserializer).map(Field::BipedObject),
FieldType::BodyPart => BodyPart::deserialize(deserializer).map(Field::BodyPart),
FieldType::Clothing => Clothing::deserialize(deserializer).map(Field::Clothing),
FieldType::Enchantment => Enchantment::deserialize(deserializer).map(Field::Enchantment),
FieldType::Creature => Creature::deserialize(deserializer).map(Field::Creature),
FieldType::ContainerFlags => ContainerFlags::deserialize(deserializer).map(Field::ContainerFlags),
FieldType::Class => Class::deserialize(deserializer).map(Field::Class),
FieldType::Attributes => Attributes::deserialize(deserializer).map(Field::Attributes),
FieldType::Skills => Skills::deserialize(deserializer).map(Field::Skills),
FieldType::Grid => Grid::deserialize(deserializer).map(Field::Grid),
FieldType::PathGrid => PathGrid::deserialize(deserializer).map(Field::PathGrid),
FieldType::MarkerU8(none) => NoneU8Serde { none }.deserialize(deserializer).map(|()| Field::None),
FieldType::Bool8 => BoolU8Serde.deserialize(deserializer).map(Field::Bool),
FieldType::Bool32 => BoolU32Serde.deserialize(deserializer).map(Field::Bool),
FieldType::F32 => F32AsIsSerde.deserialize(deserializer).map(Field::F32),
FieldType::I32 => i32::deserialize(deserializer).map(Field::I32),
FieldType::I16 => i16::deserialize(deserializer).map(Field::I16),
FieldType::I64 => i64::deserialize(deserializer).map(Field::I64),
FieldType::F32List => VecSerde(F32AsIsSerde).deserialize(deserializer).map(Field::F32List),
FieldType::I32List => <Vec<i32>>::deserialize(deserializer).map(Field::I32List),
FieldType::I16List => <Vec<i16>>::deserialize(deserializer).map(Field::I16List),
FieldType::U8 => u8::deserialize(deserializer).map(Field::U8),
}.map(|x| Right((self.field_tag, x)))
}
}
}
struct FieldDeserializer {
code_page: Option<CodePage>,
record_tag: Tag,
prev_tag: Tag,
}
impl<'de> de::Visitor<'de> for FieldDeserializer {
type Value = Either<RecordFlags, (Tag, Field)>;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "record flags or field")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> where
A: de::MapAccess<'de> {
let field_tag: Tag = map.next_key()?
.ok_or_else(|| A::Error::custom("missed field tag"))?;
let body = map.next_value_seed(FieldBodyDeserializer {
code_page: self.code_page,
record_tag: self.record_tag, prev_tag: self.prev_tag, field_tag
})?;
if map.next_key::<Tag>()?.is_some() {
return Err(A::Error::custom("duplicated field tag"));
}
Ok(body)
}
}
impl<'de> DeserializeSeed<'de> for FieldDeserializer {
type Value = Either<RecordFlags, (Tag, Field)>;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error> where
D: Deserializer<'de> {
deserializer.deserialize_map(self)
}
}
struct RecordBodyDeserializer {
code_page: Option<CodePage>,
record_tag: Tag,
record_flags: Option<RecordFlags>
}
impl<'de> de::Visitor<'de> for RecordBodyDeserializer {
type Value = Record;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "field list")
}
fn visit_seq<A>(mut self, mut seq: A) -> Result<Self::Value, A::Error> where
A: de::SeqAccess<'de> {
let mut fields = seq.size_hint().map_or_else(Vec::new, Vec::with_capacity);
let mut prev_tag = META;
while let Some(field) = seq.next_element_seed(FieldDeserializer {
code_page: self.code_page, record_tag: self.record_tag, prev_tag
})? {
match field {
Left(flags) => {
if self.record_flags.replace(flags).is_some() {
return Err(A::Error::custom("duplicated record flags"));
}
},
Right(field) => {
prev_tag = field.0;
fields.push(field);
}
}
}
Ok(Record { tag: self.record_tag, flags: self.record_flags.unwrap_or(RecordFlags::empty()), fields })
}
}
impl<'de> DeserializeSeed<'de> for RecordBodyDeserializer {
type Value = Record;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error> where
D: Deserializer<'de> {
deserializer.deserialize_seq(self)
}
}
struct RecordVisitor {
code_page: Option<CodePage>,
is_human_readable: bool
}
impl<'de> de::Visitor<'de> for RecordVisitor {
type Value = Record;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "record")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> where A: de::MapAccess<'de> {
let (record_tag, record_flags) = if self.is_human_readable {
let record_tag: Tag = map.next_key()?
.ok_or_else(|| A::Error::custom("missed record tag"))?;
(record_tag, None)
} else {
let (record_tag, record_flags): (Tag, RecordFlags) = map.next_key()?
.ok_or_else(|| A::Error::custom("missed record tag and flags"))?;
(record_tag, Some(record_flags))
};
let body = map.next_value_seed(RecordBodyDeserializer { code_page: self.code_page, record_tag, record_flags })?;
if map.next_key::<Tag>()?.is_some() {
return Err(A::Error::custom("duplicated record tag"));
}
Ok(body)
}
}
impl<'de> DeserializeSeed<'de> for RecordSerde {
type Value = Record;
fn deserialize<D>(self, deserializer: D) -> Result<Record, D::Error> where D: Deserializer<'de> {
let is_human_readable = deserializer.is_human_readable();
deserializer.deserialize_map(RecordVisitor { code_page: self.code_page, is_human_readable } )
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
enum DialogTypeOption {
None(i32),
Some(DialogType)
}
impl From<DialogTypeOption> for Field {
fn from(v: DialogTypeOption) -> Self {
match v {
DialogTypeOption::None(i) => Field::I32(i),
DialogTypeOption::Some(v) => Field::DialogType(v),
}
}
}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
#[serde(rename="DialogTypeOption")]
enum DialogTypeOptionHRSurrogate {
None(i32),
Some(DialogType)
}
impl From<DialogTypeOption> for DialogTypeOptionHRSurrogate {
fn from(t: DialogTypeOption) -> Self {
match t {
DialogTypeOption::None(i) => DialogTypeOptionHRSurrogate::None(i),
DialogTypeOption::Some(v) => DialogTypeOptionHRSurrogate::Some(v),
}
}
}
impl From<DialogTypeOptionHRSurrogate> for DialogTypeOption {
fn from(t: DialogTypeOptionHRSurrogate) -> Self {
match t {
DialogTypeOptionHRSurrogate::None(i) => DialogTypeOption::None(i),
DialogTypeOptionHRSurrogate::Some(v) => DialogTypeOption::Some(v),
}
}
}
const I32_SERDE_SIZE: u32 = 4;
const DIALOG_TYPE_SERDE_SIZE: u32 = 1;
impl Serialize for DialogTypeOption {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
if serializer.is_human_readable() {
DialogTypeOptionHRSurrogate::from(self.clone()).serialize(serializer)
} else {
match self {
DialogTypeOption::None(padding) => serializer.serialize_newtype_variant(
name_of!(type DialogTypeOption),
I32_SERDE_SIZE,
name_of!(const None in DialogTypeOption),
padding
),
DialogTypeOption::Some(c) => serializer.serialize_newtype_variant(
name_of!(type DialogTypeOption),
DIALOG_TYPE_SERDE_SIZE,
name_of!(const Some in DialogTypeOption),
c
),
}
}
}
}
struct DialogTypeOptionNHRDeserializer;
impl<'de> de::Visitor<'de> for DialogTypeOptionNHRDeserializer {
type Value = DialogTypeOption;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "32-bit integer or dialog type")
}
fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error> where A: de::EnumAccess<'de> {
let (variant_index, variant) = data.variant::<u32>()?;
match variant_index {
I32_SERDE_SIZE => Ok(DialogTypeOption::None(variant.newtype_variant()?)),
DIALOG_TYPE_SERDE_SIZE => Ok(DialogTypeOption::Some(variant.newtype_variant()?)),
n => Err(A::Error::invalid_value(Unexpected::Unsigned(n as u64), &self))
}
}
}
impl<'de> Deserialize<'de> for DialogTypeOption {
fn deserialize<D>(deserializer: D) -> Result<DialogTypeOption, D::Error> where D: Deserializer<'de> {
if deserializer.is_human_readable() {
DialogTypeOptionHRSurrogate::deserialize(deserializer).map(|x| x.into())
} else {
deserializer.deserialize_enum(
name_of!(type DialogTypeOption),
&[name_of!(const None in DialogTypeOption), name_of!(const Some in DialogTypeOption)],
DialogTypeOptionNHRDeserializer
)
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
enum PosRotOrCell {
PosRot(PosRot),
Cell(Cell)
}
impl From<PosRotOrCell> for Field {
fn from(v: PosRotOrCell) -> Self {
match v {
PosRotOrCell::PosRot(p) => Field::PosRot(p),
PosRotOrCell::Cell(c) => Field::Cell(c),
}
}
}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
#[serde(rename="PosRotOrCell")]
enum PosRotOrCellHRSurrogate {
PosRot(PosRot),
Cell(Cell)
}
impl From<PosRotOrCell> for PosRotOrCellHRSurrogate {
fn from(t: PosRotOrCell) -> Self {
match t {
PosRotOrCell::PosRot(p) => PosRotOrCellHRSurrogate::PosRot(p),
PosRotOrCell::Cell(c) => PosRotOrCellHRSurrogate::Cell(c),
}
}
}
impl From<PosRotOrCellHRSurrogate> for PosRotOrCell {
fn from(t: PosRotOrCellHRSurrogate) -> Self {
match t {
PosRotOrCellHRSurrogate::PosRot(p) => PosRotOrCell::PosRot(p),
PosRotOrCellHRSurrogate::Cell(c) => PosRotOrCell::Cell(c),
}
}
}
const POS_ROT_SERDE_SIZE: u32 = 24;
const CELL_SERDE_SIZE: u32 = 12;
impl Serialize for PosRotOrCell {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
if serializer.is_human_readable() {
PosRotOrCellHRSurrogate::from(self.clone()).serialize(serializer)
} else {
match self {
PosRotOrCell::PosRot(pos_rot) => serializer.serialize_newtype_variant(
name_of!(type PosRotOrCell),
POS_ROT_SERDE_SIZE,
name_of!(const PosRot in PosRotOrCell),
pos_rot
),
PosRotOrCell::Cell(cell) => serializer.serialize_newtype_variant(
name_of!(type PosRotOrCell),
CELL_SERDE_SIZE,
name_of!(const Cell in PosRotOrCell),
cell
),
}
}
}
}
struct PosRotOrCellNHRDeserializer;
impl<'de> de::Visitor<'de> for PosRotOrCellNHRDeserializer {
type Value = PosRotOrCell;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "pos_rot or cell")
}
fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error> where A: de::EnumAccess<'de> {
let (variant_index, variant) = data.variant::<u32>()?;
match variant_index {
POS_ROT_SERDE_SIZE => Ok(PosRotOrCell::PosRot(variant.newtype_variant()?)),
CELL_SERDE_SIZE => Ok(PosRotOrCell::Cell(variant.newtype_variant()?)),
n => Err(A::Error::invalid_value(Unexpected::Unsigned(n as u64), &self))
}
}
}
impl<'de> Deserialize<'de> for PosRotOrCell {
fn deserialize<D>(deserializer: D) -> Result<PosRotOrCell, D::Error> where D: Deserializer<'de> {
if deserializer.is_human_readable() {
PosRotOrCellHRSurrogate::deserialize(deserializer).map(|x| x.into())
} else {
deserializer.deserialize_enum(
name_of!(type PosRotOrCell),
&[name_of!(const PosRot in PosRotOrCell), name_of!(const Cell in PosRotOrCell)],
PosRotOrCellNHRDeserializer
)
}
}
}
#[cfg(test)]
mod tests {
use crate::*;
use serde::de::DeserializeSeed;
use serde_serialize_seed::ValueWithSeed;
use std::str::FromStr;
use std::hash::Hash;
use std::collections::hash_map::DefaultHasher;
#[allow(clippy::deref_addrof)]
#[test]
fn record_flags_traits() {
assert_eq!(RecordFlags::PERSIST, *&RecordFlags::PERSIST);
assert!(RecordFlags::PERSIST < RecordFlags::BLOCKED);
let mut hasher = DefaultHasher::new();
RecordFlags::DELETED.hash(&mut hasher);
}
#[test]
fn test_record_flags() {
assert_eq!("PERSIST", format!("{}", RecordFlags::PERSIST));
assert_eq!("PERSIST DELETED", format!("{}", RecordFlags::PERSIST | RecordFlags::DELETED));
assert_eq!(0x202000000000, (RecordFlags::BLOCKED | RecordFlags::DELETED).bits());
assert_eq!(Some(RecordFlags::BLOCKED | RecordFlags::DELETED), RecordFlags::from_bits(0x202000000000));
assert_eq!(Ok(RecordFlags::DELETED), RecordFlags::from_str("DELETED"));
assert_eq!(Ok(RecordFlags::DELETED | RecordFlags::PERSIST), RecordFlags::from_str("DELETED PERSIST"));
assert_eq!(Ok(RecordFlags::DELETED | RecordFlags::PERSIST), RecordFlags::from_str("PERSIST DELETED"));
assert_eq!(Ok(RecordFlags::empty()), RecordFlags::from_str(""));
assert_eq!(Ok(RecordFlags::empty()), RecordFlags::from_str(" "));
}
#[test]
fn record_yaml() {
let record = Record {
tag: SCPT,
flags: RecordFlags::PERSIST,
fields: vec![
(SCHD, Field::ScriptMetadata(ScriptMetadata {
name: "Scr1".into(),
vars: ScriptVars { shorts: 1, longs: 2, floats: 3 },
data_size: 800, var_table_size: 35
})),
(SCTX, Field::StringList(vec![
"Begin Scr1\\".into(),
" short\u{7} i".into(),
"End Scr1".into(),
]))
]
};
let yaml = serde_yaml::to_string(&ValueWithSeed(&record, RecordSerde { code_page: None })).unwrap();
let res = RecordSerde { code_page: None }.deserialize(serde_yaml::Deserializer::from_str(&yaml)).unwrap();
assert_eq!(res.tag, record.tag);
assert_eq!(res.flags, record.flags);
assert_eq!(res.fields.len(), 2);
assert_eq!(res.fields[0].0, SCHD);
assert_eq!(res.fields[1].0, SCTX);
if let Field::ScriptMetadata(res) = &res.fields[0].1 {
assert_eq!(res.name, "Scr1");
assert_eq!(res.vars.shorts, 1);
assert_eq!(res.vars.longs, 2);
assert_eq!(res.vars.floats, 3);
assert_eq!(res.data_size, 800);
assert_eq!(res.var_table_size, 35);
} else {
panic!()
}
if let Field::StringList(res) = &res.fields[1].1 {
assert_eq!(res.len(), 3);
assert_eq!(res[0], "Begin Scr1\\");
assert_eq!(res[1], " short\u{7} i");
assert_eq!(res[2], "End Scr1");
} else {
panic!()
}
}
}