use crate::code_page::CodePage;
use crate::script_data::*;
use crate::strings::*;
use crate::serde_helpers::*;
use educe::Educe;
use either::{Either, Left, Right};
use enum_derive_2018::{EnumDisplay, EnumFromStr};
use enumn::N;
use macro_attr_2018::macro_attr;
use nameof::name_of;
use serde::{Serialize, Deserialize, Serializer, Deserializer};
use serde::de::Error as de_Error;
use serde::de::{self, DeserializeSeed, Unexpected, VariantAccess};
use serde::ser::SerializeStruct;
use serde::ser::Error as ser_Error;
use serde_serialize_seed::{SerializeSeed, ValueWithSeed};
use std::convert::TryFrom;
use std::fmt::{self, Debug, Display, Formatter};
use std::mem::transmute;
use std::ops::{Index, IndexMut};
use std::str::FromStr;
pub use crate::tag::*;
include!(concat!(env!("OUT_DIR"), "/tags.rs"));
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone, Debug)]
pub(crate) enum Newline {
Unix,
Dos
}
impl Newline {
pub fn as_str(self) -> &'static str {
if self == Newline::Unix { "\n" } else { "\r\n" }
}
}
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u32)]
pub enum FileType {
ESP = 0,
ESM = 1,
ESS = 32
}
}
enum_serde!(FileType, "file type", as u32, Unsigned, u64);
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u8)]
pub enum DialogType {
Topic = 0,
Voice = 1,
Greeting = 2,
Persuasion = 3,
Journal = 4
}
}
enum_serde!(DialogType, "dialog type", as u8, Unsigned, u64);
mod dialog_type_u32 {
use crate::field::DialogType;
use serde::{Serializer, Deserializer, Serialize, Deserialize};
use serde::de::Unexpected;
use serde::de::Error as de_Error;
use std::convert::TryInto;
pub fn serialize<S>(&v: &DialogType, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
if serializer.is_human_readable() {
v.serialize(serializer)
} else {
(v as u32).serialize(serializer)
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<DialogType, D::Error> where D: Deserializer<'de> {
if deserializer.is_human_readable() {
DialogType::deserialize(deserializer)
} else {
let d = u32::deserialize(deserializer)?;
d.try_into().ok().and_then(DialogType::n).ok_or_else(|| D::Error::invalid_value(Unexpected::Unsigned(d as u64), &"dialog type"))
}
}
}
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N)]
#[repr(u32)]
pub enum EffectRange {
Self_ = 0,
Touch = 1,
Target = 2,
}
}
impl Display for EffectRange {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
EffectRange::Self_ => write!(f, "Self"),
EffectRange::Touch => write!(f, "Touch"),
EffectRange::Target => write!(f, "Target"),
}
}
}
impl FromStr for EffectRange {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Self" => Ok(EffectRange::Self_),
"Touch" => Ok(EffectRange::Touch),
"Target" => Ok(EffectRange::Target),
_ => Err(())
}
}
}
enum_serde!(EffectRange, "effect range", as u32, Unsigned, u64);
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum FieldType {
U8List, U8ListZip,
String(Option<u32>),
StringZ, StringZList,
Multiline(Newline),
F32, I32, I16, I64, U8,
MarkerU8(u8),
Bool8, Bool32,
Ingredient, ScriptMetadata, DialogMetadata, FileMetadata, Npc, NpcState, Effect, Spell,
Ai, AiWander, AiTravel, AiTarget, AiActivate, NpcFlags, CreatureFlags, Book, ContainerFlags,
Creature, Light, MiscItem, Apparatus, Weapon, Armor, BipedObject, BodyPart, Clothing, Enchantment,
Tool, RepairItem, Pos, PosRot, PosRotOrCell, Grid, PathGrid, ScriptVars,
I16List, I32List, F32List, Weather, Color, SoundChance, Potion, Class, Skill, EffectIndex,
Item, Sound, EffectMetadata, Race, SoundGen, Info, Faction, SkillMetadata, Interior,
CurrentTime, Time, EffectArg,
Attributes, Skills, Tag,
ScriptData
}
impl FieldType {
pub fn from_tags(record_tag: Tag, prev_tag: Tag, field_tag: Tag) -> FieldType {
match (record_tag, prev_tag, field_tag) {
(APPA, _, AADT) => FieldType::Apparatus,
(INFO, _, ACDT) => FieldType::StringZ,
(_, _, ACDT) => FieldType::U8ListZip,
(_, _, ACID) => FieldType::I32,
(_, _, ACSC) => FieldType::U8ListZip,
(_, _, ACSL) => FieldType::U8ListZip,
(_, _, ACT_) => FieldType::String(None),
(_, _, ACTN) => FieldType::I32,
(_, _, ACTV) => FieldType::MarkerU8(1),
(_, _, AFLG) => FieldType::I32,
(_, _, AI_A) => FieldType::AiActivate,
(_, _, AI_E) => FieldType::AiTarget,
(_, _, AI_F) => FieldType::AiTarget,
(_, _, AI_T) => FieldType::AiTravel,
(_, _, AI_W) => FieldType::AiWander,
(_, _, AIDT) => FieldType::Ai,
(_, _, AIPK) => FieldType::Tag,
(_, _, AISE) => FieldType::Bool8,
(ALCH, _, ALDT) => FieldType::Potion,
(_, _, ALWY) => FieldType::Bool8,
(CELL, _, AMBI) => FieldType::Interior,
(FACT, _, ANAM) => FieldType::String(None),
(_, _, ANAM) => FieldType::StringZ,
(_, _, ANGL) => FieldType::F32,
(_, _, ANIS) => FieldType::String(None),
(ARMO, _, AODT) => FieldType::Armor,
(REFR, _, APUD) => FieldType::String(None), (_, _, ARG_) => FieldType::EffectArg,
(_, _, ASND) => FieldType::StringZ,
(_, _, AVFX) => FieldType::StringZ,
(_, _, BASE) => FieldType::F32,
(BOOK, _, BKDT) => FieldType::Book,
(ARMO, _, BNAM) => FieldType::String(None),
(BODY, _, BNAM) => FieldType::String(None),
(CLOT, _, BNAM) => FieldType::String(None),
(INFO, _, BNAM) => FieldType::Multiline(Newline::Dos),
(_, _, BNAM) => FieldType::StringZ,
(_, _, BNDS) => FieldType::I32List,
(_, _, BOUN) => FieldType::F32List,
(_, _, BSND) => FieldType::StringZ,
(_, _, BVFX) => FieldType::StringZ,
(BODY, _, BYDT) => FieldType::BodyPart,
(_, _, CAST) => FieldType::I32,
(_, _, CFLG) => FieldType::I32,
(_, _, CHRD) => FieldType::U8ListZip,
(_, _, CIDX) => FieldType::I64,
(CLAS, _, CLDT) => FieldType::Class,
(_, _, CMND) => FieldType::Bool8,
(ARMO, _, CNAM) => FieldType::String(None),
(CLOT, _, CNAM) => FieldType::String(None),
(KLST, _, CNAM) => FieldType::I32,
(REGN, _, CNAM) => FieldType::Color,
(_, _, CNAM) => FieldType::StringZ,
(CELL, _, CNDT) => FieldType::Grid,
(CONT, _, CNDT) => FieldType::F32,
(_, TIME, COUN) => FieldType::I64,
(_, ANIS, COUN) => FieldType::I64,
(_, _, COUN) => FieldType::I32,
(CELL, _, CRED) => FieldType::U8ListZip,
(_, _, CREG) => FieldType::StringZ,
(_, _, CRID) => FieldType::I32,
(CELL, _, CSHN) => FieldType::StringZ,
(_, _, CSND) => FieldType::StringZ,
(CELL, _, CSTN) => FieldType::StringZ,
(CLOT, _, CTDT) => FieldType::Clothing,
(_, _, CURD) => FieldType::I32,
(_, _, CVFX) => FieldType::StringZ,
(_, _, CWTH) => FieldType::I32,
(_, _, DANM) => FieldType::U8,
(CELL, _, DATA) => FieldType::PosRotOrCell,
(DIAL, _, DATA) => FieldType::DialogMetadata,
(GMAP, _, DATA) => FieldType::U8ListZip,
(INFO, _, DATA) => FieldType::Info,
(LAND, _, DATA) => FieldType::I32,
(LEVC, _, DATA) => FieldType::I32,
(LEVI, _, DATA) => FieldType::I32,
(LTEX, _, DATA) => FieldType::StringZ,
(PGRD, _, DATA) => FieldType::PathGrid,
(REFR, _, DATA) => FieldType::PosRot,
(SNDG, _, DATA) => FieldType::SoundGen,
(SOUN, _, DATA) => FieldType::Sound,
(SSCR, _, DATA) => FieldType::String(None),
(TES3, _, DATA) => FieldType::I64,
(QUES, _, DATA) => FieldType::StringZ,
(_, _, DATA) => FieldType::U8ListZip,
(_, _, DELE) => FieldType::I32,
(_, _, DEPE) => FieldType::String(None),
(BSGN, _, DESC) => FieldType::StringZ,
(_, _, DESC) => FieldType::String(None),
(_, CAST, DISP) => FieldType::String(None),
(_, _, DISP) => FieldType::I32,
(_, _, DNAM) => FieldType::StringZ,
(_, _, DODT) => FieldType::PosRot,
(_, _, DRAW) => FieldType::I32,
(_, _, DRTI) => FieldType::F32,
(_, _, DTIM) => FieldType::Time,
(_, _, DURA) => FieldType::F32,
(_, _, EFID) => FieldType::EffectIndex,
(_, _, EIND) => FieldType::I32,
(_, _, ENAB) => FieldType::Bool8,
(ALCH, _, ENAM) => FieldType::Effect,
(ENCH, _, ENAM) => FieldType::Effect,
(PCDT, _, ENAM) => FieldType::I64,
(SPEL, _, ENAM) => FieldType::Effect,
(_, _, ENAM) => FieldType::StringZ,
(ENCH, _, ENDT) => FieldType::Enchantment,
(_, _, EQIP) => FieldType::U8ListZip,
(_, _, FACT) => FieldType::String(None),
(FACT, _, FADT) => FieldType::Faction,
(_, _, FALL) => FieldType::F32,
(_, _, FARA) => FieldType::I32,
(_, _, FARE) => FieldType::I32,
(_, _, FAST) => FieldType::Bool8,
(CELL, _, FGTN) => FieldType::StringZ,
(CAM_, _, FIRS) => FieldType::Bool8,
(CONT, _, FLAG) => FieldType::ContainerFlags,
(CREA, _, FLAG) => FieldType::CreatureFlags,
(NPC_, _, FLAG) => FieldType::NpcFlags,
(_, _, FLAG) => FieldType::I32,
(_, _, FLTV) => FieldType::F32,
(GLOB, _, FNAM) => FieldType::String(None),
(PCDT, _, FNAM) => FieldType::U8ListZip,
(STLN, _, FNAM) => FieldType::String(None),
(_, _, FNAM) => FieldType::StringZ,
(_, _, FORM) => FieldType::I32,
(CELL, _, FRMR) => FieldType::I32,
(_, _, FRMR) => FieldType::I32List,
(_, _, FTEX) => FieldType::U8ListZip,
(_, _, GMDT) => FieldType::U8ListZip,
(_, _, GOLD) => FieldType::I32,
(_, _, GRAV) => FieldType::I32,
(_, _, HCUS) => FieldType::MarkerU8(0),
(TES3, _, HEDR) => FieldType::FileMetadata,
(_, _, HFOW) => FieldType::Bool32,
(_, _, HIDD) => FieldType::Bool8,
(_, _, HLOC) => FieldType::MarkerU8(1),
(_, _, HSND) => FieldType::StringZ,
(_, _, HVFX) => FieldType::StringZ,
(_, _, ICNT) => FieldType::I32,
(_, _, ID__) => FieldType::String(None),
(_, _, INAM) => FieldType::StringZ,
(_, _, INCR) => FieldType::Attributes,
(ARMO, _, INDX) => FieldType::BipedObject,
(CLOT, _, INDX) => FieldType::BipedObject,
(MGEF, _, INDX) => FieldType::EffectIndex,
(SKIL, _, INDX) => FieldType::Skill,
(_, _, INDX) => FieldType::I32,
(CELL, _, INTV) => FieldType::F32,
(LAND, _, INTV) => FieldType::Grid,
(LEVC, _, INTV) => FieldType::I16,
(LEVI, _, INTV) => FieldType::I16,
(_, _, INTV) => FieldType::I32,
(INGR, _, IRDT) => FieldType::Ingredient,
(_, _, ITEM) => FieldType::I32List,
(_, _, ITEX) => FieldType::StringZ,
(_, _, JEDA) => FieldType::I32,
(_, _, JEDM) => FieldType::I32,
(_, _, JEMO) => FieldType::I32,
(_, _, JETY) => FieldType::I32,
(PCDT, _, KNAM) => FieldType::U8ListZip,
(_, _, KNAM) => FieldType::StringZ,
(_, _, LAST) => FieldType::I32,
(_, _, LEFT) => FieldType::F32,
(_, _, LEVL) => FieldType::I32,
(ENAB, _, LEVT) => FieldType::Bool8,
(_, _, LHAT) => FieldType::String(None),
(LIGH, _, LHDT) => FieldType::Light,
(_, _, LHIT) => FieldType::String(None),
(PCDT, _, LNAM) => FieldType::I64,
(LOCK, _, LKDT) => FieldType::Tool,
(_, _, LKEP) => FieldType::Pos,
(_, _, LOCA) => FieldType::String(None),
(_, _, LPRO) => FieldType::I32,
(CELL, _, LSHN) => FieldType::StringZ,
(CELL, _, LSTN) => FieldType::StringZ,
(_, _, LUAD) => FieldType::U8ListZip,
(_, _, LUAS) => FieldType::String(None),
(_, _, LUAW) => FieldType::I64,
(_, _, LVCR) => FieldType::U8,
(_, _, MAGN) => FieldType::F32,
(FMAP, _, MAPD) => FieldType::U8ListZip,
(FMAP, _, MAPH) => FieldType::I64,
(_, _, MARK) => FieldType::PosRot,
(TES3, _, MAST) => FieldType::StringZ,
(MISC, _, MCDT) => FieldType::MiscItem,
(MGEF, _, MEDT) => FieldType::EffectMetadata,
(_, _, MGEF) => FieldType::EffectIndex,
(PCDT, _, MNAM) => FieldType::StringZ,
(CELL, _, MNAM) => FieldType::U8,
(_, _, MODI) => FieldType::F32,
(_, _, MODL) => FieldType::StringZ,
(_, _, MOVE) => FieldType::I32,
(_, _, MRK_) => FieldType::Grid,
(CELL, _, MVRF) => FieldType::I32,
(_, _, MVRF) => FieldType::I32List,
(CELL, _, NAM0) => FieldType::I32,
(PCDT, _, NAM0) => FieldType::StringZ,
(SPLM, _, NAM0) => FieldType::U8,
(PCDT, _, NAM1) => FieldType::StringZ,
(PCDT, _, NAM2) => FieldType::StringZ,
(PCDT, _, NAM3) => FieldType::StringZ,
(CELL, _, NAM5) => FieldType::I32,
(CELL, _, NAM8) => FieldType::U8ListZip,
(CELL, _, NAM9) => FieldType::I32,
(PCDT, _, NAM9) => FieldType::I32,
(GMST, _, NAME) => FieldType::String(None),
(GSCR, _, NAME) => FieldType::String(None),
(INFO, _, NAME) => FieldType::String(None),
(JOUR, _, NAME) => FieldType::Multiline(Newline::Unix),
(SPLM, _, NAME) => FieldType::I32,
(SSCR, _, NAME) => FieldType::String(None),
(STLN, _, NAME) => FieldType::String(None),
(_, _, NAME) => FieldType::StringZ,
(_, _, ND3D) => FieldType::U8,
(LEVC, _, NNAM) => FieldType::U8,
(LEVI, _, NNAM) => FieldType::U8,
(_, _, NNAM) => FieldType::StringZ,
(_, _, NPCO) => FieldType::Item,
(CREA, _, NPDT) => FieldType::Creature,
(SPLM, _, NPDT) => FieldType::U8ListZip,
(NPC_, _, NPDT) => FieldType::Npc,
(NPCC, _, NPDT) => FieldType::NpcState,
(_, _, NPCS) => FieldType::String(Some(32)),
(_, _, NWTH) => FieldType::I32,
(_, _, OBJE) => FieldType::Tag,
(STLN, _, ONAM) => FieldType::String(None),
(_, _, ONAM) => FieldType::StringZ,
(_, _, PAYD) => FieldType::I32,
(PROB, _, PBDT) => FieldType::Tool,
(_, _, PGRC) => FieldType::U8ListZip,
(_, _, PGRP) => FieldType::U8ListZip,
(_, _, PLCE) => FieldType::String(None),
(_, _, PLCN) => FieldType::String(None),
(_, _, PLLE) => FieldType::I32,
(_, _, PLNA) => FieldType::String(None),
(PCDT, _, PNAM) => FieldType::U8ListZip,
(_, _, PNAM) => FieldType::StringZ,
(_, STAR, POS_) => FieldType::Pos,
(_, _, POS_) => FieldType::PosRot,
(_, _, PTEX) => FieldType::StringZ,
(_, _, QFIN) => FieldType::Bool8,
(_, _, QSTA) => FieldType::I32,
(_, _, QWTH) => FieldType::I32,
(RACE, _, RADT) => FieldType::Race,
(_, _, REPT) => FieldType::MarkerU8(1),
(_, _, REPU) => FieldType::I32,
(_, _, RESP) => FieldType::Time,
(_, _, RGNC) => FieldType::U8,
(_, _, RGNN) => FieldType::StringZ,
(_, _, RGNW) => FieldType::I32,
(REPA, _, RIDT) => FieldType::RepairItem,
(FACT, _, RNAM) => FieldType::String(Some(32)),
(SCPT, _, RNAM) => FieldType::I32,
(_, _, RNAM) => FieldType::StringZ,
(_, _, RUN_) => FieldType::Bool32,
(SCPT, _, SCDT) => FieldType::ScriptData,
(SCPT, _, SCHD) => FieldType::ScriptMetadata,
(TES3, _, SCRD) => FieldType::U8ListZip,
(_, _, SCRI) => FieldType::StringZ,
(_, _, SCRN) => FieldType::U8ListZip,
(TES3, _, SCRS) => FieldType::U8ListZip,
(_, _, SCTX) => FieldType::Multiline(Newline::Dos),
(SCPT, _, SCVR) => FieldType::StringZList,
(_, _, SCVR) => FieldType::String(None),
(_, _, SELE) => FieldType::I32,
(_, _, SIGN) => FieldType::String(None),
(SKIL, _, SKDT) => FieldType::SkillMetadata,
(_, _, SLCS) => FieldType::ScriptVars,
(_, _, SLFD) => FieldType::F32List,
(_, _, SLLD) => FieldType::I32List,
(_, _, SLSD) => FieldType::I16List,
(PCDT, _, SNAM) => FieldType::U8ListZip,
(REGN, _, SNAM) => FieldType::SoundChance,
(_, _, SNAM) => FieldType::StringZ,
(_, _, SPAC) => FieldType::String(None),
(_, _, SPAW) => FieldType::I32,
(SPLM, _, SPDT) => FieldType::U8ListZip,
(SPEL, _, SPDT) => FieldType::Spell,
(_, _, SPEC) => FieldType::I32List,
(_, _, SPEL) => FieldType::String(None),
(_, _, STAR) => FieldType::Time,
(_, _, STBA) => FieldType::F32,
(_, _, STCU) => FieldType::F32,
(_, _, STDF) => FieldType::F32,
(_, _, STMO) => FieldType::F32,
(CELL, _, STPR) => FieldType::U8ListZip,
(REFR, _, STPR) => FieldType::U8ListZip,
(_, _, STPR) => FieldType::F32,
(_, _, STRV) => FieldType::String(None),
(_, _, TAID) => FieldType::I32,
(GSCR, _, TARG) => FieldType::String(None),
(_, DATA, TARG) => FieldType::String(None),
(_, _, TARG) => FieldType::I32,
(ENAB, _, TELE) => FieldType::Bool8,
(BOOK, _, TEXT) => FieldType::Multiline(Newline::Dos),
(JOUR, _, TEXT) => FieldType::String(None),
(_, _, TEXT) => FieldType::StringZ,
(CSTA, ANIS, TIME) => FieldType::I32,
(CSTA, _, TIME) => FieldType::I64,
(_, _, TIME) => FieldType::Time,
(_, _, TMPS) => FieldType::I32,
(_, _, TNAM) => FieldType::StringZ,
(_, _, TOPI) => FieldType::String(None),
(_, _, TRFC) => FieldType::I32,
(_, _, TSTM) => FieldType::CurrentTime,
(_, _, TYPE) => FieldType::I32,
(_, _, QID_) => FieldType::String(None),
(INFO, _, QSTF) => FieldType::MarkerU8(1),
(INFO, _, QSTN) => FieldType::MarkerU8(1),
(INFO, _, QSTR) => FieldType::MarkerU8(1),
(_, _, USED) => FieldType::String(None),
(_, _, UNAM) => FieldType::MarkerU8(0),
(_, _, VCLR) => FieldType::U8ListZip,
(_, _, VHGT) => FieldType::U8ListZip,
(_, _, VNML) => FieldType::U8ListZip,
(_, _, VTEX) => FieldType::U8ListZip,
(REGN, _, WEAT) => FieldType::Weather,
(_, _, WHGT) => FieldType::F32,
(_, _, WIDX) => FieldType::I64,
(_, _, WLVL) => FieldType::F32,
(_, _, WNAM) => FieldType::U8ListZip,
(WEAP, _, WPDT) => FieldType::Weapon,
(_, _, WUPD) => FieldType::I32,
(_, _, WWAT) => FieldType::Attributes,
(_, _, WWSK) => FieldType::Skills,
(_, _, XCHG) => FieldType::F32,
(_, _, XHLT) => FieldType::I32,
(_, _, XIDX) => FieldType::I32,
(REFR, _, XNAM) => FieldType::StringZ,
(SPLM, _, XNAM) => FieldType::U8,
(_, _, XSCL) => FieldType::F32,
(_, _, XSOL) => FieldType::StringZ,
(_, _, YEIN) => FieldType::String(None),
(_, _, YETO) => FieldType::String(None),
(REFR, _, YNAM) => FieldType::I32,
(CELL, _, ZNAM) => FieldType::U8,
_ => FieldType::U8List
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Educe)]
#[educe(PartialEq, Eq)]
pub struct Ingredient {
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub weight: f32,
pub value: u32,
#[serde(with="effect_index_option_i32")]
pub effect_1_index: Either<Option<i32>, EffectIndex>,
#[serde(with="effect_index_option_i32")]
pub effect_2_index: Either<Option<i32>, EffectIndex>,
#[serde(with="effect_index_option_i32")]
pub effect_3_index: Either<Option<i32>, EffectIndex>,
#[serde(with="effect_index_option_i32")]
pub effect_4_index: Either<Option<i32>, EffectIndex>,
#[serde(with="skill_option_i32")]
pub effect_1_skill: Either<Option<i32>, Skill>,
#[serde(with="skill_option_i32")]
pub effect_2_skill: Either<Option<i32>, Skill>,
#[serde(with="skill_option_i32")]
pub effect_3_skill: Either<Option<i32>, Skill>,
#[serde(with="skill_option_i32")]
pub effect_4_skill: Either<Option<i32>, Skill>,
#[serde(with="attribute_option_i32")]
pub effect_1_attribute: Either<Option<i32>, Attribute>,
#[serde(with="attribute_option_i32")]
pub effect_2_attribute: Either<Option<i32>, Attribute>,
#[serde(with="attribute_option_i32")]
pub effect_3_attribute: Either<Option<i32>, Attribute>,
#[serde(with="attribute_option_i32")]
pub effect_4_attribute: Either<Option<i32>, Attribute>,
}
pub(crate) fn eq_f32(a: &f32, b: &f32) -> bool {
a.to_bits() == b.to_bits()
}
fn eq_f32_list(a: &[f32], b: &[f32]) -> bool {
if a.len() != b.len() { return false; }
a.iter().zip(b.iter()).all(|(x, y)| eq_f32(x, y))
}
pub(crate) mod float_32 {
use serde::{Serializer, Deserializer, Serialize};
use serde::de::DeserializeSeed;
use serde_serialize_seed::ValueWithSeed;
use crate::serde_helpers::*;
pub fn serialize<S>(v: &f32, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
ValueWithSeed(v, F32AsIsSerde).serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<f32, D::Error> where D: Deserializer<'de> {
F32AsIsSerde.deserialize(deserializer)
}
}
mod option_i8 {
use serde::{Serializer, Deserializer, Deserialize, Serialize};
use serde::ser::Error as ser_Error;
pub fn serialize<S>(&v: &Option<i8>, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
if serializer.is_human_readable() {
v.serialize(serializer)
} else {
let v = if let Some(v) = v {
if v == -1 { return Err(S::Error::custom("-1 is reserved")); }
v
} else {
-1
};
v.serialize(serializer)
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<i8>, D::Error> where D: Deserializer<'de> {
if deserializer.is_human_readable() {
<Option<i8>>::deserialize(deserializer)
} else {
let d = i8::deserialize(deserializer)?;
if d == -1 {
Ok(None)
} else {
Ok(Some(d))
}
}
}
}
mod bool_u32 {
use serde::{Deserializer, Serialize, Serializer};
use serde::de::DeserializeSeed;
use serde_serialize_seed::ValueWithSeed;
use crate::serde_helpers::*;
pub fn serialize<S>(v: &bool, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
ValueWithSeed(v, BoolU32Serde).serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error> where D: Deserializer<'de> {
BoolU32Serde.deserialize(deserializer)
}
}
mod bool_u8 {
use serde::{Deserializer, Serialize, Serializer};
use serde::de::DeserializeSeed;
use serde_serialize_seed::ValueWithSeed;
use crate::serde_helpers::*;
pub fn serialize<S>(v: &bool, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
ValueWithSeed(v, BoolU8Serde).serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error> where D: Deserializer<'de> {
BoolU8Serde.deserialize(deserializer)
}
}
mod bool_either_i16 {
use serde::{Serializer, Deserializer, Deserialize, Serialize};
use serde::de::Error as de_Error;
use either::{Either, Left, Right};
use serde::de::Unexpected;
pub fn serialize<S>(&v: &Either<bool, bool>, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
if serializer.is_human_readable() {
v.serialize(serializer)
} else {
let v: i16 = match v {
Left(false) => -2,
Left(true) => -1,
Right(false) => 0,
Right(true) => 1
};
v.serialize(serializer)
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Either<bool, bool>, D::Error> where D: Deserializer<'de> {
if deserializer.is_human_readable() {
<Either<bool, bool>>::deserialize(deserializer)
} else {
let d = i16::deserialize(deserializer)?;
match d {
0 => Ok(Right(false)),
1 => Ok(Right(true)),
-1 => Ok(Left(true)),
-2 => Ok(Left(false)),
d => Err(D::Error::invalid_value(Unexpected::Signed(d as i64), &"-2, -1, 0, or 1"))
}
}
}
}
#[derive(Educe, Debug, Clone, Serialize, Deserialize)]
#[educe(Eq, PartialEq)]
pub struct CurrentTime {
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub hour: f32,
pub day: u32,
pub month: u32,
pub year: u32
}
#[derive(Educe, Debug, Clone, Serialize, Deserialize)]
#[educe(Eq, PartialEq)]
pub struct Time {
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub hour: f32,
pub day: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct ScriptVars {
pub shorts: u32,
pub longs: u32,
pub floats: u32,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ScriptMetadata {
pub name: String,
pub vars: ScriptVars,
pub data_size: u32,
pub var_table_size: u32
}
const SCRIPT_METADATA_NAME_FIELD: &str = name_of!(name in ScriptMetadata);
const SCRIPT_METADATA_VARS_FIELD: &str = name_of!(vars in ScriptMetadata);
const SCRIPT_METADATA_DATA_SIZE_FIELD: &str = name_of!(data_size in ScriptMetadata);
const SCRIPT_METADATA_VAR_TABLE_SIZE_FIELD: &str = name_of!(var_table_size in ScriptMetadata);
const SCRIPT_METADATA_FIELDS: &[&str] = &[
SCRIPT_METADATA_NAME_FIELD,
SCRIPT_METADATA_VARS_FIELD,
SCRIPT_METADATA_DATA_SIZE_FIELD,
SCRIPT_METADATA_VAR_TABLE_SIZE_FIELD,
];
#[derive(Clone)]
pub struct ScriptMetadataSerde {
pub code_page: Option<CodePage>
}
impl SerializeSeed for ScriptMetadataSerde {
type Value = ScriptMetadata;
fn serialize<S: Serializer>(&self, value: &Self::Value, serializer: S) -> Result<S::Ok, S::Error> {
let mut serializer = serializer.serialize_struct(name_of!(type ScriptMetadata), 4)?;
serializer.serialize_field(
SCRIPT_METADATA_NAME_FIELD,
&ValueWithSeed(value.name.as_str(), StringSerde { code_page: self.code_page, len: Some(32) })
)?;
serializer.serialize_field(SCRIPT_METADATA_VARS_FIELD, &value.vars)?;
serializer.serialize_field(SCRIPT_METADATA_DATA_SIZE_FIELD, &value.data_size)?;
serializer.serialize_field(SCRIPT_METADATA_VAR_TABLE_SIZE_FIELD, &value.var_table_size)?;
serializer.end()
}
}
enum ScriptMetadataField {
Name,
Vars,
DataSize,
VarTableSize
}
struct ScriptMetadataFieldDeVisitor;
impl<'de> de::Visitor<'de> for ScriptMetadataFieldDeVisitor {
type Value = ScriptMetadataField;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "script metadata field")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
match value {
SCRIPT_METADATA_NAME_FIELD => Ok(ScriptMetadataField::Name),
SCRIPT_METADATA_VARS_FIELD => Ok(ScriptMetadataField::Vars),
SCRIPT_METADATA_DATA_SIZE_FIELD => Ok(ScriptMetadataField::DataSize),
SCRIPT_METADATA_VAR_TABLE_SIZE_FIELD => Ok(ScriptMetadataField::VarTableSize),
x => Err(E::unknown_field(x, SCRIPT_METADATA_FIELDS)),
}
}
}
impl<'de> de::Deserialize<'de> for ScriptMetadataField {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_identifier(ScriptMetadataFieldDeVisitor)
}
}
struct ScriptMetadataDeVisitor(ScriptMetadataSerde);
impl<'de> de::Visitor<'de> for ScriptMetadataDeVisitor {
type Value = ScriptMetadata;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "script metadata")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> where A: de::MapAccess<'de> {
let mut name = None;
let mut vars = None;
let mut data_size = None;
let mut var_table_size = None;
while let Some(field) = map.next_key()? {
match field {
ScriptMetadataField::Name =>
if name.replace(map.next_value_seed(StringSerde {
code_page: self.0.code_page, len: Some(32)
})?).is_some() {
return Err(A::Error::duplicate_field(SCRIPT_METADATA_NAME_FIELD));
},
ScriptMetadataField::Vars => if vars.replace(map.next_value()?).is_some() {
return Err(A::Error::duplicate_field(SCRIPT_METADATA_VARS_FIELD));
},
ScriptMetadataField::DataSize => if data_size.replace(map.next_value()?).is_some() {
return Err(A::Error::duplicate_field(SCRIPT_METADATA_DATA_SIZE_FIELD));
},
ScriptMetadataField::VarTableSize => if var_table_size.replace(map.next_value()?).is_some() {
return Err(A::Error::duplicate_field(SCRIPT_METADATA_VAR_TABLE_SIZE_FIELD));
},
}
}
let name = name.ok_or_else(|| A::Error::missing_field(SCRIPT_METADATA_NAME_FIELD))?;
let vars = vars.ok_or_else(|| A::Error::missing_field(SCRIPT_METADATA_VARS_FIELD))?;
let data_size = data_size.ok_or_else(|| A::Error::missing_field(SCRIPT_METADATA_DATA_SIZE_FIELD))?;
let var_table_size = var_table_size.ok_or_else(|| A::Error::missing_field(SCRIPT_METADATA_VAR_TABLE_SIZE_FIELD))?;
Ok(ScriptMetadata { name, vars, data_size, var_table_size })
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> where A: de::SeqAccess<'de> {
let name = seq.next_element_seed(StringSerde { code_page: self.0.code_page, len: Some(32) })?
.ok_or_else(|| A::Error::invalid_length(0, &self))?;
let vars = seq.next_element()?
.ok_or_else(|| A::Error::invalid_length(1, &self))?;
let data_size = seq.next_element()?
.ok_or_else(|| A::Error::invalid_length(2, &self))?;
let var_table_size = seq.next_element()?
.ok_or_else(|| A::Error::invalid_length(3, &self))?;
Ok(ScriptMetadata { name, vars, data_size, var_table_size })
}
}
impl<'de> DeserializeSeed<'de> for ScriptMetadataSerde {
type Value = ScriptMetadata;
fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
deserializer.deserialize_struct(name_of!(type ScriptMetadata), SCRIPT_METADATA_FIELDS, ScriptMetadataDeVisitor(self))
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct FileMetadata {
pub version: u32,
pub file_type: FileType,
pub author: String,
pub description: Vec<String>,
pub records: u32
}
const FILE_METADATA_VERSION_FIELD: &str = name_of!(version in FileMetadata);
const FILE_METADATA_TYPE_FIELD: &str = "type";
const FILE_METADATA_AUTHOR_FIELD: &str = name_of!(author in FileMetadata);
const FILE_METADATA_DESCRIPTION_FIELD: &str = name_of!(description in FileMetadata);
const FILE_METADATA_RECORDS_FIELD: &str = name_of!(records in FileMetadata);
const FILE_METADATA_FIELDS: &[&str] = &[
FILE_METADATA_VERSION_FIELD,
FILE_METADATA_TYPE_FIELD,
FILE_METADATA_AUTHOR_FIELD,
FILE_METADATA_DESCRIPTION_FIELD,
FILE_METADATA_RECORDS_FIELD,
];
#[derive(Clone)]
pub struct FileMetadataSerde {
pub code_page: Option<CodePage>
}
impl SerializeSeed for FileMetadataSerde {
type Value = FileMetadata;
fn serialize<S: Serializer>(&self, value: &Self::Value, serializer: S) -> Result<S::Ok, S::Error> {
let mut serializer = serializer.serialize_struct(name_of!(type FileMetadata), 5)?;
serializer.serialize_field(FILE_METADATA_VERSION_FIELD, &value.version)?;
serializer.serialize_field(FILE_METADATA_TYPE_FIELD, &value.file_type)?;
serializer.serialize_field(
FILE_METADATA_AUTHOR_FIELD,
&ValueWithSeed(value.author.as_str(), StringSerde { code_page: self.code_page, len: Some(32) })
)?;
serializer.serialize_field(
FILE_METADATA_DESCRIPTION_FIELD,
&ValueWithSeed(&value.description[..], StringListSerde {
code_page: self.code_page, separator: Newline::Dos.as_str(), len: Some(256)
})
)?;
serializer.serialize_field(FILE_METADATA_RECORDS_FIELD, &value.records)?;
serializer.end()
}
}
enum FileMetadataField {
Version,
Type,
Author,
Description,
Records
}
struct FileMetadataFieldDeVisitor;
impl<'de> de::Visitor<'de> for FileMetadataFieldDeVisitor {
type Value = FileMetadataField;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "file metadata field")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
match value {
FILE_METADATA_VERSION_FIELD => Ok(FileMetadataField::Version),
FILE_METADATA_TYPE_FIELD => Ok(FileMetadataField::Type),
FILE_METADATA_AUTHOR_FIELD => Ok(FileMetadataField::Author),
FILE_METADATA_DESCRIPTION_FIELD => Ok(FileMetadataField::Description),
FILE_METADATA_RECORDS_FIELD => Ok(FileMetadataField::Records),
x => Err(E::unknown_field(x, FILE_METADATA_FIELDS)),
}
}
}
impl<'de> de::Deserialize<'de> for FileMetadataField {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_identifier(FileMetadataFieldDeVisitor)
}
}
struct FileMetadataDeVisitor(FileMetadataSerde);
impl<'de> de::Visitor<'de> for FileMetadataDeVisitor {
type Value = FileMetadata;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "file metadata")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> where A: de::MapAccess<'de> {
let mut version = None;
let mut file_type = None;
let mut author = None;
let mut description = None;
let mut records = None;
while let Some(field) = map.next_key()? {
match field {
FileMetadataField::Version => if version.replace(map.next_value()?).is_some() {
return Err(A::Error::duplicate_field(FILE_METADATA_VERSION_FIELD));
},
FileMetadataField::Type => if file_type.replace(map.next_value()?).is_some() {
return Err(A::Error::duplicate_field(FILE_METADATA_TYPE_FIELD));
},
FileMetadataField::Author =>
if author.replace(map.next_value_seed(StringSerde {
code_page: self.0.code_page, len: Some(32)
})?).is_some() {
return Err(A::Error::duplicate_field(FILE_METADATA_AUTHOR_FIELD));
},
FileMetadataField::Description =>
if description.replace(
map.next_value_seed(StringListSerde {
code_page: self.0.code_page, separator: Newline::Dos.as_str(), len: Some(256)
})?
).is_some() {
return Err(A::Error::duplicate_field(FILE_METADATA_DESCRIPTION_FIELD));
},
FileMetadataField::Records => if records.replace(map.next_value()?).is_some() {
return Err(A::Error::duplicate_field(FILE_METADATA_RECORDS_FIELD));
},
}
}
let version = version.ok_or_else(|| A::Error::missing_field(FILE_METADATA_VERSION_FIELD))?;
let file_type = file_type.ok_or_else(|| A::Error::missing_field(FILE_METADATA_TYPE_FIELD))?;
let author = author.ok_or_else(|| A::Error::missing_field(FILE_METADATA_AUTHOR_FIELD))?;
let description = description.ok_or_else(|| A::Error::missing_field(FILE_METADATA_DESCRIPTION_FIELD))?;
let records = records.ok_or_else(|| A::Error::missing_field(FILE_METADATA_RECORDS_FIELD))?;
Ok(FileMetadata { version, file_type, author, description, records })
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> where A: de::SeqAccess<'de> {
let version = seq.next_element()?
.ok_or_else(|| A::Error::invalid_length(0, &self))?;
let file_type = seq.next_element()?
.ok_or_else(|| A::Error::invalid_length(1, &self))?;
let author = seq.next_element_seed(StringSerde { code_page: self.0.code_page, len: Some(32) })?
.ok_or_else(|| A::Error::invalid_length(2, &self))?;
let description = seq.next_element_seed(StringListSerde {
code_page: self.0.code_page, separator: Newline::Dos.as_str(), len: Some(256)
})?.ok_or_else(|| A::Error::invalid_length(3, &self))?;
let records = seq.next_element()?
.ok_or_else(|| A::Error::invalid_length(4, &self))?;
Ok(FileMetadata { version, file_type, author, description, records })
}
}
impl<'de> DeserializeSeed<'de> for FileMetadataSerde {
type Value = FileMetadata;
fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
deserializer.deserialize_struct(name_of!(type FileMetadata), FILE_METADATA_FIELDS, FileMetadataDeVisitor(self))
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Effect {
#[serde(with="effect_index_option_i16")]
pub index: Either<Option<i16>, EffectIndex>,
#[serde(with="skill_option_i8")]
pub skill: Either<Option<i8>, Skill>,
#[serde(with="attribute_option_i8")]
pub attribute: Either<Option<i8>, Attribute>,
pub range: EffectRange,
pub area: i32,
pub duration: i32,
pub magnitude_min: i32,
pub magnitude_max: i32
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct NpcState {
pub disposition: i16,
pub reputation: i16,
pub index: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct NpcStats {
pub attributes: Attributes<u8>,
pub skills: Skills<u8>,
pub faction: u8,
pub health: i16,
pub magicka: i16,
pub fatigue: i16,
}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
#[serde(rename="NpcStatsOption")]
enum NpcStatsOptionHRSurrogate {
None(u16),
Some(NpcStats)
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Npc {
pub level: u16,
pub disposition: i8,
pub reputation: i8,
pub rank: i8,
pub gold: i32,
pub padding: u8,
pub stats: Either<u16, NpcStats>
}
#[derive(Serialize, Deserialize)]
struct NpcHRSurrogate {
pub level: u16,
pub disposition: i8,
pub reputation: i8,
pub rank: i8,
pub gold: i32,
pub padding: u8,
pub stats: NpcStatsOptionHRSurrogate
}
impl From<Npc> for NpcHRSurrogate {
fn from(t: Npc) -> Self {
NpcHRSurrogate {
level: t.level, disposition: t.disposition, reputation:t.reputation,
rank: t.rank, gold: t.gold, padding: t.padding,
stats: t.stats.either(NpcStatsOptionHRSurrogate::None, NpcStatsOptionHRSurrogate::Some)
}
}
}
impl From<NpcHRSurrogate> for Npc {
fn from(t: NpcHRSurrogate) -> Self {
let stats = match t.stats {
NpcStatsOptionHRSurrogate::None(x) => Left(x),
NpcStatsOptionHRSurrogate::Some(x) => Right(x)
};
Npc {
level: t.level, disposition: t.disposition, reputation:t.reputation,
rank: t.rank, gold: t.gold, padding: t.padding,
stats
}
}
}
impl Serialize for Npc {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
if serializer.is_human_readable() {
NpcHRSurrogate::from(self.clone()).serialize(serializer)
} else {
let surrogate: Either<NpcNHRSurrogate12, NpcNHRSurrogate52> = self.clone().into();
match surrogate {
Left(npc12) => serializer.serialize_newtype_variant(
name_of!(type Npc), 12, "Npc12", &npc12
),
Right(npc52) => serializer.serialize_newtype_variant(
name_of!(type Npc), 52, "Npc52", &npc52
),
}
}
}
}
struct NpcNHRDeserializer;
impl<'de> de::Visitor<'de> for NpcNHRDeserializer {
type Value = Npc;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "NPC")
}
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 {
12 => Ok(variant.newtype_variant::<NpcNHRSurrogate12>()?.into()),
52 => Ok(variant.newtype_variant::<NpcNHRSurrogate52>()?.into()),
n => Err(A::Error::invalid_value(Unexpected::Unsigned(n as u64), &self))
}
}
}
impl<'de> Deserialize<'de> for Npc {
fn deserialize<D>(deserializer: D) -> Result<Npc, D::Error> where D: Deserializer<'de> {
if deserializer.is_human_readable() {
NpcHRSurrogate::deserialize(deserializer).map(Npc::from)
} else {
deserializer.deserialize_enum(name_of!(type Npc), &["Npc12", "Npc52"], NpcNHRDeserializer)
}
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename="Npc12")]
struct NpcNHRSurrogate12 {
pub level: u16,
pub disposition: i8,
pub reputation: i8,
pub rank: i8,
pub padding_8: u8,
pub padding_16: u16,
pub gold: i32,
}
#[derive(Serialize, Deserialize)]
#[serde(rename="Npc52")]
struct NpcNHRSurrogate52 {
pub level: u16,
pub stats: NpcStats,
pub disposition: i8,
pub reputation: i8,
pub rank: i8,
pub padding: u8,
pub gold: i32,
}
impl From<Npc> for Either<NpcNHRSurrogate12, NpcNHRSurrogate52> {
fn from(npc: Npc) -> Either<NpcNHRSurrogate12, NpcNHRSurrogate52> {
match npc.stats {
Right(stats) => Right(NpcNHRSurrogate52 {
level: npc.level, disposition: npc.disposition,
reputation: npc.reputation, rank: npc.rank,
padding: npc.padding,
gold: npc.gold,
stats
}),
Left(padding_16) => Left(NpcNHRSurrogate12 {
level: npc.level, disposition: npc.disposition,
reputation: npc.reputation, rank: npc.rank,
padding_8: npc.padding, padding_16,
gold: npc.gold
})
}
}
}
impl From<NpcNHRSurrogate52> for Npc {
fn from(npc: NpcNHRSurrogate52) -> Npc {
Npc {
level: npc.level, disposition: npc.disposition, reputation: npc.reputation,
rank: npc.rank, gold: npc.gold, padding: npc.padding,
stats: Right(npc.stats)
}
}
}
impl From<NpcNHRSurrogate12> for Npc {
fn from(npc: NpcNHRSurrogate12) -> Npc {
Npc {
level: npc.level, disposition: npc.disposition, reputation: npc.reputation,
rank: npc.rank, gold: npc.gold, padding: npc.padding_8,
stats: Left(npc.padding_16)
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Item {
pub count: i32,
pub item_id: String,
}
const ITEM_COUNT_FIELD: &str = name_of!(count in Item);
const ITEM_ITEM_ID_FIELD: &str = name_of!(item_id in Item);
const ITEM_FIELDS: &[&str] = &[
ITEM_COUNT_FIELD,
ITEM_ITEM_ID_FIELD,
];
#[derive(Clone)]
pub struct ItemSerde {
pub code_page: Option<CodePage>
}
impl SerializeSeed for ItemSerde {
type Value = Item;
fn serialize<S: Serializer>(&self, value: &Self::Value, serializer: S) -> Result<S::Ok, S::Error> {
let mut serializer = serializer.serialize_struct(name_of!(type Item), 2)?;
serializer.serialize_field(ITEM_COUNT_FIELD, &value.count)?;
serializer.serialize_field(
ITEM_ITEM_ID_FIELD,
&ValueWithSeed(value.item_id.as_str(), StringSerde { code_page: self.code_page, len: Some(32) })
)?;
serializer.end()
}
}
enum ItemField {
Count,
ItemId,
}
struct ItemFieldDeVisitor;
impl<'de> de::Visitor<'de> for ItemFieldDeVisitor {
type Value = ItemField;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "item field")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
match value {
ITEM_COUNT_FIELD => Ok(ItemField::Count),
ITEM_ITEM_ID_FIELD => Ok(ItemField::ItemId),
x => Err(E::unknown_field(x, ITEM_FIELDS)),
}
}
}
impl<'de> de::Deserialize<'de> for ItemField {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_identifier(ItemFieldDeVisitor)
}
}
struct ItemDeVisitor(ItemSerde);
impl<'de> de::Visitor<'de> for ItemDeVisitor {
type Value = Item;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "item")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> where A: de::MapAccess<'de> {
let mut count = None;
let mut item_id = None;
while let Some(field) = map.next_key()? {
match field {
ItemField::Count => if count.replace(map.next_value()?).is_some() {
return Err(A::Error::duplicate_field(ITEM_COUNT_FIELD));
},
ItemField::ItemId =>
if item_id.replace(map.next_value_seed(StringSerde {
code_page: self.0.code_page, len: Some(32)
})?).is_some() {
return Err(A::Error::duplicate_field(ITEM_ITEM_ID_FIELD));
},
}
}
let count = count.ok_or_else(|| A::Error::missing_field(ITEM_COUNT_FIELD))?;
let item_id = item_id.ok_or_else(|| A::Error::missing_field(ITEM_ITEM_ID_FIELD))?;
Ok(Item { count, item_id })
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> where A: de::SeqAccess<'de> {
let count = seq.next_element()?
.ok_or_else(|| A::Error::invalid_length(0, &self))?;
let item_id = seq.next_element_seed(StringSerde { code_page: self.0.code_page, len: Some(32) })?
.ok_or_else(|| A::Error::invalid_length(1, &self))?;
Ok(Item { count, item_id })
}
}
impl<'de> DeserializeSeed<'de> for ItemSerde {
type Value = Item;
fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
deserializer.deserialize_struct(name_of!(type Item), ITEM_FIELDS, ItemDeVisitor(self))
}
}
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u32)]
pub enum SpellType {
Spell = 0,
Ability = 1,
Blight = 2,
Disease = 3,
Curse = 4,
Power = 5
}
}
enum_serde!(SpellType, "spell type", as u32, Unsigned, u64);
bitflags_ext! {
pub struct SpellFlags: u32 {
AUTO_CALCULATE_COST = 1,
PC_START = 2,
ALWAYS_SUCCEEDS = 4,
}
}
enum_serde!(SpellFlags, "spell flags", u32, bits(), try from_bits, Unsigned, u64);
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Spell {
#[serde(rename="type")]
pub spell_type: SpellType,
pub cost: u32,
pub flags: SpellFlags
}
bitflags_ext! {
pub struct Services: u32 {
WEAPON = 0x00000001,
ARMOR = 0x00000002,
CLOTHING = 0x00000004,
BOOKS = 0x00000008,
INGREDIENTS = 0x00000010,
PICKS = 0x00000020,
PROBES = 0x00000040,
LIGHTS = 0x00000080,
APPARATUS = 0x00000100,
REPAIR_ITEMS = 0x00000200,
MISCELLANEOUS = 0x00000400,
SPELLS = 0x00000800,
MAGIC_ITEMS = 0x00001000,
POTIONS = 0x00002000,
TRAINING = 0x00004000,
SPELLMAKING = 0x00008000,
ENCHANTING = 0x00010000,
REPAIR = 0x00020000,
_40000 = 0x40000,
_80000 = 0x00080000,
_100000 = 0x100000,
_200000 = 0x00200000,
_400000 = 0x00400000,
_800000 = 0x00800000,
_1000000 = 0x01000000,
_2000000 = 0x2000000,
_4000000 = 0x4000000,
_10000000 = 0x10000000,
_20000000 = 0x20000000,
_40000000 = 0x40000000,
_80000000 = 0x80000000,
}
}
enum_serde!(Services, "services", u32, bits(), try from_bits, Unsigned, u64);
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Ai {
pub hello: u16,
pub fight: u8,
pub flee: u8,
pub alarm: u8,
pub padding_8: u8,
pub padding_16: u16,
pub services: Services
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct AiWander {
pub distance: u16,
pub duration: u16,
pub time_of_day: u8,
pub idle: [u8; 8],
#[serde(with="bool_u8")]
pub repeat: bool,
}
bitflags_ext! {
pub struct AiTravelFlags: u32 {
RESET = 0x000100,
_1 = 0x000001,
_800 = 0x000800,
_1000 = 0x001000,
_4000 = 0x004000,
_10000 = 0x010000,
_20000 = 0x020000,
_40000 = 0x040000,
_400000 = 0x400000
}
}
enum_serde!(AiTravelFlags, "AI travel flags", u32, bits(), try from_bits, Unsigned, u64);
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct AiTravel {
pub pos: Pos,
pub flags: AiTravelFlags
}
bitflags_ext! {
pub struct AiTargetFlags: u8 {
_1 = 0x01,
_2 = 0x02,
_4 = 0x04,
_8 = 0x08
}
}
enum_serde!(AiTargetFlags, "AI target flags", u8, bits(), try from_bits, Unsigned, u64);
#[derive(Debug, Clone, Educe)]
#[educe(Eq, PartialEq)]
pub struct AiTarget {
pub pos: Pos,
pub duration: u16,
pub actor_id: String,
pub reset: bool,
pub flags: AiTargetFlags
}
const AI_TARGET_POS_FIELD: &str = name_of!(pos in AiTarget);
const AI_TARGET_DURATION_FIELD: &str = name_of!(duration in AiTarget);
const AI_TARGET_ACTOR_ID_FIELD: &str = name_of!(actor_id in AiTarget);
const AI_TARGET_RESET_FIELD: &str = name_of!(reset in AiTarget);
const AI_TARGET_FLAGS_FIELD: &str = name_of!(flags in AiTarget);
const AI_TARGET_FIELDS: &[&str] = &[
AI_TARGET_POS_FIELD,
AI_TARGET_DURATION_FIELD,
AI_TARGET_ACTOR_ID_FIELD,
AI_TARGET_RESET_FIELD,
AI_TARGET_FLAGS_FIELD,
];
#[derive(Clone)]
pub struct AiTargetSerde {
pub code_page: Option<CodePage>
}
impl SerializeSeed for AiTargetSerde {
type Value = AiTarget;
fn serialize<S: Serializer>(&self, value: &Self::Value, serializer: S) -> Result<S::Ok, S::Error> {
let mut serializer = serializer.serialize_struct(name_of!(type AiTarget), 5)?;
serializer.serialize_field(AI_TARGET_POS_FIELD, &value.pos)?;
serializer.serialize_field(AI_TARGET_DURATION_FIELD, &value.duration)?;
serializer.serialize_field(
AI_TARGET_ACTOR_ID_FIELD,
&ValueWithSeed(value.actor_id.as_str(), StringSerde { code_page: self.code_page, len: Some(32) })
)?;
serializer.serialize_field(AI_TARGET_RESET_FIELD, &ValueWithSeed(&value.reset, BoolU8Serde))?;
serializer.serialize_field(AI_TARGET_FLAGS_FIELD, &value.flags)?;
serializer.end()
}
}
enum AiTargetField {
Pos,
Duration,
ActorId,
Reset,
Flags
}
struct AiTargetFieldDeVisitor;
impl<'de> de::Visitor<'de> for AiTargetFieldDeVisitor {
type Value = AiTargetField;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "AI target field")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
match value {
AI_TARGET_POS_FIELD => Ok(AiTargetField::Pos),
AI_TARGET_DURATION_FIELD => Ok(AiTargetField::Duration),
AI_TARGET_ACTOR_ID_FIELD => Ok(AiTargetField::ActorId),
AI_TARGET_RESET_FIELD => Ok(AiTargetField::Reset),
AI_TARGET_FLAGS_FIELD => Ok(AiTargetField::Flags),
x => Err(E::unknown_field(x, AI_TARGET_FIELDS)),
}
}
}
impl<'de> de::Deserialize<'de> for AiTargetField {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_identifier(AiTargetFieldDeVisitor)
}
}
struct AiTargetDeVisitor(AiTargetSerde);
impl<'de> de::Visitor<'de> for AiTargetDeVisitor {
type Value = AiTarget;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "AI target")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> where A: de::MapAccess<'de> {
let mut pos = None;
let mut duration = None;
let mut actor_id = None;
let mut reset = None;
let mut flags = None;
while let Some(field) = map.next_key()? {
match field {
AiTargetField::Pos => if pos.replace(map.next_value()?).is_some() {
return Err(A::Error::duplicate_field(AI_TARGET_POS_FIELD));
},
AiTargetField::Duration => if duration.replace(map.next_value()?).is_some() {
return Err(A::Error::duplicate_field(AI_TARGET_DURATION_FIELD));
},
AiTargetField::ActorId =>
if actor_id.replace(map.next_value_seed(
StringSerde { code_page: self.0.code_page, len: Some(32) }
)?).is_some() {
return Err(A::Error::duplicate_field(AI_TARGET_ACTOR_ID_FIELD));
},
AiTargetField::Reset => if reset.replace(map.next_value_seed(BoolU8Serde)?).is_some() {
return Err(A::Error::duplicate_field(AI_TARGET_RESET_FIELD));
},
AiTargetField::Flags => if flags.replace(map.next_value()?).is_some() {
return Err(A::Error::duplicate_field(AI_TARGET_FLAGS_FIELD));
},
}
}
let pos = pos.ok_or_else(|| A::Error::missing_field(AI_TARGET_POS_FIELD))?;
let duration = duration.ok_or_else(|| A::Error::missing_field(AI_TARGET_DURATION_FIELD))?;
let actor_id = actor_id.ok_or_else(|| A::Error::missing_field(AI_TARGET_ACTOR_ID_FIELD))?;
let reset = reset.ok_or_else(|| A::Error::missing_field(AI_TARGET_RESET_FIELD))?;
let flags = flags.ok_or_else(|| A::Error::missing_field(AI_TARGET_FLAGS_FIELD))?;
Ok(AiTarget { pos, duration, actor_id, reset, flags })
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> where A: de::SeqAccess<'de> {
let pos = seq.next_element()?
.ok_or_else(|| A::Error::invalid_length(0, &self))?;
let duration = seq.next_element()?
.ok_or_else(|| A::Error::invalid_length(1, &self))?;
let actor_id = seq.next_element_seed(StringSerde { code_page: self.0.code_page, len: Some(32) })?
.ok_or_else(|| A::Error::invalid_length(2, &self))?;
let reset = seq.next_element_seed(BoolU8Serde)?
.ok_or_else(|| A::Error::invalid_length(3, &self))?;
let flags = seq.next_element()?
.ok_or_else(|| A::Error::invalid_length(4, &self))?;
Ok(AiTarget { pos, duration, actor_id, reset, flags })
}
}
impl<'de> DeserializeSeed<'de> for AiTargetSerde {
type Value = AiTarget;
fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
deserializer.deserialize_struct(name_of!(type AiTarget), AI_TARGET_FIELDS, AiTargetDeVisitor(self))
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct AiActivate {
pub object_id: String,
pub reset: bool
}
const AI_ACTIVATE_OBJECT_ID_FIELD: &str = name_of!(object_id in AiActivate);
const AI_ACTIVATE_RESET_FIELD: &str = name_of!(reset in AiActivate);
const AI_ACTIVATE_FIELDS: &[&str] = &[
AI_ACTIVATE_OBJECT_ID_FIELD,
AI_ACTIVATE_RESET_FIELD,
];
#[derive(Clone)]
pub struct AiActivateSerde {
pub code_page: Option<CodePage>
}
impl SerializeSeed for AiActivateSerde {
type Value = AiActivate;
fn serialize<S: Serializer>(&self, value: &Self::Value, serializer: S) -> Result<S::Ok, S::Error> {
let mut serializer = serializer.serialize_struct(name_of!(type AiActivate), 2)?;
serializer.serialize_field(
AI_ACTIVATE_OBJECT_ID_FIELD,
&ValueWithSeed(value.object_id.as_str(), StringSerde { code_page: self.code_page, len: Some(32) })
)?;
serializer.serialize_field(AI_ACTIVATE_RESET_FIELD, &ValueWithSeed(&value.reset, BoolU8Serde))?;
serializer.end()
}
}
enum AiActivateField {
ObjectId,
Reset,
}
struct AiActivateFieldDeVisitor;
impl<'de> de::Visitor<'de> for AiActivateFieldDeVisitor {
type Value = AiActivateField;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "AI activate field")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
match value {
AI_ACTIVATE_OBJECT_ID_FIELD => Ok(AiActivateField::ObjectId),
AI_ACTIVATE_RESET_FIELD => Ok(AiActivateField::Reset),
x => Err(E::unknown_field(x, AI_ACTIVATE_FIELDS)),
}
}
}
impl<'de> de::Deserialize<'de> for AiActivateField {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_identifier(AiActivateFieldDeVisitor)
}
}
struct AiActivateDeVisitor(AiActivateSerde);
impl<'de> de::Visitor<'de> for AiActivateDeVisitor {
type Value = AiActivate;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "AI activate")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> where A: de::MapAccess<'de> {
let mut object_id = None;
let mut reset = None;
while let Some(field) = map.next_key()? {
match field {
AiActivateField::ObjectId =>
if object_id.replace(map.next_value_seed(
StringSerde { code_page: self.0.code_page, len: Some(32) }
)?).is_some() {
return Err(A::Error::duplicate_field(AI_ACTIVATE_OBJECT_ID_FIELD));
},
AiActivateField::Reset => if reset.replace(map.next_value_seed(BoolU8Serde)?).is_some() {
return Err(A::Error::duplicate_field(AI_ACTIVATE_RESET_FIELD));
},
}
}
let object_id = object_id.ok_or_else(|| A::Error::missing_field(AI_ACTIVATE_OBJECT_ID_FIELD))?;
let reset = reset.ok_or_else(|| A::Error::missing_field(AI_ACTIVATE_RESET_FIELD))?;
Ok(AiActivate { object_id, reset })
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> where A: de::SeqAccess<'de> {
let object_id = seq.next_element_seed(StringSerde { code_page: self.0.code_page, len: Some(32) })?
.ok_or_else(|| A::Error::invalid_length(0, &self))?;
let reset = seq.next_element_seed(BoolU8Serde)?
.ok_or_else(|| A::Error::invalid_length(1, &self))?;
Ok(AiActivate { object_id, reset })
}
}
impl<'de> DeserializeSeed<'de> for AiActivateSerde {
type Value = AiActivate;
fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
deserializer.deserialize_struct(name_of!(type AiActivate), AI_ACTIVATE_FIELDS, AiActivateDeVisitor(self))
}
}
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u8)]
pub enum Blood {
Default = 0,
Skeleton = 4,
MetalSparks = 8,
}
}
enum_serde!(Blood, "blood", as u8, Unsigned, u64);
bitflags_ext! {
pub struct NpcFlags: u8 {
FEMALE = 0x01,
ESSENTIAL = 0x02,
RESPAWN = 0x04,
_8 = 0x08,
AUTO_CALCULATE_STATS = 0x10
}
}
enum_serde!(NpcFlags, "NPC flags", u8, bits(), try from_bits, Unsigned, u64, ^0x08);
bitflags_ext! {
pub struct CreatureFlags: u8 {
BIPED = 0x01,
RESPAWN = 0x02,
WEAPON_AND_SHIELD = 0x04,
SWIMS = 0x10,
FLIES = 0x20,
WALKS = 0x40,
ESSENTIAL = 0x80
}
}
enum_serde!(CreatureFlags, "creature flags", u8, bits(), try from_bits, Unsigned, u64, ^0x08);
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct FlagsAndBlood<Flags> {
pub flags: Flags,
pub blood: Blood,
pub padding: u16,
}
bitflags_ext! {
pub struct BookFlags: u32 {
SCROLL = 0x01,
_10 = 0x10
}
}
enum_serde!(BookFlags, "book flags", u32, bits(), try from_bits, Unsigned, u64);
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct Book {
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub weight: f32,
pub value: u32,
pub flags: BookFlags,
#[serde(with="skill_option_i32")]
pub skill: Either<Option<i32>, Skill>,
pub enchantment: u32
}
bitflags_ext! {
pub struct ContainerFlags: u32 {
ORGANIC = 0x01,
RESPAWN = 0x02
}
}
enum_serde!(ContainerFlags, "container flags", u32, bits(), try from_bits, Unsigned, u64, ^0x08);
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u32)]
pub enum CreatureType {
Creature = 0,
Daedra = 1,
Undead = 2,
Humanoid = 3
}
}
enum_serde!(CreatureType, "creature type", as u32, Unsigned, u64);
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Creature {
#[serde(rename="type")]
pub creature_type: CreatureType,
pub level: u32,
pub attributes: Attributes<u32>,
pub health: u32,
pub magicka: u32,
pub fatigue: u32,
pub soul: u32,
pub combat: u32,
pub magic: u32,
pub stealth: u32,
pub attack_1_min: u32,
pub attack_1_max: u32,
pub attack_2_min: u32,
pub attack_2_max: u32,
pub attack_3_min: u32,
pub attack_3_max: u32,
pub gold: u32,
}
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u32)]
pub enum Attribute {
Strength = 0,
Intelligence = 1,
Willpower = 2,
Agility = 3,
Speed = 4,
Endurance = 5,
Personality = 6,
Luck = 7,
}
}
enum_serde!(Attribute, "attribute", as u32, Unsigned, u64);
mod attribute_option_i8 {
use either::{Either};
use crate::field::Attribute;
use crate::serde_helpers::*;
use serde::{Deserializer, Serialize, Serializer};
use serde::de::DeserializeSeed;
use serde_serialize_seed::ValueWithSeed;
use std::convert::TryInto;
pub fn serialize<S>(v: &Either<Option<i8>, Attribute>, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
ValueWithSeed(v, OptionIndexSerde {
name: "attribute",
none: -1,
from: |x| x.try_into().ok().and_then(Attribute::n),
into: |x| (x as u32).try_into().unwrap()
}).serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Either<Option<i8>, Attribute>, D::Error> where D: Deserializer<'de> {
OptionIndexSerde {
name: "attribute",
none: -1,
from: |x| x.try_into().ok().and_then(Attribute::n),
into: |x| (x as u32).try_into().unwrap()
}.deserialize(deserializer)
}
}
mod attribute_option_i32 {
use either::{Either};
use crate::field::Attribute;
use crate::serde_helpers::*;
use serde::{Deserializer, Serialize, Serializer};
use serde::de::DeserializeSeed;
use serde_serialize_seed::ValueWithSeed;
use std::convert::TryInto;
pub fn serialize<S>(v: &Either<Option<i32>, Attribute>, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
ValueWithSeed(v, OptionIndexSerde {
name: "attribute",
none: -1,
from: |x| x.try_into().ok().and_then(Attribute::n),
into: |x| (x as u32).try_into().unwrap()
}).serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Either<Option<i32>, Attribute>, D::Error> where D: Deserializer<'de> {
OptionIndexSerde {
name: "attribute",
none: -1,
from: |x| x.try_into().ok().and_then(Attribute::n),
into: |x| (x as u32).try_into().unwrap()
}.deserialize(deserializer)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Attributes<T> {
pub strength: T,
pub intelligence: T,
pub willpower: T,
pub agility: T,
pub speed: T,
pub endurance: T,
pub personality: T,
pub luck: T,
}
impl<T> Index<Attribute> for Attributes<T> {
type Output = T;
fn index(&self, index: Attribute) -> &Self::Output {
match index {
Attribute::Strength => &self.strength,
Attribute::Intelligence => &self.intelligence,
Attribute::Willpower => &self.willpower,
Attribute::Agility => &self.agility,
Attribute::Speed => &self.speed,
Attribute::Endurance => &self.endurance,
Attribute::Personality => &self.personality,
Attribute::Luck => &self.luck,
}
}
}
impl<T> IndexMut<Attribute> for Attributes<T> {
fn index_mut(&mut self, index: Attribute) -> &mut Self::Output {
match index {
Attribute::Strength => &mut self.strength,
Attribute::Intelligence => &mut self.intelligence,
Attribute::Willpower => &mut self.willpower,
Attribute::Agility => &mut self.agility,
Attribute::Speed => &mut self.speed,
Attribute::Endurance => &mut self.endurance,
Attribute::Personality => &mut self.personality,
Attribute::Luck => &mut self.luck,
}
}
}
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
pub struct EffectArg {
pub dword: u32
}
impl EffectArg {
pub const STRENGTH_BLOCK_00000000: EffectArg = EffectArg { dword: 0x00000000 };
pub const ARMORER_INTELLIGENCE_00000001: EffectArg = EffectArg { dword: 0x00000001 };
pub const MEDIUM_ARMOR_WILLPOWER_00000002: EffectArg = EffectArg { dword: 0x00000002 };
pub const HEAVY_ARMOR_AGILITY_00000003: EffectArg = EffectArg { dword: 0x00000003 };
pub const BLUNT_WEAPON_SPEED_00000004: EffectArg = EffectArg { dword: 0x00000004 };
pub const LONG_BLADE_ENDURANCE_00000005: EffectArg = EffectArg { dword: 0x00000005 };
pub const AXE_PERSONALITY_00000006: EffectArg = EffectArg { dword: 0x00000006 };
pub const SPEAR_LUCK_00000007: EffectArg = EffectArg { dword: 0x00000007 };
pub const ATHLETICS_00000008: EffectArg = EffectArg { dword: 0x00000008 };
pub const ENCHANT_00000009: EffectArg = EffectArg { dword: 0x00000009 };
pub const DESTRUCTION_0000000A: EffectArg = EffectArg { dword: 0x0000000A };
pub const ALTERATION_0000000B: EffectArg = EffectArg { dword: 0x0000000B };
pub const ILLUSION_0000000C: EffectArg = EffectArg { dword: 0x0000000C };
pub const CONJURATION_0000000D: EffectArg = EffectArg { dword: 0x0000000D };
pub const MYSTICISM_0000000E: EffectArg = EffectArg { dword: 0x0000000E };
pub const RESTORATION_0000000F: EffectArg = EffectArg { dword: 0x0000000F };
pub const ALCHEMY_00000010: EffectArg = EffectArg { dword: 0x00000010 };
pub const UNARMORED_00000011: EffectArg = EffectArg { dword: 0x00000011 };
pub const SECURITY_00000012: EffectArg = EffectArg { dword: 0x00000012 };
pub const SNEAK_00000013: EffectArg = EffectArg { dword: 0x00000013 };
pub const ACROBATICS_00000014: EffectArg = EffectArg { dword: 0x00000014 };
pub const LIGHT_ARMOR_00000015: EffectArg = EffectArg { dword: 0x00000015 };
pub const SHORT_BLADE_00000016: EffectArg = EffectArg { dword: 0x00000016 };
pub const MARKSMAN_00000017: EffectArg = EffectArg { dword: 0x00000017 };
pub const MERCANTILE_00000018: EffectArg = EffectArg { dword: 0x00000018 };
pub const SPEECHCRAFT_00000019: EffectArg = EffectArg { dword: 0x00000019 };
pub const HAND_TO_HAND_0000001A: EffectArg = EffectArg { dword: 0x0000001A };
}
impl From<u32> for EffectArg {
fn from(dword: u32) -> EffectArg {
EffectArg { dword }
}
}
impl Debug for EffectArg {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl Display for EffectArg {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self {
EffectArg::STRENGTH_BLOCK_00000000 => write!(f, "STRENGTH_BLOCK_00000000"),
EffectArg::ARMORER_INTELLIGENCE_00000001 => write!(f, "ARMORER_INTELLIGENCE_00000001"),
EffectArg::MEDIUM_ARMOR_WILLPOWER_00000002 => write!(f, "MEDIUM_ARMOR_WILLPOWER_00000002"),
EffectArg::HEAVY_ARMOR_AGILITY_00000003 => write!(f, "HEAVY_ARMOR_AGILITY_00000003"),
EffectArg::BLUNT_WEAPON_SPEED_00000004 => write!(f, "BLUNT_WEAPON_SPEED_00000004"),
EffectArg::LONG_BLADE_ENDURANCE_00000005 => write!(f, "LONG_BLADE_ENDURANCE_00000005"),
EffectArg::AXE_PERSONALITY_00000006 => write!(f, "AXE_PERSONALITY_00000006"),
EffectArg::SPEAR_LUCK_00000007 => write!(f, "SPEAR_LUCK_00000007"),
EffectArg::ATHLETICS_00000008 => write!(f, "ATHLETICS_00000008"),
EffectArg::ENCHANT_00000009 => write!(f, "ENCHANT_00000009"),
EffectArg::DESTRUCTION_0000000A => write!(f, "DESTRUCTION_0000000A"),
EffectArg::ALTERATION_0000000B => write!(f, "ALTERATION_0000000B"),
EffectArg::ILLUSION_0000000C => write!(f, "ILLUSION_0000000C"),
EffectArg::CONJURATION_0000000D => write!(f, "CONJURATION_0000000D"),
EffectArg::MYSTICISM_0000000E => write!(f, "MYSTICISM_0000000E"),
EffectArg::RESTORATION_0000000F => write!(f, "RESTORATION_0000000F"),
EffectArg::ALCHEMY_00000010 => write!(f, "ALCHEMY_00000010"),
EffectArg::UNARMORED_00000011 => write!(f, "UNARMORED_00000011"),
EffectArg::SECURITY_00000012 => write!(f, "SECURITY_00000012"),
EffectArg::SNEAK_00000013 => write!(f, "SNEAK_00000013"),
EffectArg::ACROBATICS_00000014 => write!(f, "ACROBATICS_00000014"),
EffectArg::LIGHT_ARMOR_00000015 => write!(f, "LIGHT_ARMOR_00000015"),
EffectArg::SHORT_BLADE_00000016 => write!(f, "SHORT_BLADE_00000016"),
EffectArg::MARKSMAN_00000017 => write!(f, "MARKSMAN_00000017"),
EffectArg::MERCANTILE_00000018 => write!(f, "MERCANTILE_00000018"),
EffectArg::SPEECHCRAFT_00000019 => write!(f, "SPEECHCRAFT_00000019"),
EffectArg::HAND_TO_HAND_0000001A => write!(f, "HAND_TO_HAND_0000001A"),
EffectArg { dword } => write!(f, "{dword:08X}"),
}
}
}
impl FromStr for EffectArg {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"STRENGTH_BLOCK_00000000" => EffectArg::STRENGTH_BLOCK_00000000,
"STRENGTH" => EffectArg::STRENGTH_BLOCK_00000000,
"BLOCK" => EffectArg::STRENGTH_BLOCK_00000000,
"ARMORER_INTELLIGENCE_00000001" => EffectArg::ARMORER_INTELLIGENCE_00000001,
"ARMORER" => EffectArg::ARMORER_INTELLIGENCE_00000001,
"INTELLIGENCE" => EffectArg::ARMORER_INTELLIGENCE_00000001,
"MEDIUM_ARMOR_WILLPOWER_00000002" => EffectArg::MEDIUM_ARMOR_WILLPOWER_00000002,
"MEDIUM_ARMOR" => EffectArg::MEDIUM_ARMOR_WILLPOWER_00000002,
"WILLPOWER" => EffectArg::MEDIUM_ARMOR_WILLPOWER_00000002,
"HEAVY_ARMOR_AGILITY_00000003" => EffectArg::HEAVY_ARMOR_AGILITY_00000003,
"HEAVY_ARMOR" => EffectArg::HEAVY_ARMOR_AGILITY_00000003,
"AGILITY" => EffectArg::HEAVY_ARMOR_AGILITY_00000003,
"BLUNT_WEAPON_SPEED_00000004" => EffectArg::BLUNT_WEAPON_SPEED_00000004,
"BLUNT_WEAPON" => EffectArg::BLUNT_WEAPON_SPEED_00000004,
"SPEED" => EffectArg::BLUNT_WEAPON_SPEED_00000004,
"LONG_BLADE_ENDURANCE_00000005" => EffectArg::LONG_BLADE_ENDURANCE_00000005,
"LONG_BLADE" => EffectArg::LONG_BLADE_ENDURANCE_00000005,
"ENDURANCE" => EffectArg::LONG_BLADE_ENDURANCE_00000005,
"AXE_PERSONALITY_00000006" => EffectArg::AXE_PERSONALITY_00000006,
"AXE" => EffectArg::AXE_PERSONALITY_00000006,
"PERSONALITY" => EffectArg::AXE_PERSONALITY_00000006,
"SPEAR_LUCK_00000007" => EffectArg::SPEAR_LUCK_00000007,
"SPEAR" => EffectArg::SPEAR_LUCK_00000007,
"LUCK" => EffectArg::SPEAR_LUCK_00000007,
"ATHLETICS_00000008" => EffectArg::ATHLETICS_00000008,
"ATHLETICS" => EffectArg::ATHLETICS_00000008,
"ENCHANT_00000009" => EffectArg::ENCHANT_00000009,
"ENCHANT" => EffectArg::ENCHANT_00000009,
"DESTRUCTION_0000000A" => EffectArg::DESTRUCTION_0000000A,
"DESTRUCTION" => EffectArg::DESTRUCTION_0000000A,
"ALTERATION_0000000B" => EffectArg::ALTERATION_0000000B,
"ALTERATION" => EffectArg::ALTERATION_0000000B,
"ILLUSION_0000000C" => EffectArg::ILLUSION_0000000C,
"ILLUSION" => EffectArg::ILLUSION_0000000C,
"CONJURATION_0000000D" => EffectArg::CONJURATION_0000000D,
"CONJURATION" => EffectArg::CONJURATION_0000000D,
"MYSTICISM_0000000E" => EffectArg::MYSTICISM_0000000E,
"MYSTICISM" => EffectArg::MYSTICISM_0000000E,
"RESTORATION_0000000F" => EffectArg::RESTORATION_0000000F,
"RESTORATION" => EffectArg::RESTORATION_0000000F,
"ALCHEMY_00000010" => EffectArg::ALCHEMY_00000010,
"ALCHEMY" => EffectArg::ALCHEMY_00000010,
"UNARMORED_00000011" => EffectArg::UNARMORED_00000011,
"UNARMORED" => EffectArg::UNARMORED_00000011,
"SECURITY_00000012" => EffectArg::SECURITY_00000012,
"SECURITY" => EffectArg::SECURITY_00000012,
"SNEAK_00000013" => EffectArg::SNEAK_00000013,
"SNEAK" => EffectArg::SNEAK_00000013,
"ACROBATICS_00000014" => EffectArg::ACROBATICS_00000014,
"ACROBATICS" => EffectArg::ACROBATICS_00000014,
"LIGHT_ARMOR_00000015" => EffectArg::LIGHT_ARMOR_00000015,
"LIGHT_ARMOR" => EffectArg::LIGHT_ARMOR_00000015,
"SHORT_BLADE_00000016" => EffectArg::SHORT_BLADE_00000016,
"SHORT_BLADE" => EffectArg::SHORT_BLADE_00000016,
"MARKSMAN_00000017" => EffectArg::MARKSMAN_00000017,
"MARKSMAN" => EffectArg::MARKSMAN_00000017,
"MERCANTILE_00000018" => EffectArg::MERCANTILE_00000018,
"MERCANTILE" => EffectArg::MERCANTILE_00000018,
"SPEECHCRAFT_00000019" => EffectArg::SPEECHCRAFT_00000019,
"SPEECHCRAFT" => EffectArg::SPEECHCRAFT_00000019,
"HAND_TO_HAND_0000001A" => EffectArg::HAND_TO_HAND_0000001A,
"HAND_TO_HAND" => EffectArg::HAND_TO_HAND_0000001A,
s => EffectArg { dword: u32::from_str_radix(s, 16).map_err(|_| ())? },
})
}
}
enum_serde!(EffectArg, "effect arg", u32, dword, from);
impl From<Skill> for EffectArg {
fn from(skill: Skill) -> EffectArg {
EffectArg::from(skill as u32)
}
}
impl From<Attribute> for EffectArg {
fn from(attribute: Attribute) -> EffectArg {
EffectArg::from(attribute as u32)
}
}
impl TryFrom<EffectArg> for Skill {
type Error = ();
fn try_from(arg: EffectArg) -> Result<Skill, ()> {
Skill::n(arg.dword).ok_or(())
}
}
impl TryFrom<EffectArg> for Attribute {
type Error = ();
fn try_from(arg: EffectArg) -> Result<Attribute, ()> {
Attribute::n(arg.dword).ok_or(())
}
}
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u32)]
pub enum Skill {
Block = 0,
Armorer = 1,
MediumArmor = 2,
HeavyArmor = 3,
BluntWeapon = 4,
LongBlade = 5,
Axe = 6,
Spear = 7,
Athletics = 8,
Enchant = 9,
Destruction = 10,
Alteration = 11,
Illusion = 12,
Conjuration = 13,
Mysticism = 14,
Restoration = 15,
Alchemy = 16,
Unarmored = 17,
Security = 18,
Sneak = 19,
Acrobatics = 20,
LightArmor = 21,
ShortBlade = 22,
Marksman = 23,
Mercantile = 24,
Speechcraft = 25,
HandToHand = 26
}
}
enum_serde!(Skill, "skill", as u32, Unsigned, u64);
mod skill_option_i32 {
use crate::field::Skill;
use crate::serde_helpers::*;
use either::{Either};
use serde::{Deserializer, Serialize, Serializer};
use serde::de::DeserializeSeed;
use serde_serialize_seed::ValueWithSeed;
use std::convert::TryInto;
pub fn serialize<S>(v: &Either<Option<i32>, Skill>, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
ValueWithSeed(v, OptionIndexSerde {
name: "skill",
none:-1,
from: |x| x.try_into().ok().and_then(Skill::n),
into: |x| (x as u32).try_into().unwrap()
}).serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Either<Option<i32>, Skill>, D::Error> where D: Deserializer<'de> {
OptionIndexSerde {
name: "skill",
none:-1,
from: |x| x.try_into().ok().and_then(Skill::n),
into: |x| (x as u32).try_into().unwrap()
}.deserialize(deserializer)
}
}
mod skill_option_i8 {
use crate::field::Skill;
use crate::serde_helpers::*;
use either::{Either};
use serde::{Deserializer, Serialize, Serializer};
use serde::de::DeserializeSeed;
use serde_serialize_seed::ValueWithSeed;
use std::convert::TryInto;
pub fn serialize<S>(v: &Either<Option<i8>, Skill>, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
ValueWithSeed(v, OptionIndexSerde {
name: "skill",
none: -1,
from: |x| x.try_into().ok().and_then(Skill::n),
into: |x| (x as u32).try_into().unwrap()
}).serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Either<Option<i8>, Skill>, D::Error> where D: Deserializer<'de> {
OptionIndexSerde {
name: "skill",
none: -1,
from: |x| x.try_into().ok().and_then(Skill::n),
into: |x| (x as u32).try_into().unwrap()
}.deserialize(deserializer)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Skills<T> {
pub block: T,
pub armorer: T,
pub medium_armor: T,
pub heavy_armor: T,
pub blunt_weapon: T,
pub long_blade: T,
pub axe: T,
pub spear: T,
pub athletics: T,
pub enchant: T,
pub destruction: T,
pub alteration: T,
pub illusion: T,
pub conjuration: T,
pub mysticism: T,
pub restoration: T,
pub alchemy: T,
pub unarmored: T,
pub security: T,
pub sneak: T,
pub acrobatics: T,
pub light_armor: T,
pub short_blade: T,
pub marksman: T,
pub mercantile: T,
pub speechcraft: T,
pub hand_to_hand: T,
}
impl<T> Index<Skill> for Skills<T> {
type Output = T;
fn index(&self, index: Skill) -> &Self::Output {
match index {
Skill::Block => &self.block,
Skill::Armorer => &self.armorer,
Skill::MediumArmor => &self.medium_armor,
Skill::HeavyArmor => &self.heavy_armor,
Skill::BluntWeapon => &self.blunt_weapon,
Skill::LongBlade => &self.long_blade,
Skill::Axe => &self.axe,
Skill::Spear => &self.spear,
Skill::Athletics => &self.athletics,
Skill::Enchant => &self.enchant,
Skill::Destruction => &self.destruction,
Skill::Alteration => &self.alteration,
Skill::Illusion => &self.illusion,
Skill::Conjuration => &self.conjuration,
Skill::Mysticism => &self.mysticism,
Skill::Restoration => &self.restoration,
Skill::Alchemy => &self.alchemy,
Skill::Unarmored => &self.unarmored,
Skill::Security => &self.security,
Skill::Sneak => &self.sneak,
Skill::Acrobatics => &self.acrobatics,
Skill::LightArmor => &self.light_armor,
Skill::ShortBlade => &self.short_blade,
Skill::Marksman => &self.marksman,
Skill::Mercantile => &self.mercantile,
Skill::Speechcraft => &self.speechcraft,
Skill::HandToHand => &self.hand_to_hand,
}
}
}
impl<T> IndexMut<Skill> for Skills<T> {
fn index_mut(&mut self, index: Skill) -> &mut Self::Output {
match index {
Skill::Block => &mut self.block,
Skill::Armorer => &mut self.armorer,
Skill::MediumArmor => &mut self.medium_armor,
Skill::HeavyArmor => &mut self.heavy_armor,
Skill::BluntWeapon => &mut self.blunt_weapon,
Skill::LongBlade => &mut self.long_blade,
Skill::Axe => &mut self.axe,
Skill::Spear => &mut self.spear,
Skill::Athletics => &mut self.athletics,
Skill::Enchant => &mut self.enchant,
Skill::Destruction => &mut self.destruction,
Skill::Alteration => &mut self.alteration,
Skill::Illusion => &mut self.illusion,
Skill::Conjuration => &mut self.conjuration,
Skill::Mysticism => &mut self.mysticism,
Skill::Restoration => &mut self.restoration,
Skill::Alchemy => &mut self.alchemy,
Skill::Unarmored => &mut self.unarmored,
Skill::Security => &mut self.security,
Skill::Sneak => &mut self.sneak,
Skill::Acrobatics => &mut self.acrobatics,
Skill::LightArmor => &mut self.light_armor,
Skill::ShortBlade => &mut self.short_blade,
Skill::Marksman => &mut self.marksman,
Skill::Mercantile => &mut self.mercantile,
Skill::Speechcraft => &mut self.speechcraft,
Skill::HandToHand => &mut self.hand_to_hand,
}
}
}
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u32)]
pub enum School {
Alteration = 0,
Conjuration = 1,
Illusion = 2,
Destruction = 3,
Mysticism = 4,
Restoration = 5,
}
}
enum_serde!(School, "school", as u32, Unsigned, u64);
impl From<School> for Skill {
fn from(s: School) -> Skill {
match s {
School::Alteration => Skill::Alteration,
School::Conjuration => Skill::Conjuration,
School::Illusion => Skill::Illusion,
School::Destruction => Skill::Destruction,
School::Mysticism => Skill::Mysticism,
School::Restoration => Skill::Restoration
}
}
}
impl TryFrom<Skill> for School {
type Error = ();
fn try_from(s: Skill) -> Result<School, ()> {
match s {
Skill::Alteration => Ok(School::Alteration),
Skill::Conjuration => Ok(School::Conjuration),
Skill::Illusion => Ok(School::Illusion),
Skill::Destruction => Ok(School::Destruction),
Skill::Mysticism => Ok(School::Mysticism),
Skill::Restoration => Ok(School::Restoration),
_ => Err(())
}
}
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
pub enum EffectArgType {
Attribute, Skill
}
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u32)]
pub enum EffectIndex {
WaterBreathing = 0, SwiftSwim = 1, WaterWalking = 2, Shield = 3, FireShield = 4, LightningShield = 5,
FrostShield = 6, Burden = 7, Feather = 8, Jump = 9, Levitate = 10, SlowFall = 11, Lock = 12, Open = 13,
FireDamage = 14, ShockDamage = 15, FrostDamage = 16, DrainAttribute = 17, DrainHealth = 18,
DrainSpellpoints = 19, DrainFatigue = 20, DrainSkill = 21, DamageAttribute = 22, DamageHealth = 23,
DamageMagicka = 24, DamageFatigue = 25, DamageSkill = 26, Poison = 27, WeaknessToFire = 28,
WeaknessToFrost = 29, WeaknessToShock = 30, WeaknessToMagicka = 31, WeaknessToCommonDisease = 32,
WeaknessToBlightDisease = 33, WeaknessToCorprusDisease = 34, WeaknessToPoison = 35,
WeaknessToNormalWeapons = 36, DisintegrateWeapon = 37, DisintegrateArmor = 38, Invisibility = 39,
Chameleon = 40, Light = 41, Sanctuary = 42, NightEye = 43, Charm = 44, Paralyze = 45, Silence = 46,
Blind = 47, Sound = 48, CalmHumanoid = 49, CalmCreature = 50, FrenzyHumanoid = 51, FrenzyCreature = 52,
DemoralizeHumanoid = 53, DemoralizeCreature = 54, RallyHumanoid = 55, RallyCreature = 56, Dispel = 57,
Soultrap = 58, Telekinesis = 59, Mark = 60, Recall = 61, DivineIntervention = 62, AlmsiviIntervention = 63,
DetectAnimal = 64, DetectEnchantment = 65, DetectKey = 66, SpellAbsorption = 67, Reflect = 68,
CureCommonDisease = 69, CureBlightDisease = 70, CureCorprusDisease = 71, CurePoison = 72,
CureParalyzation = 73, RestoreAttribute = 74, RestoreHealth = 75, RestoreSpellPoints = 76,
RestoreFatigue = 77, RestoreSkill = 78, FortifyAttribute = 79, FortifyHealth = 80, FortifySpellpoints = 81,
FortifyFatigue = 82, FortifySkill = 83, FortifyMagickaMultiplier = 84, AbsorbAttribute = 85,
AbsorbHealth = 86, AbsorbSpellPoints = 87, AbsorbFatigue = 88, AbsorbSkill = 89, ResistFire = 90,
ResistFrost = 91, ResistShock = 92, ResistMagicka = 93, ResistCommonDisease = 94,
ResistBlightDisease = 95, ResistCorprusDisease = 96, ResistPoison = 97, ResistNormalWeapons = 98,
ResistParalysis = 99, RemoveCurse = 100, TurnUndead = 101, SummonScamp = 102, SummonClannfear = 103,
SummonDaedroth = 104, SummonDremora = 105, SummonAncestralGhost = 106, SummonSkeletalMinion = 107,
SummonLeastBonewalker = 108, SummonGreaterBonewalker = 109, SummonBonelord = 110,
SummonWingedTwilight = 111, SummonHunger = 112, SummonGoldensaint = 113, SummonFlameAtronach = 114,
SummonFrostAtronach = 115, SummonStormAtronach = 116, FortifyAttackBonus = 117, CommandCreatures = 118,
CommandHumanoids = 119, BoundDagger = 120, BoundLongsword = 121, BoundMace = 122, BoundBattleAxe = 123,
BoundSpear = 124, BoundLongbow = 125, ExtraSpell = 126, BoundCuirass = 127, BoundHelm = 128,
BoundBoots = 129, BoundShield = 130, BoundGloves = 131, Corpus = 132, Vampirism = 133,
SummonCenturionSphere = 134, SunDamage = 135, StuntedMagicka = 136, SummonFabricant = 137,
SummonCreature01 = 138, SummonCreature02 = 139, SummonCreature03 = 140, SummonCreature04 = 141,
SummonCreature05 = 142
}
}
impl EffectIndex {
pub fn arg_type(self) -> Option<EffectArgType> {
match self {
EffectIndex::AbsorbAttribute |
EffectIndex::DamageAttribute |
EffectIndex::DrainAttribute |
EffectIndex::FortifyAttribute |
EffectIndex::RestoreAttribute =>
Some(EffectArgType::Attribute),
EffectIndex::AbsorbSkill |
EffectIndex::DamageSkill |
EffectIndex::DrainSkill |
EffectIndex::FortifySkill |
EffectIndex::RestoreSkill =>
Some(EffectArgType::Skill),
_ => None
}
}
}
enum_serde!(EffectIndex, "effect index", as u32, Unsigned, u64);
mod effect_index_option_i32 {
use crate::field::EffectIndex;
use crate::serde_helpers::*;
use either::{Either};
use serde::{Deserializer, Serialize, Serializer};
use serde::de::DeserializeSeed;
use serde_serialize_seed::ValueWithSeed;
use std::convert::TryInto;
pub fn serialize<S>(v: &Either<Option<i32>, EffectIndex>, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
ValueWithSeed(v, OptionIndexSerde {
name: "effect index",
none: -1,
from: |x| x.try_into().ok().and_then(EffectIndex::n),
into: |x| (x as u32).try_into().unwrap()
}).serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Either<Option<i32>, EffectIndex>, D::Error> where D: Deserializer<'de> {
OptionIndexSerde {
name: "effect index",
none: -1,
from: |x| x.try_into().ok().and_then(EffectIndex::n),
into: |x| (x as u32).try_into().unwrap()
}.deserialize(deserializer)
}
}
mod effect_index_option_i16 {
use crate::field::EffectIndex;
use crate::serde_helpers::*;
use either::{Either};
use serde::{Deserializer, Serialize, Serializer};
use serde::de::DeserializeSeed;
use serde_serialize_seed::ValueWithSeed;
use std::convert::TryInto;
pub fn serialize<S>(v: &Either<Option<i16>, EffectIndex>, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
ValueWithSeed(v, OptionIndexSerde {
name: "effect index",
none: -1,
from: |x| x.try_into().ok().and_then(EffectIndex::n),
into: |x| (x as u32).try_into().unwrap()
}).serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Either<Option<i16>, EffectIndex>, D::Error> where D: Deserializer<'de> {
OptionIndexSerde {
name: "effect index",
none: -1,
from: |x| x.try_into().ok().and_then(EffectIndex::n),
into: |x| (x as u32).try_into().unwrap()
}.deserialize(deserializer)
}
}
bitflags_ext! {
pub struct EffectFlags: u32 {
SPELLMAKING = 0x200,
ENCHANTING = 0x400,
LIGHT_NEGATIVE = 0x800
}
}
enum_serde!(EffectFlags, "effect flags", u32, bits(), try from_bits, Unsigned, u64);
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct EffectMetadata {
pub school: School,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub base_cost: f32,
pub flags: EffectFlags,
#[serde(with="color_components")]
pub color: Color,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub size_factor: f32,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub speed: f32,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub size_cap: f32,
}
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone, Debug)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
}
impl Display for Color {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "#{:02X}{:02X}{:02X}", self.r, self.g, self.b)
}
}
impl FromStr for Color {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != 7{ return Err(()); }
if &s[0..1] != "#" || &s[1..2] == "+" { return Err(()); }
let r = u8::from_str_radix(&s[1..3], 16).map_err(|_| ())?;
let g = u8::from_str_radix(&s[3..5], 16).map_err(|_| ())?;
let b = u8::from_str_radix(&s[5..7], 16).map_err(|_| ())?;
Ok(Color { r, g, b })
}
}
impl Color {
pub fn to_u32(self) -> u32 {
(self.r as u32) | ((self.g as u32) << 8) | ((self.b as u32) << 16)
}
pub fn try_from_u32(u: u32) -> Option<Color> {
if u & 0xFF000000 != 0 {
None
} else {
let r = (u & 0xFF) as u8;
let g = ((u >> 8) & 0xFF) as u8;
let b = ((u >> 16) & 0xFF) as u8;
Some(Color { r, g, b })
}
}
}
enum_serde!(Color, "RGB color", u32, to_u32(), try try_from_u32, Unsigned, u64);
mod color_components {
use std::convert::TryInto;
use serde::{Serializer, Deserializer, Serialize, Deserialize};
use crate::field::Color;
use serde::de::Unexpected;
use serde::de::Error as de_Error;
use serde::ser::SerializeTuple;
pub fn serialize<S>(&c: &Color, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
if serializer.is_human_readable() {
c.serialize(serializer)
} else {
let mut serializer = serializer.serialize_tuple(3)?;
serializer.serialize_element(&(c.r as u32))?;
serializer.serialize_element(&(c.g as u32))?;
serializer.serialize_element(&(c.b as u32))?;
serializer.end()
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Color, D::Error> where D: Deserializer<'de> {
if deserializer.is_human_readable() {
Color::deserialize(deserializer)
} else {
let (r, g, b) = <(u32, u32, u32)>::deserialize(deserializer)?;
let r = r.try_into().map_err(|_| D::Error::invalid_value(Unexpected::Unsigned(r as u64), &"0 .. 255"))?;
let g = g.try_into().map_err(|_| D::Error::invalid_value(Unexpected::Unsigned(g as u64), &"0 .. 255"))?;
let b = b.try_into().map_err(|_| D::Error::invalid_value(Unexpected::Unsigned(b as u64), &"0 .. 255"))?;
Ok(Color { r, g, b })
}
}
}
bitflags_ext! {
pub struct LightFlags: u32 {
DYNAMIC = 0x0001,
CAN_CARRY = 0x0002,
NEGATIVE = 0x0004,
FLICKER = 0x0008,
FIRE = 0x0010,
OFF_BY_DEFAULT = 0x0020,
FLICKER_SLOW = 0x0040,
PULSE = 0x0080,
PULSE_SLOW = 0x0100
}
}
enum_serde!(LightFlags, "light flags", u32, bits(), try from_bits, Unsigned, u64);
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct Light {
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub weight: f32,
pub value: u32,
pub time: i32,
pub radius: u32,
pub color: Color,
pub flags: LightFlags,
}
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct MiscItem {
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub weight: f32,
pub value: u32,
#[serde(with="bool_u32")]
pub is_key: bool,
}
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u32)]
pub enum ApparatusType {
MortarPestle = 0,
Alembic = 1,
Calcinator = 2,
Retort = 3
}
}
enum_serde!(ApparatusType, "apparatus type", as u32, Unsigned, u64);
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct Apparatus {
#[serde(rename="type")]
pub apparatus_type: ApparatusType,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub quality: f32,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub weight: f32,
pub value: u32,
}
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u32)]
pub enum ArmorType {
Helmet = 0,
Cuirass = 1,
LeftPauldron = 2,
RightPauldron = 3,
Greaves = 4,
Boots = 5,
LeftGauntlet = 6,
RightGauntlet = 7,
Shield = 8,
LeftBracer = 9,
RightBracer = 10
}
}
enum_serde!(ArmorType, "armor type", as u32, Unsigned, u64);
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct Armor {
#[serde(rename="type")]
pub armor_type: ArmorType,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub weight: f32,
pub value: u32,
pub health: u32,
pub enchantment: u32,
pub armor: u32,
}
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u16)]
pub enum WeaponType {
ShortBladeOneHand = 0,
LongBladeOneHand = 1,
LongBladeTwoClose = 2,
BluntOneHand = 3,
BluntTwoClose = 4,
BluntTwoWide = 5,
SpearTwoWide = 6,
AxeOneHand = 7,
AxeTwoClose = 8,
MarksmanBow = 9,
MarksmanCrossbow = 10,
MarksmanThrown = 11,
Arrow = 12,
Bolt = 13
}
}
enum_serde!(WeaponType, "weapon type", as u16, Unsigned, u64);
bitflags_ext! {
pub struct WeaponFlags: u32 {
MAGICAL = 0x01,
SILVER = 0x02
}
}
enum_serde!(WeaponFlags, "weapon flags", u32, bits(), try from_bits, Unsigned, u64);
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct Weapon {
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub weight: f32,
pub value: u32,
#[serde(rename="type")]
pub weapon_type: WeaponType,
pub health: u16,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub speed: f32,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub reach: f32,
pub enchantment: u16,
pub chop_min: u8,
pub chop_max: u8,
pub slash_min: u8,
pub slash_max: u8,
pub thrust_min: u8,
pub thrust_max: u8,
pub flags: WeaponFlags,
}
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u8)]
pub enum BodyPartKind {
Head = 0,
Hair = 1,
Neck = 2,
Chest = 3,
Groin = 4,
Hand = 5,
Wrist = 6,
Forearm = 7,
UpperArm = 8,
Foot = 9,
Ankle = 10,
Knee = 11,
UpperLeg = 12,
Clavicle = 13,
Tail = 14,
}
}
enum_serde!(BodyPartKind, "body part kind", as u8, Unsigned, u64);
bitflags_ext! {
pub struct BodyPartFlags: u8 {
FEMALE = 0x01,
NON_PLAYABLE = 0x02
}
}
enum_serde!(BodyPartFlags, "body part flags", u8, bits(), try from_bits, Unsigned, u64);
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u8)]
pub enum BodyPartType {
Skin = 0,
Clothing = 1,
Armor = 2
}
}
enum_serde!(BodyPartType, "body part type", as u8, Unsigned, u64);
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u8)]
pub enum BipedObject {
Head = 0,
Hair = 1,
Neck = 2,
Cuirass = 3,
Groin = 4,
Skirt = 5,
RightHand = 6,
LeftHand = 7,
RightWrist = 8,
LeftWrist = 9,
Shield = 10,
RightForearm = 11,
LeftForearm = 12,
RightUpperArm = 13,
LeftUpperArm = 14,
RightFoot = 15,
LeftFoot = 16,
RightAnkle = 17,
LeftAnkle = 18,
RightKnee = 19,
LeftKnee = 20,
RightUpperLeg = 21,
LeftUpperLeg = 22,
RightPauldron = 23,
LeftPauldron = 24,
Weapon = 25,
Tail = 26,
}
}
enum_serde!(BipedObject, "biped object", as u8, Unsigned, u64);
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u32)]
pub enum ClothingType {
Pants = 0,
Shoes = 1,
Shirt = 2,
Belt = 3,
Robe = 4,
RightGlove = 5,
LeftGlove = 6,
Skirt = 7,
Ring = 8,
Amulet = 9
}
}
enum_serde!(ClothingType, "clothing type", as u32, Unsigned, u64);
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct BodyPart {
pub kind: BodyPartKind,
#[serde(with="bool_u8")]
pub vampire: bool,
pub flags: BodyPartFlags,
#[serde(rename="type")]
pub body_part_type: BodyPartType,
}
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct Clothing {
#[serde(rename="type")]
pub clothing_type: ClothingType,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub weight: f32,
pub value: u16,
pub enchantment: u16,
}
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u32)]
pub enum EnchantmentType {
CastOnce = 0,
WhenStrikes = 1,
WhenUsed = 2,
ConstantEffect = 3
}
}
enum_serde!(EnchantmentType, "enchantment type", as u32, Unsigned, u64);
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Enchantment {
#[serde(rename="type")]
pub enchantment_type: EnchantmentType,
pub cost: u32,
pub charge_amount: u32,
#[serde(with="bool_either_i16")]
pub auto_calculate: Either<bool, bool>,
pub padding: u16,
}
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct Tool {
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub weight: f32,
pub value: u32,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub quality: f32,
pub uses: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub(crate) struct RepairItem {
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub weight: f32,
pub value: u32,
pub uses: u32,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub quality: f32,
}
impl From<RepairItem> for Tool {
fn from(t: RepairItem)-> Tool {
Tool { weight: t.weight, value: t.value, quality: t.quality, uses: t.uses }
}
}
impl From<Tool> for RepairItem {
fn from(t: Tool)-> RepairItem {
RepairItem { weight: t.weight, value: t.value, quality: t.quality, uses: t.uses }
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct Pos {
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub x: f32,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub y: f32,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub z: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct Rot {
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub x: f32,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub y: f32,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub z: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct PosRot {
pub pos: Pos,
pub rot: Rot,
}
bitflags_ext! {
pub struct CellFlags: u32 {
INTERIOR = 0x01,
HAS_WATER = 0x02,
ILLEGAL_TO_SLEEP = 0x04,
BEHAVE_LIKE_EXTERIOR = 0x80,
_8 = 0x08,
_10 = 0x10,
_20 = 0x20,
_40 = 0x40
}
}
enum_serde!(CellFlags, "cell flags", u32, bits(), try from_bits, Unsigned, u64);
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Cell {
pub flags: CellFlags,
pub position: CellPosition,
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename="Cell")]
struct CellHRSurrogate {
pub flags: CellFlags,
pub position: CellPosition,
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename="Cell")]
struct CellNHRSurrogate {
pub flags: CellFlags,
pub position: (i32, i32),
}
impl From<CellHRSurrogate> for Cell {
fn from(cell: CellHRSurrogate) -> Cell {
Cell {
flags: cell.flags,
position: cell.position,
}
}
}
impl From<CellNHRSurrogate> for Cell {
fn from(cell: CellNHRSurrogate) -> Cell {
let position = CellPosition::from_exterior(
cell.flags.contains(CellFlags::INTERIOR),
cell.position
);
Cell { flags: cell.flags, position }
}
}
impl Serialize for Cell {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
let is_interior = self.flags.contains(CellFlags::INTERIOR);
let position_is_interior = match self.position {
CellPosition::Interior { .. } => true,
CellPosition::Exterior { .. } => false,
};
if is_interior != position_is_interior {
return Err(S::Error::custom("cell INTERIOR flag and position type do not match"));
}
if serializer.is_human_readable() {
CellHRSurrogate {
flags: self.flags,
position: self.position.clone()
}.serialize(serializer)
} else {
CellNHRSurrogate {
flags: self.flags,
position: self.position.to_exterior()
}.serialize(serializer)
}
}
}
impl<'de> Deserialize<'de> for Cell {
fn deserialize<D>(deserializer: D) -> Result<Cell, D::Error> where D: Deserializer<'de> {
if deserializer.is_human_readable() {
CellHRSurrogate::deserialize(deserializer).map(Cell::from)
} else {
CellNHRSurrogate::deserialize(deserializer).map(Cell::from)
}
}
}
#[derive(Debug, Clone, Educe, Serialize, Deserialize)]
#[educe(Eq, PartialEq)]
#[serde(tag="type")]
pub enum CellPosition {
Interior {
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
x: f32,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
y: f32
},
Exterior { x: i32, y: i32 }
}
impl CellPosition {
fn from_exterior(is_interior: bool, position: (i32, i32)) -> Self {
if is_interior {
CellPosition::Interior {
x: unsafe { transmute(position.0) },
y: unsafe { transmute(position.1) },
}
} else {
CellPosition::Exterior {
x: position.0,
y: position.1
}
}
}
fn to_exterior(&self) -> (i32, i32) {
match self {
&CellPosition::Exterior { x, y } => (x, y),
&CellPosition::Interior { x, y } => (
unsafe { transmute(x) },
unsafe { transmute(y) }
)
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct Interior {
pub ambient: Color,
pub sunlight: Color,
pub fog: Color,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub fog_density: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Grid {
pub x: i32,
pub y: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct PathGrid {
pub grid: Grid,
pub flags: u16,
pub points: u16,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Weather {
pub clear: u8,
pub cloudy: u8,
pub foggy: u8,
pub overcast: u8,
pub rain: u8,
pub thunder: u8,
pub ash: u8,
pub blight: u8,
pub ex: Option<WeatherEx>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct WeatherEx {
pub snow: u8,
pub blizzard: u8,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct SoundChance {
pub sound_id: String,
pub chance: u8,
}
const SOUND_CHANCE_SOUND_ID_FIELD: &str = name_of!(sound_id in SoundChance);
const SOUND_CHANCE_CHANCE_FIELD: &str = name_of!(chance in SoundChance);
const SOUND_CHANCE_FIELDS: &[&str] = &[
SOUND_CHANCE_SOUND_ID_FIELD,
SOUND_CHANCE_CHANCE_FIELD,
];
#[derive(Clone)]
pub struct SoundChanceSerde {
pub code_page: Option<CodePage>
}
impl SerializeSeed for SoundChanceSerde {
type Value = SoundChance;
fn serialize<S: Serializer>(&self, value: &Self::Value, serializer: S) -> Result<S::Ok, S::Error> {
let mut serializer = serializer.serialize_struct(name_of!(type SoundChance), 2)?;
serializer.serialize_field(
SOUND_CHANCE_SOUND_ID_FIELD,
&ValueWithSeed(value.sound_id.as_str(), StringSerde { code_page: self.code_page, len: Some(32) })
)?;
serializer.serialize_field(SOUND_CHANCE_CHANCE_FIELD, &value.chance)?;
serializer.end()
}
}
enum SoundChanceField {
SoundId,
Chance,
}
struct SoundChanceFieldDeVisitor;
impl<'de> de::Visitor<'de> for SoundChanceFieldDeVisitor {
type Value = SoundChanceField;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "sound chance field")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
match value {
SOUND_CHANCE_SOUND_ID_FIELD => Ok(SoundChanceField::SoundId),
SOUND_CHANCE_CHANCE_FIELD => Ok(SoundChanceField::Chance),
x => Err(E::unknown_field(x, SOUND_CHANCE_FIELDS)),
}
}
}
impl<'de> de::Deserialize<'de> for SoundChanceField {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_identifier(SoundChanceFieldDeVisitor)
}
}
struct SoundChanceDeVisitor(SoundChanceSerde);
impl<'de> de::Visitor<'de> for SoundChanceDeVisitor {
type Value = SoundChance;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "AI activate")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> where A: de::MapAccess<'de> {
let mut sound_id = None;
let mut chance = None;
while let Some(field) = map.next_key()? {
match field {
SoundChanceField::SoundId =>
if sound_id.replace(map.next_value_seed(
StringSerde { code_page: self.0.code_page, len: Some(32) }
)?).is_some() {
return Err(A::Error::duplicate_field(SOUND_CHANCE_SOUND_ID_FIELD));
},
SoundChanceField::Chance => if chance.replace(map.next_value()?).is_some() {
return Err(A::Error::duplicate_field(SOUND_CHANCE_CHANCE_FIELD));
},
}
}
let sound_id = sound_id.ok_or_else(|| A::Error::missing_field(SOUND_CHANCE_SOUND_ID_FIELD))?;
let chance = chance.ok_or_else(|| A::Error::missing_field(SOUND_CHANCE_CHANCE_FIELD))?;
Ok(SoundChance { sound_id, chance })
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> where A: de::SeqAccess<'de> {
let sound_id = seq.next_element_seed(StringSerde { code_page: self.0.code_page, len: Some(32) })?
.ok_or_else(|| A::Error::invalid_length(0, &self))?;
let chance = seq.next_element()?
.ok_or_else(|| A::Error::invalid_length(1, &self))?;
Ok(SoundChance { sound_id, chance })
}
}
impl<'de> DeserializeSeed<'de> for SoundChanceSerde {
type Value = SoundChance;
fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
deserializer.deserialize_struct(name_of!(type SoundChance), SOUND_CHANCE_FIELDS, SoundChanceDeVisitor(self))
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct Potion {
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub weight: f32,
pub value: u32,
#[serde(with="bool_u32")]
pub auto_calculate_value: bool,
}
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u32)]
pub enum Specialization {
Combat = 0,
Magic = 1,
Stealth = 2,
}
}
enum_serde!(Specialization, "specialization", as u32, Unsigned, u64);
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Class {
pub primary_attribute_1: Attribute,
pub primary_attribute_2: Attribute,
pub specialization: Specialization,
pub minor_skill_1: Skill,
pub major_skill_1: Skill,
pub minor_skill_2: Skill,
pub major_skill_2: Skill,
pub minor_skill_3: Skill,
pub major_skill_3: Skill,
pub minor_skill_4: Skill,
pub major_skill_4: Skill,
pub minor_skill_5: Skill,
pub major_skill_5: Skill,
#[serde(with="bool_u32")]
pub playable: bool,
pub auto_calc_services: Services,
}
bitflags_ext! {
pub struct RaceFlags: u32 {
PLAYABLE = 0x01,
BEAST_RACE = 0x02
}
}
enum_serde!(RaceFlags, "race flags", u32, bits(), try from_bits, Unsigned, u64);
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct RaceAttribute {
pub male: u32,
pub female: u32,
}
impl Index<Sex> for RaceAttribute {
type Output = u32;
fn index(&self, index: Sex) -> &Self::Output {
match index {
Sex::Male => &self.male,
Sex::Female => &self.female,
}
}
}
impl IndexMut<Sex> for RaceAttribute {
fn index_mut(&mut self, index: Sex) -> &mut Self::Output {
match index {
Sex::Male => &mut self.male,
Sex::Female => &mut self.female,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct RaceParameter {
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub male: f32,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub female: f32,
}
impl Index<Sex> for RaceParameter {
type Output = f32;
fn index(&self, index: Sex) -> &Self::Output {
match index {
Sex::Male => &self.male,
Sex::Female => &self.female,
}
}
}
impl IndexMut<Sex> for RaceParameter {
fn index_mut(&mut self, index: Sex) -> &mut Self::Output {
match index {
Sex::Male => &mut self.male,
Sex::Female => &mut self.female,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Race {
#[serde(with="skill_option_i32")]
pub skill_1: Either<Option<i32>, Skill>,
pub skill_1_bonus: u32,
#[serde(with="skill_option_i32")]
pub skill_2: Either<Option<i32>, Skill>,
pub skill_2_bonus: u32,
#[serde(with="skill_option_i32")]
pub skill_3: Either<Option<i32>, Skill>,
pub skill_3_bonus: u32,
#[serde(with="skill_option_i32")]
pub skill_4: Either<Option<i32>, Skill>,
pub skill_4_bonus: u32,
#[serde(with="skill_option_i32")]
pub skill_5: Either<Option<i32>, Skill>,
pub skill_5_bonus: u32,
#[serde(with="skill_option_i32")]
pub skill_6: Either<Option<i32>, Skill>,
pub skill_6_bonus: u32,
#[serde(with="skill_option_i32")]
pub skill_7: Either<Option<i32>, Skill>,
pub skill_7_bonus: u32,
pub attributes: Attributes<RaceAttribute>,
pub height: RaceParameter,
pub weight: RaceParameter,
pub flags: RaceFlags
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Sound {
pub volume: u8,
pub range_min: u8,
pub range_max: u8,
}
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u32)]
pub enum SoundGen {
Left = 0,
Right = 1,
SwimLeft = 2,
SwimRight = 3,
Moan = 4,
Roar = 5,
Scream = 6,
Land = 7
}
}
enum_serde!(SoundGen, "sound gen", as u32, Unsigned, u64);
macro_attr! {
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
#[repr(u8)]
pub enum Sex {
Male = 0,
Female = 1
}
}
enum_serde!(Sex, "sex", as u8, Unsigned, u64);
mod sex_option_i8 {
use crate::field::Sex;
use crate::serde_helpers::*;
use either::{Either};
use serde::{Deserializer, Serialize, Serializer};
use serde::de::DeserializeSeed;
use serde_serialize_seed::ValueWithSeed;
use std::convert::TryInto;
pub fn serialize<S>(v: &Either<Option<i8>, Sex>, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
ValueWithSeed(v, OptionIndexSerde {
name: "sex",
none: -1,
from: |x| x.try_into().ok().and_then(Sex::n),
into: |x| (x as u8).try_into().unwrap()
}).serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Either<Option<i8>, Sex>, D::Error> where D: Deserializer<'de> {
OptionIndexSerde {
name: "sex",
none: -1,
from: |x| x.try_into().ok().and_then(Sex::n),
into: |x| (x as u8).try_into().unwrap()
}.deserialize(deserializer)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Info {
#[serde(with="dialog_type_u32")]
pub dialog_type: DialogType,
pub disp_index: u32,
#[serde(with="option_i8")]
pub rank: Option<i8>,
#[serde(with="sex_option_i8")]
pub sex: Either<Option<i8>, Sex>,
#[serde(with="option_i8")]
pub pc_rank: Option<i8>,
pub padding: u8,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Rank {
pub attribute_1: u32,
pub attribute_2: u32,
pub primary_skill: u32,
pub favored_skill: u32,
pub reputation: u32
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Faction {
pub favored_attribute_1: Attribute,
pub favored_attribute_2: Attribute,
pub ranks: [Rank; 10],
#[serde(with="skill_option_i32")]
pub favored_skill_1: Either<Option<i32>, Skill>,
#[serde(with="skill_option_i32")]
pub favored_skill_2: Either<Option<i32>, Skill>,
#[serde(with="skill_option_i32")]
pub favored_skill_3: Either<Option<i32>, Skill>,
#[serde(with="skill_option_i32")]
pub favored_skill_4: Either<Option<i32>, Skill>,
#[serde(with="skill_option_i32")]
pub favored_skill_5: Either<Option<i32>, Skill>,
#[serde(with="skill_option_i32")]
pub favored_skill_6: Either<Option<i32>, Skill>,
#[serde(with="skill_option_i32")]
pub favored_skill_7: Either<Option<i32>, Skill>,
#[serde(with="bool_u32")]
pub hidden_from_pc: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Educe)]
#[educe(Eq, PartialEq)]
pub struct SkillMetadata {
pub governing_attribute: Attribute,
pub specialization: Specialization,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub use_value_1: f32,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub use_value_2: f32,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub use_value_3: f32,
#[educe(PartialEq(method="eq_f32"))]
#[serde(with="float_32")]
pub use_value_4: f32,
}
macro_rules! define_field {
($($variant:ident($(#[educe(PartialEq(method=$a:literal))])? $from:ty),)*) => {
#[derive(Debug, Clone)]
#[derive(Educe)]
#[educe(PartialEq, Eq)]
pub enum Field {
None,
$($variant($(#[educe(PartialEq(method=$a))])? $from)),*
}
$(
impl From<$from> for Field {
fn from(v: $from) -> Self { Field::$variant(v) }
}
)*
}
}
define_field!(
Ai(Ai),
AiActivate(AiActivate),
AiTarget(AiTarget),
AiTravel(AiTravel),
AiWander(AiWander),
Apparatus(Apparatus),
Armor(Armor),
BipedObject(BipedObject),
BodyPart(BodyPart),
Book(Book),
Cell(Cell),
Class(Class),
Clothing(Clothing),
Color(Color),
ContainerFlags(ContainerFlags),
Creature(Creature),
CreatureFlags(FlagsAndBlood<CreatureFlags>),
DialogType(DialogType),
Effect(Effect),
EffectIndex(EffectIndex),
Tag(Tag),
EffectMetadata(EffectMetadata),
Enchantment(Enchantment),
F32(#[educe(PartialEq(method="eq_f32"))] f32),
F32List(#[educe(PartialEq(method="eq_f32_list"))] Vec<f32>),
Faction(Faction),
FileMetadata(FileMetadata),
Grid(Grid),
I16(i16),
I16List(Vec<i16>),
I32(i32),
I32List(Vec<i32>),
I64(i64),
Info(Info),
Ingredient(Ingredient),
Interior(Interior),
Item(Item),
Light(Light),
MiscItem(MiscItem),
Npc(Npc),
NpcFlags(FlagsAndBlood<NpcFlags>),
NpcState(NpcState),
PathGrid(PathGrid),
Pos(Pos),
PosRot(PosRot),
Potion(Potion),
Race(Race),
ScriptMetadata(ScriptMetadata),
ScriptVars(ScriptVars),
Skill(Skill),
SkillMetadata(SkillMetadata),
Sound(Sound),
SoundChance(SoundChance),
SoundGen(SoundGen),
Spell(Spell),
String(String),
StringList(Vec<String>),
StringZ(StringZ),
StringZList(StringZList),
Tool(Tool),
U8(u8),
Bool(bool),
U8List(Vec<u8>),
Weapon(Weapon),
Weather(Weather),
CurrentTime(CurrentTime),
Time(Time),
EffectArg(EffectArg),
Attributes(Attributes<u32>),
Skills(Skills<u32>),
ScriptData(ScriptData),
);
impl From<()> for Field {
fn from(_: ()) -> Self { Field::None }
}
fn allow_fit(record_tag: Tag, field_tag: Tag) -> bool {
matches!((record_tag, field_tag)
, (_, AI_A)
| (_, AI_E)
| (_, AI_F)
| (ARMO, BNAM)
| (BODY, BNAM)
| (CLOT, BNAM)
| (INFO, BNAM)
| (ARMO, CNAM)
| (SSCR, DATA)
| (BSGN, DESC)
| (ACTI, FNAM)
| (TES3, HEDR)
| (CELL, NAME)
| (JOUR, NAME)
| (SSCR, NAME)
| (INFO, NNAM)
| (INFO, PNAM)
| (FACT, RNAM)
| (_, SCTX)
| (REGN, SNAM)
| (BOOK, TEXT)
)
}
impl Field {
pub fn fit(&mut self, record_tag: Tag, prev_tag: Tag, field_tag: Tag) {
if !allow_fit(record_tag, field_tag) { return; }
match FieldType::from_tags(record_tag, prev_tag, field_tag) {
FieldType::FileMetadata => {
if let Field::FileMetadata(v) = self {
v.author.find('\0').map(|i| v.author.truncate(i));
let mut d = v.description.join(Newline::Dos.as_str());
d.find('\0').map(|i| d.truncate(i));
v.description = d.split(Newline::Dos.as_str()).map(String::from).collect();
} else {
panic!("invalid field type")
}
},
FieldType::String(_) => {
if let Field::String(v) = self {
v.find('\0').map(|i| v.truncate(i));
} else {
panic!("invalid field type")
}
},
FieldType::SoundChance => {
if let Field::SoundChance(v) = self {
v.sound_id.find('\0').map(|i| v.sound_id.truncate(i));
} else {
panic!("invalid field type")
}
},
FieldType::Multiline(newline) => {
if let Field::StringList(v) = self {
let mut s = v.join(newline.as_str());
s.find('\0').map(|i| s.truncate(i));
*v = s.split(newline.as_str()).map(String::from).collect();
} else {
panic!("invalid field type")
}
},
FieldType::StringZ => {
if let Field::StringZ(v) = self {
v.string.find('\0').map(|i| v.string.truncate(i));
v.has_tail_zero = true;
} else {
panic!("invalid field type")
}
},
FieldType::StringZList => {
if let Field::StringZList(v) = self {
v.has_tail_zero = true;
} else {
panic!("invalid field type")
}
},
FieldType::AiTarget => {
if let Field::AiTarget(v) = self {
v.actor_id.find('\0').map(|i| v.actor_id.truncate(i));
} else {
panic!("invalid field type")
}
},
FieldType::AiActivate => {
if let Field::AiActivate(v) = self {
v.object_id.find('\0').map(|i| v.object_id.truncate(i));
} else {
panic!("invalid field type")
}
},
_ => ()
}
}
}
#[cfg(test)]
mod tests {
use crate::*;
use quickcheck_macros::quickcheck;
use std::str::FromStr;
#[test]
fn debug_and_display_tag() {
assert_eq!("TES3", format!("{}", TES3));
assert_eq!("TES3", format!("{:?}", TES3));
assert_eq!(Ok(SCPT), Tag::from_str("SCPT"));
}
#[test]
fn test_file_type() {
assert_eq!("ESM", format!("{}", FileType::ESM));
assert_eq!("ESS", format!("{:?}", FileType::ESS));
assert_eq!(Some(FileType::ESP), FileType::n(0));
assert_eq!(None, FileType::n(2));
assert_eq!(32, FileType::ESS as u32);
assert_eq!(Ok(FileType::ESP), FileType::from_str("ESP"));
}
#[test]
fn light_flags_from_str() {
assert_eq!(
LightFlags::from_str("DYNAMIC CAN_CARRY FIRE FLICKER_SLOW"),
Ok(LightFlags::DYNAMIC | LightFlags::CAN_CARRY | LightFlags::FIRE | LightFlags::FLICKER_SLOW)
);
}
#[quickcheck]
fn effect_arg_from_str_is_display_inversion(dword: u32) -> bool {
EffectArg::from_str(&EffectArg { dword }.to_string()) == Ok(EffectArg { dword })
}
#[quickcheck]
fn effect_arg_from_dword_str_eq_effect_arg_from_dword(dword: u32) -> bool {
EffectArg::from_str(&format!("{dword:04X}")) == Ok(EffectArg { dword })
}
}