esl 0.17.1

A library for reading, writing and processing ESM/ESP/ESS files.
Documentation
use crate::field::{eq_f32, float_32};
use crate::code_page::CodePage;
use crate::serde_helpers::HexDump;
use educe::Educe;
use enum_derive_2018::{EnumDisplay, EnumFromStr};
use enumn::N;
use macro_attr_2018::macro_attr;
use nameof::name_of;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::de::{self, DeserializeSeed};
use serde::de::Error as de_Error;
use serde::ser::SerializeStruct;
use serde::ser::Error as ser_Error;
use serde_serialize_seed::{SerializeSeed, ValueWithSeed};
use std::fmt::{self, Formatter};

macro_attr! {
    #[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
    #[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
    #[repr(u8)]
    pub enum VarType {
        Short = b's',
        Float = b'f',
        Long = b'l',
        Mystery = b'V',
    }
}

enum_serde!(VarType, "var type", as u8, Unsigned, u64);

impl VarType {
    fn write(self, res: &mut Vec<u8>) {
        res.push(self as u8);
    }
}
 
#[derive(Debug, Clone, Copy, Educe, Serialize, Deserialize)]
#[educe(Eq, PartialEq)]
#[serde(tag="kind")]
pub enum Float {
    Val {
        #[educe(PartialEq(method="eq_f32"))]
        #[serde(with="float_32")]
        val: f32
    },
    Var { #[serde(rename="type")] var_type: VarType, index: u16 },
}

impl Float {
    fn write(self, res: &mut Vec<u8>) -> Result<(), String> {
        match self {
            Float::Var { var_type, index } => {
                var_type.write(res);
                write_u16(index, res);
                res.push(0);
            },
            Float::Val { val } => {
                let bits = val.to_bits();
                if VarType::n((bits & 0xFF) as u8).is_some() && (bits >> 24) == 0 {
                    return Err("denied float value '{val}/{bits:08X}'".to_string());
                }
                write_u32(bits, res);
            }
        }
        Ok(())
    }
}

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(tag="kind")]
pub enum Var {
    Local { owner: Option<String>, #[serde(rename="type")] var_type: VarType, index: u16 },
    Global { name: String },
}

fn write_char(code_page: CodePage, s: &str, res: &mut Vec<u8>) -> Result<(), String> {
    let bytes = code_page.encode(s).map_err(|e| match e {
        None => format!("the '{s}' string does not correspond to any source byte sequence"),
        Some(c) => format!("the '{c}' char is not representable in {code_page:?} code page")
    })?;
    if bytes.len() != 1 {
        return Err(format!("multiply chars '{s}"));
    }
    res.push(bytes[0]);
    Ok(())
}

fn write_str_raw(
    write_len: impl FnOnce(usize, &mut Vec<u8>) -> Result<(), ()>, code_page: CodePage, s: &str, res: &mut Vec<u8>
) -> Result<(), String> {
    let bytes = code_page.encode(s).map_err(|e| match e {
        None => format!("the '{s}' string does not correspond to any source byte sequence"),
        Some(c) => format!("the '{c}' char is not representable in {code_page:?} code page")
    })?;
    write_len(bytes.len(), res).map_err(|()| format!("too long string '{s}'"))?;
    res.extend_from_slice(&bytes);
    Ok(())
}

fn write_str(code_page: CodePage, s: &str, res: &mut Vec<u8>) -> Result<(), String> {
    write_str_raw(|len, res| {
        let len = len.try_into().map_err(|_| ())?;
        res.push(len);
        Ok(())
    }, code_page, s, res)
}

fn write_int_list(w: &[i16], res: &mut Vec<u8>) -> Result<(), String> {
    let len = w.len().try_into().map_err(|_| "too big word list".to_string())?;
    write_u16(len, res);
    for w in w {
        write_i16(*w, res);
    }
    Ok(())
}

fn write_str_list(code_page: CodePage, s: &[String], res: &mut Vec<u8>) -> Result<(), String> {
    let len = s.len().try_into().map_err(|_| "too big string list".to_string())?;
    res.push(len);
    for s in s {
        write_str(code_page, s, res)?;
    }
    Ok(())
}

fn write_var_list(code_page: CodePage, v: &[Var], res: &mut Vec<u8>) -> Result<(), String> {
    let len = v.len().try_into().map_err(|_| "too big var list".to_string())?;
    res.push(len);
    for v in v {
        v.write(code_page, res)?;
    }
    Ok(())
}

fn write_text(code_page: CodePage, s: &str, res: &mut Vec<u8>) -> Result<(), String> {
    write_str_raw(|len, res| {
        let len = len.try_into().map_err(|_| ())?;
        write_u16(len, res);
        Ok(())
    }, code_page, s, res)
}

fn write_u16(v: u16, res: &mut Vec<u8>) {
    res.push((v & 0xFF) as u8);
    res.push((v >> 8) as u8);
}

fn write_i16(v: i16, res: &mut Vec<u8>) {
    write_u16(v as u16, res);
}

fn write_u32(v: u32, res: &mut Vec<u8>) {
    res.push((v & 0xFF) as u8);
    res.push((v >> 8) as u8);
    res.push((v >> 16) as u8);
    res.push((v >> 24) as u8);
}

impl Var {
    fn write(&self, code_page: CodePage, res: &mut Vec<u8>) -> Result<(), String> {
        match self {
            &Var::Local { ref owner, var_type, index } => {
                if let Some(owner) = owner {
                    res.push(b'r');
                    write_str(code_page, owner, res)?;
                }
                var_type.write(res);
                write_u16(index, res);
            },
            Var::Global { name } => {
                res.push(b'G');
                write_str(code_page, name, res)?;
            }
        }
        Ok(())
    }
}
  
macro_attr! {
    #[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone)]
    #[derive(Debug, N, EnumDisplay!, EnumFromStr!)]
    #[repr(u16)]
    pub enum Func {
        End = 0x0101,
        Set = 0x0105,
        If = 0x0106,
        Else = 0x0107,
        ElseIf = 0x0108,
        EndIf = 0x0109,
        SetRef = 0x010C,
        Return = 0x0124,
        EnableRest = 0x013F,
        MessageBox = 0x1000,
        PlaySound = 0x1002,
        StreamMusic = 0x1003,
        Position = 0x1004,
        PositionCell = 0x1005,
        Move = 0x1006,
        Rotate = 0x1007,
        MoveWorld = 0x1008,
        RotateWorld = 0x1009,
        SetPos = 0x100B,
        SetAngle = 0x100D,
        SetAtStart = 0x1010,
        PlayGroup = 0x1014,
        LoopGroup = 0x1015,
        Activate = 0x1017,
        OnActivate = 0x1018,
        StartCombat = 0x1019,
        StopCombat = 0x101A,
        StartScript = 0x101B,
        StopScript = 0x101C,
        AddTopic = 0x1022,
        SetStrength = 0x1024,
        ModStrength = 0x1025,
        SetIntelligence = 0x1027,
        ModIntelligence = 0x1028,
        SetWillpower = 0x102A,
        ModWillpower = 0x102B,
        SetAgility = 0x102D,
        ModAgility = 0x102E,
        SetSpeed = 0x1030,
        ModSpeed = 0x1031,
        SetEndurance = 0x1033,
        ModEndurance = 0x1034,
        SetPersonality = 0x1036,
        ModPersonality = 0x1037,
        SetLuck = 0x1039,
        ModLuck = 0x103A,
        SetBlock = 0x103C,
        ModBlock = 0x103D,
        SetArmorer = 0x103F,
        ModArmorer = 0x1040,
        SetMediumArmor = 0x1042,
        ModMediumArmor = 0x1043,
        SetHeavyArmor = 0x1045,
        ModHeavyArmor = 0x1046,
        SetBluntWeapon = 0x1048,
        ModBluntWeapon = 0x1049,
        SetLongBlade = 0x104B,
        ModLongBlade = 0x104C,
        SetAxe = 0x104E,
        ModAxe = 0x104F,
        SetSpear = 0x1051,
        ModSpear = 0x1052,
        SetAthletics = 0x1054,
        ModAthletics = 0x1055,
        SetEnchant = 0x1057,
        SetDestruction = 0x105A,
        SetAlteration = 0x105D,
        SetIllusion = 0x1060,
        SetConjuration = 0x1063,
        SetMysticism = 0x1066,
        SetRestoration = 0x1069,
        ModRestoration = 0x106A,
        SetAlchemy = 0x106C,
        SetMarksman = 0x1081,
        ModMarksman = 0x1082,
        ModMercantile = 0x1085,
        SetHealth = 0x108D,
        ModHealth = 0x108E,
        SetMagicka = 0x1090,
        ModMagicka = 0x1091,
        SetFatigue = 0x1093,
        ModFatigue = 0x1094,
        ModReputation = 0x1097,
        SetDisposition = 0x1099,
        ModDisposition = 0x109A,
        SetPCCrimeLevel = 0x109C,
        Journal = 0x10CC,
        RaiseRank = 0x10CE,
        PCRaiseRank = 0x10D0,
        PCClearExpelled = 0x10D3,
        AddItem = 0x10D4,
        RemoveItem = 0x10D5,
        ModPCFacRep = 0x10D9,
        Enable = 0x10DA,
        Disable = 0x10DB,
        EnablePlayerControls = 0x10DD,
        DisablePlayerControls = 0x10DE,
        WakeUpPC = 0x10E1,
        EnablePlayerViewSwitch = 0x10E2,
        DisablePlayerViewSwitch = 0x10E3,
        ShowRestMenu = 0x10E5,
        PlaceAtPC = 0x10E6,
        Resurrect = 0x10E7,
        ForceGreeting = 0x10E8,
        RemoveSoulGem = 0x10EC,
        EnableTeleporting = 0x10EE,
        DisableTeleporting = 0x10EF,
        AiEscort = 0x10F4,
        AiFollow = 0x10F6,
        AiTravel = 0x10F8,
        AiWander = 0x10F9,
        SetFight = 0x1100,
        ModFight = 0x1101,
        SetFlee = 0x1103,
        SetHello = 0x1109,
        Drop = 0x110D,
        ModFactionReaction = 0x1111,
        EnableStatsMenu = 0x1117,
        EnableInventoryMenu = 0x1118,
        EnableMapMenu = 0x1119,
        EnableMagicMenu = 0x111A,
        Say = 0x111B,
        AddSpell = 0x111D,
        RemoveSpell = 0x111E,
        Cast = 0x1123,
        ChangeWeather = 0x1124,
        ModRegion = 0x1125,
        EnableNameMenu = 0x1126,
        EnableRaceMenu = 0x1127,
        EnableClassMenu = 0x1128,
        EnableBirthMenu = 0x1129,
        PlaySoundVP = 0x112B,
        PlaySound3D = 0x112C,
        PlaySound3DVP = 0x112D,
        PlayLoopSound3D = 0x112E,
        PlayLoopSound3DVP = 0x112F,
        FadeOut = 0x1130,
        FadeIn = 0x1131,
        ModCurrentHealth = 0x1132,
        ModCurrentFatigue = 0x1134,
        HurtStandingActor = 0x1135,
        Lock = 0x1136,
        Unlock = 0x1137,
        PCJoinFaction = 0x113B,
        EnablePlayerJumping = 0x113F,
        DisablePlayerJumping = 0x1140,
        EnableVanityMode = 0x114B,
        DisableVanityMode = 0x114C,
        PayFine = 0x114F,
        StopSound = 0x1151,
        ShowMap = 0x1152,
        PlayBink = 0x1155,
        EnablePlayerFighting = 0x1159,
        DisablePlayerFighting = 0x115A,
        EnablePlayerMagic = 0x115C,
        DisablePlayerMagic = 0x115D,
        DontSaveObject = 0x115F,
        EnableStatReviewMenu = 0x1160,
        ForceSneak = 0x1163,
        ClearForceSneak = 0x1164,
        Fall = 0x1166,
    }
}

enum_serde!(Func, "func", as u16, Unsigned, u64);

#[derive(Debug, Clone, Eq, PartialEq, Copy, Ord, PartialOrd, Hash)]
pub enum FuncParams {
    None,
    Byte,
    ByteStr,
    CharFloat,
    Float,
    FloatStr,
    Float3Byte,
    Float3IntListByte,
    Float4,
    Float4Str,
    Int,
    IntByte,
    Int2,
    Str,
    StrByte,
    StrByte8,
    StrFloat2,
    StrInt,
    StrIntFloatInt,
    StrIntFloat3Byte,
    StrInt2,
    StrText,
    Str2,
    Str2Int,
    Text,
    TextVarListStrList,
    VarStr,
}

impl Func {
    pub fn params(self) -> FuncParams {
        match self {
            Func::Activate => FuncParams::None,
            Func::AddItem => FuncParams::StrInt,
            Func::AddSpell => FuncParams::Str,
            Func::AddTopic => FuncParams::Str,
            Func::AiEscort => FuncParams::StrIntFloat3Byte,
            Func::AiFollow => FuncParams::StrIntFloat3Byte,
            Func::AiTravel => FuncParams::Float3Byte,
            Func::AiWander => FuncParams::Float3IntListByte,
            Func::Cast => FuncParams::Str2,
            Func::ChangeWeather => FuncParams::StrInt,
            Func::ClearForceSneak => FuncParams::None,
            Func::Disable => FuncParams::None,
            Func::DisablePlayerControls => FuncParams::None,
            Func::DisablePlayerFighting => FuncParams::None,
            Func::DisablePlayerJumping => FuncParams::None,
            Func::DisablePlayerMagic => FuncParams::None,
            Func::DisablePlayerViewSwitch => FuncParams::None,
            Func::DisableTeleporting => FuncParams::None,
            Func::DisableVanityMode => FuncParams::None,
            Func::DontSaveObject => FuncParams::None,
            Func::Drop => FuncParams::StrInt,
            Func::Else => FuncParams::Byte,
            Func::ElseIf => FuncParams::ByteStr,
            Func::Enable => FuncParams::None,
            Func::EnableBirthMenu => FuncParams::None,
            Func::EnableClassMenu => FuncParams::None,
            Func::EnableInventoryMenu => FuncParams::None,
            Func::EnableMagicMenu => FuncParams::None,
            Func::EnableMapMenu => FuncParams::None,
            Func::EnableNameMenu => FuncParams::None,
            Func::EnablePlayerControls => FuncParams::None,
            Func::EnablePlayerFighting => FuncParams::None,
            Func::EnablePlayerJumping => FuncParams::None,
            Func::EnablePlayerMagic => FuncParams::None,
            Func::EnablePlayerViewSwitch => FuncParams::None,
            Func::EnableRaceMenu => FuncParams::None,
            Func::EnableRest => FuncParams::None,
            Func::EnableStatReviewMenu => FuncParams::None,
            Func::EnableStatsMenu => FuncParams::None,
            Func::EnableTeleporting => FuncParams::None,
            Func::EnableVanityMode => FuncParams::None,
            Func::End => FuncParams::None,
            Func::EndIf => FuncParams::None,
            Func::FadeIn => FuncParams::Float,
            Func::FadeOut => FuncParams::Float,
            Func::Fall => FuncParams::None,
            Func::ForceGreeting => FuncParams::None,
            Func::ForceSneak => FuncParams::None,
            Func::HurtStandingActor => FuncParams::Float,
            Func::If => FuncParams::ByteStr,
            Func::Journal => FuncParams::StrInt2,
            Func::Lock => FuncParams::Int,
            Func::LoopGroup => FuncParams::Int2,
            Func::MessageBox => FuncParams::TextVarListStrList,
            Func::ModAgility => FuncParams::Float,
            Func::ModArmorer => FuncParams::Float,
            Func::ModAthletics => FuncParams::Float,
            Func::ModAxe => FuncParams::Float,
            Func::ModBlock => FuncParams::Float,
            Func::ModBluntWeapon => FuncParams::Float,
            Func::ModCurrentFatigue => FuncParams::Float,
            Func::ModCurrentHealth => FuncParams::Float,
            Func::ModDisposition => FuncParams::Float,
            Func::ModEndurance => FuncParams::Float,
            Func::ModFactionReaction => FuncParams::Str2Int,
            Func::ModFatigue => FuncParams::Float,
            Func::ModFight => FuncParams::Float,
            Func::ModHealth => FuncParams::Float,
            Func::ModHeavyArmor => FuncParams::Float,
            Func::ModIntelligence => FuncParams::Float,
            Func::ModLongBlade => FuncParams::Float,
            Func::ModLuck => FuncParams::Float,
            Func::ModMagicka => FuncParams::Float,
            Func::ModMarksman => FuncParams::Float,
            Func::ModMediumArmor => FuncParams::Float,
            Func::ModMercantile => FuncParams::Float,
            Func::ModPCFacRep => FuncParams::FloatStr,
            Func::ModPersonality => FuncParams::Float,
            Func::ModRegion => FuncParams::StrByte8,
            Func::ModReputation => FuncParams::Float,
            Func::ModRestoration => FuncParams::Float,
            Func::ModSpear => FuncParams::Float,
            Func::ModSpeed => FuncParams::Float,
            Func::ModStrength => FuncParams::Float,
            Func::ModWillpower => FuncParams::Float,
            Func::Move => FuncParams::CharFloat,
            Func::MoveWorld => FuncParams::CharFloat,
            Func::OnActivate => FuncParams::None,
            Func::PayFine => FuncParams::None,
            Func::PCClearExpelled => FuncParams::Str,
            Func::PCJoinFaction => FuncParams::Str,
            Func::PCRaiseRank => FuncParams::Str,
            Func::PlaceAtPC => FuncParams::StrIntFloatInt,
            Func::PlayBink => FuncParams::StrByte,
            Func::PlayGroup => FuncParams::IntByte,
            Func::PlayLoopSound3D => FuncParams::Str,
            Func::PlayLoopSound3DVP => FuncParams::StrFloat2,
            Func::PlaySound => FuncParams::Str,
            Func::PlaySoundVP => FuncParams::StrFloat2,
            Func::PlaySound3D => FuncParams::Str,
            Func::PlaySound3DVP => FuncParams::StrFloat2,
            Func::Position => FuncParams::Float4,
            Func::PositionCell => FuncParams::Float4Str,
            Func::RaiseRank => FuncParams::None,
            Func::RemoveItem => FuncParams::StrInt,
            Func::RemoveSoulGem => FuncParams::Str,
            Func::RemoveSpell => FuncParams::Str,
            Func::Resurrect => FuncParams::None,
            Func::Return => FuncParams::None,
            Func::Rotate => FuncParams::CharFloat,
            Func::RotateWorld => FuncParams::CharFloat,
            Func::Say => FuncParams::StrText,
            Func::Set => FuncParams::VarStr,
            Func::SetAgility => FuncParams::Float,
            Func::SetAlchemy => FuncParams::Float,
            Func::SetAlteration => FuncParams::Float,
            Func::SetAngle => FuncParams::CharFloat,
            Func::SetArmorer => FuncParams::Float,
            Func::SetAthletics => FuncParams::Float,
            Func::SetAtStart => FuncParams::None,
            Func::SetAxe => FuncParams::Float,
            Func::SetBlock => FuncParams::Float,
            Func::SetBluntWeapon => FuncParams::Float,
            Func::SetConjuration => FuncParams::Float,
            Func::SetDestruction => FuncParams::Float,
            Func::SetDisposition => FuncParams::Float,
            Func::SetEnchant => FuncParams::Float,
            Func::SetEndurance => FuncParams::Float,
            Func::SetFatigue => FuncParams::Float,
            Func::SetFight => FuncParams::Float,
            Func::SetFlee => FuncParams::Float,
            Func::SetHealth => FuncParams::Float,
            Func::SetHeavyArmor => FuncParams::Float,
            Func::SetHello => FuncParams::Float,
            Func::SetLongBlade => FuncParams::Float,
            Func::SetLuck => FuncParams::Float,
            Func::SetMagicka => FuncParams::Float,
            Func::SetMarksman => FuncParams::Float,
            Func::SetMysticism => FuncParams::Float,
            Func::SetPCCrimeLevel => FuncParams::Float,
            Func::SetPos => FuncParams::CharFloat,
            Func::SetRef => FuncParams::Str,
            Func::SetRestoration => FuncParams::Float,
            Func::SetSpear => FuncParams::Float,
            Func::SetSpeed => FuncParams::Float,
            Func::SetStrength => FuncParams::Float,
            Func::SetMediumArmor => FuncParams::Float,
            Func::SetIllusion => FuncParams::Float,
            Func::SetIntelligence => FuncParams::Float,
            Func::SetWillpower => FuncParams::Float,
            Func::SetPersonality => FuncParams::Float,
            Func::ShowMap => FuncParams::Str,
            Func::ShowRestMenu => FuncParams::None,
            Func::StartCombat => FuncParams::Str,
            Func::StartScript => FuncParams::Str,
            Func::StopCombat => FuncParams::None,
            Func::StopScript => FuncParams::Str,
            Func::StopSound => FuncParams::Str,
            Func::StreamMusic => FuncParams::Str,
            Func::Unlock => FuncParams::None,
            Func::WakeUpPC => FuncParams::None,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FuncArgs {
    None,
    Byte(u8),
    ByteStr(u8, String),
    CharFloat(String, Float),
    Float(Float),
    FloatStr(Float, String),
    Float3Byte([Float; 3], u8),
    Float3IntListByte([Float; 3], Vec<i16>, u8),
    Float4([Float; 4]),
    Float4Str([Float; 4], String),
    Int(i16),
    IntByte(i16, u8),
    Int2([i16; 2]),
    Str(String),
    StrByte(String, u8),
    StrByte8(String, [u8; 8]),
    StrFloat2(String, [Float; 2]),
    StrInt(String, i16),
    StrIntFloatInt(String, i16, Float, i16),
    StrIntFloat3Byte(String, i16, [Float; 3], u8),
    StrInt2(String, [i16; 2]),
    StrText(String, String),
    Str2([String; 2]),
    Str2Int([String; 2], i16),
    Text(String),
    TextVarListStrList(String, Vec<Var>, Vec<String>),
    VarStr(Var, String),
}

#[derive(Clone)]
pub struct FuncArgsSerde {
    pub params: FuncParams
}

impl SerializeSeed for FuncArgsSerde {
    type Value = FuncArgs;

    fn serialize<S: Serializer>(&self, value: &Self::Value, serializer: S) -> Result<S::Ok, S::Error> {
        if self.params != value.params() {
            return Err(S::Error::custom("stmt args params mismatch"));
        }
        match value {
            FuncArgs::None => ().serialize(serializer),
            FuncArgs::Byte(a1) => a1.serialize(serializer),
            FuncArgs::ByteStr(a1, a2) => (a1, a2).serialize(serializer),
            FuncArgs::CharFloat(a1, a2) => (a1, a2).serialize(serializer),
            FuncArgs::Float(a1) => a1.serialize(serializer),
            FuncArgs::FloatStr(a1, a2) => (a1, a2).serialize(serializer),
            FuncArgs::Float3Byte(a1, a2) => (a1, a2).serialize(serializer),
            FuncArgs::Float4(a1) => a1.serialize(serializer),
            FuncArgs::Float4Str(a1, a2) => (a1, a2).serialize(serializer),
            FuncArgs::Float3IntListByte(a1, a2, a3) => (a1, a2, a3).serialize(serializer),
            FuncArgs::Int(a1) => a1.serialize(serializer),
            FuncArgs::IntByte(a1, a2) => (a1, a2).serialize(serializer),
            FuncArgs::Int2(a1) => a1.serialize(serializer),
            FuncArgs::Str(a1) => a1.serialize(serializer),
            FuncArgs::StrByte(a1, a2) => (a1, a2).serialize(serializer),
            FuncArgs::StrByte8(a1, a2) => (a1, a2).serialize(serializer),
            FuncArgs::StrFloat2(a1, a2) => (a1, a2).serialize(serializer),
            FuncArgs::StrInt(a1, a2) => (a1, a2).serialize(serializer),
            FuncArgs::StrIntFloatInt(a1, a2, a3, a4) => (a1, a2, a3, a4).serialize(serializer),
            FuncArgs::StrIntFloat3Byte(a1, a2, a3, a4) => (a1, a2, a3, a4).serialize(serializer),
            FuncArgs::StrInt2(a1, a2) => (a1, a2).serialize(serializer),
            FuncArgs::StrText(a1, a2) => (a1, a2).serialize(serializer),
            FuncArgs::Str2(a1) => a1.serialize(serializer),
            FuncArgs::Str2Int(a1, a2) => (a1, a2).serialize(serializer),
            FuncArgs::Text(a1) => a1.serialize(serializer),
            FuncArgs::TextVarListStrList(a1, a2, a3) => (a1, a2, a3).serialize(serializer),
            FuncArgs::VarStr(a1, a2) => (a1, a2).serialize(serializer),
        }
    }
}

impl<'de> DeserializeSeed<'de> for FuncArgsSerde {
    type Value = FuncArgs;

    fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
        Ok(match self.params {
            FuncParams::None => { <()>::deserialize(deserializer)?; FuncArgs::None },
            FuncParams::Byte => { let a1 = u8::deserialize(deserializer)?; FuncArgs::Byte(a1) },
            FuncParams::ByteStr => { let (a1, a2) = <(u8, String)>::deserialize(deserializer)?; FuncArgs::ByteStr(a1, a2) },
            FuncParams::CharFloat => {
                let (a1, a2) = <(String, Float)>::deserialize(deserializer)?;
                FuncArgs::CharFloat(a1, a2)
            },
            FuncParams::Float => { let a1 = Float::deserialize(deserializer)?; FuncArgs::Float(a1) },
            FuncParams::FloatStr => { let (a1, a2) = <(Float, String)>::deserialize(deserializer)?; FuncArgs::FloatStr(a1, a2) },
            FuncParams::Float3Byte => {
                let (a1, a2) = <([Float; 3], u8)>::deserialize(deserializer)?;
                FuncArgs::Float3Byte(a1, a2)
            },
            FuncParams::Float3IntListByte => {
                let (a1, a2, a3) = <([Float; 3], Vec<i16>, u8)>::deserialize(deserializer)?;
                FuncArgs::Float3IntListByte(a1, a2, a3)
            },
            FuncParams::Float4 => { let a1 = <[Float; 4]>::deserialize(deserializer)?; FuncArgs::Float4(a1) },
            FuncParams::Float4Str => {
                let (a1, a2) = <([Float; 4], String)>::deserialize(deserializer)?;
                FuncArgs::Float4Str(a1, a2)
            },
            FuncParams::Int => { let a1 = i16::deserialize(deserializer)?; FuncArgs::Int(a1) },
            FuncParams::IntByte => { let (a1, a2) = <(i16, u8)>::deserialize(deserializer)?; FuncArgs::IntByte(a1, a2) },
            FuncParams::Int2 => { let a1 = <[i16; 2]>::deserialize(deserializer)?; FuncArgs::Int2(a1) },
            FuncParams::Str => { let a1 = String::deserialize(deserializer)?; FuncArgs::Str(a1) },
            FuncParams::StrByte => { let (a1, a2) = <(String, u8)>::deserialize(deserializer)?; FuncArgs::StrByte(a1, a2) },
            FuncParams::StrByte8 => { let (a1, a2) = <(String, [u8; 8])>::deserialize(deserializer)?; FuncArgs::StrByte8(a1, a2) },
            FuncParams::StrFloat2 => {
                let (a1, a2) = <(String, [Float; 2])>::deserialize(deserializer)?; FuncArgs::StrFloat2(a1, a2)
            },
            FuncParams::StrInt => { let (a1, a2) = <(String, i16)>::deserialize(deserializer)?; FuncArgs::StrInt(a1, a2) },
            FuncParams::StrIntFloatInt => {
                let (a1, a2, a3, a4) = <(String, i16, Float, i16)>::deserialize(deserializer)?;
                FuncArgs::StrIntFloatInt(a1, a2, a3, a4)
            },
            FuncParams::StrIntFloat3Byte => {
                let (a1, a2, a3, a4) = <(String, i16, [Float; 3], u8)>::deserialize(deserializer)?;
                FuncArgs::StrIntFloat3Byte(a1, a2, a3, a4)
            },
            FuncParams::StrInt2 => {
                let (a1, a2) = <(String, [i16; 2])>::deserialize(deserializer)?; FuncArgs::StrInt2(a1, a2)
            },
            FuncParams::StrText => { let (a1, a2) = <(String, String)>::deserialize(deserializer)?; FuncArgs::StrText(a1, a2) },
            FuncParams::Str2 => { let a1 = <[String; 2]>::deserialize(deserializer)?; FuncArgs::Str2(a1) },
            FuncParams::Str2Int => { let (a1, a2) = <([String; 2], i16)>::deserialize(deserializer)?; FuncArgs::Str2Int(a1, a2) },
            FuncParams::Text => { let a1 = String::deserialize(deserializer)?; FuncArgs::Text(a1) },
            FuncParams::TextVarListStrList => {
                let (a1, a2, a3) = <(String, Vec<Var>, Vec<String>)>::deserialize(deserializer)?;
                FuncArgs::TextVarListStrList(a1, a2, a3)
            },
            FuncParams::VarStr => { let (a1, a2) = <(Var, String)>::deserialize(deserializer)?; FuncArgs::VarStr(a1, a2) },
        })
    }
}

impl FuncArgs {
    pub fn params(&self) -> FuncParams {
        match self {
            FuncArgs::None => FuncParams::None,
            FuncArgs::Byte(..) => FuncParams::Byte,
            FuncArgs::ByteStr(..) => FuncParams::ByteStr,
            FuncArgs::CharFloat(..) => FuncParams::CharFloat,
            FuncArgs::Float(..) => FuncParams::Float,
            FuncArgs::FloatStr(..) => FuncParams::FloatStr,
            FuncArgs::Float3Byte(..) => FuncParams::Float3Byte,
            FuncArgs::Float4(..) => FuncParams::Float4,
            FuncArgs::Float4Str(..) => FuncParams::Float4Str,
            FuncArgs::Float3IntListByte(..) => FuncParams::Float3IntListByte,
            FuncArgs::Int(..) => FuncParams::Int,
            FuncArgs::IntByte(..) => FuncParams::IntByte,
            FuncArgs::Int2(..) => FuncParams::Int2,
            FuncArgs::Str(..) => FuncParams::Str,
            FuncArgs::StrByte(..) => FuncParams::StrByte,
            FuncArgs::StrByte8(..) => FuncParams::StrByte8,
            FuncArgs::StrFloat2(..) => FuncParams::StrFloat2,
            FuncArgs::StrInt(..) => FuncParams::StrInt,
            FuncArgs::StrIntFloatInt(..) => FuncParams::StrIntFloatInt,
            FuncArgs::StrIntFloat3Byte(..) => FuncParams::StrIntFloat3Byte,
            FuncArgs::StrInt2(..) => FuncParams::StrInt2,
            FuncArgs::StrText(..) => FuncParams::StrText,
            FuncArgs::Str2(..) => FuncParams::Str2,
            FuncArgs::Str2Int(..) => FuncParams::Str2Int,
            FuncArgs::Text(..) => FuncParams::Text,
            FuncArgs::TextVarListStrList(..) => FuncParams::TextVarListStrList,
            FuncArgs::VarStr(..) => FuncParams::VarStr,
        }
    }

    fn write(&self, code_page: CodePage, res: &mut Vec<u8>) -> Result<(), String> {
        match self {
            FuncArgs::None => { },
            FuncArgs::Byte(a1) => res.push(*a1),
            FuncArgs::ByteStr(a1, a2) => {
                res.push(*a1);
                write_str(code_page, a2, res)?;
            },
            FuncArgs::CharFloat(a1, a2) => {
                write_char(code_page, a1, res)?;
                a2.write(res)?;
            },
            FuncArgs::Float(a1) => a1.write(res)?,
            FuncArgs::FloatStr(a1, a2) => {
                a1.write(res)?;
                write_str(code_page, a2, res)?;
            },
            FuncArgs::Float3Byte([a1_1, a1_2, a1_3], a2) => {
                a1_1.write(res)?;
                a1_2.write(res)?;
                a1_3.write(res)?;
                res.push(*a2);
            },
            FuncArgs::Float3IntListByte([a1_1, a1_2, a1_3], a2, a3) => {
                a1_1.write(res)?;
                a1_2.write(res)?;
                a1_3.write(res)?;
                write_int_list(a2, res)?;
                res.push(*a3);
            },
            FuncArgs::Float4([a1_1, a1_2, a1_3, a1_4]) => {
                a1_1.write(res)?;
                a1_2.write(res)?;
                a1_3.write(res)?;
                a1_4.write(res)?;
            },
            FuncArgs::Float4Str([a1_1, a1_2, a1_3, a1_4], a2) => {
                a1_1.write(res)?;
                a1_2.write(res)?;
                a1_3.write(res)?;
                a1_4.write(res)?;
                write_str(code_page, a2, res)?;
            },
            FuncArgs::Int(a1) => write_i16(*a1, res),
            FuncArgs::IntByte(a1, a2) => {
                write_i16(*a1, res);
                res.push(*a2);
            },
            FuncArgs::Int2([a1_1, a1_2]) => {
                write_i16(*a1_1, res);
                write_i16(*a1_2, res);
            },
            FuncArgs::Str(a1) => write_str(code_page, a1, res)?,
            FuncArgs::StrByte(a1, a2) => {
                write_str(code_page, a1, res)?;
                res.push(*a2);
            },
            FuncArgs::StrByte8(a1, [a2_1, a2_2, a2_3, a2_4, a2_5, a2_6, a2_7, a2_8]) => {
                write_str(code_page, a1, res)?;
                res.push(*a2_1);
                res.push(*a2_2);
                res.push(*a2_3);
                res.push(*a2_4);
                res.push(*a2_5);
                res.push(*a2_6);
                res.push(*a2_7);
                res.push(*a2_8);
            },
            FuncArgs::StrFloat2(a1, [a2_1, a2_2]) => {
                write_str(code_page, a1, res)?;
                a2_1.write(res)?;
                a2_2.write(res)?;
            },
            FuncArgs::StrInt(a1, a2) => {
                write_str(code_page, a1, res)?;
                write_i16(*a2, res);
            },
            FuncArgs::StrIntFloatInt(a1, a2, a3, a4) => {
                write_str(code_page, a1, res)?;
                write_i16(*a2, res);
                a3.write(res)?;
                write_i16(*a4, res);
            },
            FuncArgs::StrIntFloat3Byte(a1, a2, [a3_1, a3_2, a3_3], a4) => {
                write_str(code_page, a1, res)?;
                write_i16(*a2, res);
                a3_1.write(res)?;
                a3_2.write(res)?;
                a3_3.write(res)?;
                res.push(*a4);
            },
            FuncArgs::StrInt2(a1, [a2_1, a2_2]) => {
                write_str(code_page, a1, res)?;
                write_i16(*a2_1, res);
                write_i16(*a2_2, res);
            },
            FuncArgs::StrText(a1, a2) => {
                write_str(code_page, a1, res)?;
                write_text(code_page, a2, res)?;
            },
            FuncArgs::Str2([a1_1, a1_2]) => {
                write_str(code_page, a1_1, res)?;
                write_str(code_page, a1_2, res)?;
            },
            FuncArgs::Str2Int([a1_1, a1_2], a2) => {
                write_str(code_page, a1_1, res)?;
                write_str(code_page, a1_2, res)?;
                write_i16(*a2, res);
            },
            FuncArgs::Text(a1) => write_text(code_page, a1, res)?,
            FuncArgs::TextVarListStrList(a1, a2, a3) => {
                write_text(code_page, a1, res)?;
                write_var_list(code_page, a2, res)?;
                write_str_list(code_page, a3, res)?;
            },
            FuncArgs::VarStr(a1, a2) => {
                a1.write(code_page, res)?;
                write_str(code_page, a2, res)?;
            },
        }
        Ok(())
    }
}

mod parser {
    use super::*;
    use nom_errors::*;
    use nom_errors::bytes::*;

    fn float(input: &[u8]) -> NomRes<&[u8], Float, (), !> {
        map(le_u32(), |bits| {
            if let Some(var_type) = VarType::n((bits & 0xFF) as u8) {
                if (bits >> 24) != 0 {
                    None
                } else {
                    Some(Float::Var { var_type, index: ((bits >> 8) & 0xFFFF) as u16 })
                }
            } else { None }.unwrap_or(Float::Val { val: f32::from_bits(bits) })
        })(input)
    }

    fn string<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], String, (), !> {
        map(flat_map(le_u8(), |len| take(len.into())), move |x| code_page.decode(x))
    }

    fn str_list<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], Vec<String>, (), !> {
        flat_map(le_u8(), move |len| count(string(code_page), len.into()))
    }

    fn int_list(input: &[u8]) -> NomRes<&[u8], Vec<i16>, (), !> {
        flat_map(le_u16(), move |len| count(le_i16(), len.into()))(input)
    }

    fn var_list<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], Vec<Var>, (), !> {
        flat_map(le_u8(), move |len| count(var(code_page), len.into()))
    }

    fn text<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], String, (), !> {
        map(flat_map(le_u16(), |len| take(len.into())), move |x| code_page.decode(x))
    }

    fn ch<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], String, (), !> {
        map(take(1), move |x| code_page.decode(x))
    }

    fn var_type(input: &[u8]) -> NomRes<&[u8], VarType, (), !> {
        map_res(le_u8(), |x| VarType::n(x).ok_or(()))(input)
    }

    fn local_var(input: &[u8]) -> NomRes<&[u8], (VarType, u16), (), !> {
        map(seq_2(var_type, le_u16()), |(var_type, index)| (var_type, index))(input)
    }

    fn owner_var<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], (String, VarType, u16), (), !> {
        map(seq_3(tag([b'r']), string(code_page), local_var), |(_, owner, var)| (owner, var.0, var.1))
    }

    fn global_var<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], String, (), !> {
        map(seq_2(tag([b'G']), string(code_page)), |(_, var)| var)
    }

    fn var<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], Var, (), !> {
        alt_3(
            map(owner_var(code_page), |(owner, var_type, index)| Var::Local { owner: Some(owner), var_type, index }),
            map(local_var, |(var_type, index)| Var::Local { owner: None, var_type, index }),
            map(global_var(code_page), |name| Var::Global { name })
        )
    }

    fn byte_args(input: &[u8]) -> NomRes<&[u8], FuncArgs, (), !> {
        map(le_u8(), FuncArgs::Byte)(input)
    }

    fn byte_str_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(
            seq_2(
                le_u8(),
                string(code_page)
            ),
            |(a1, a2)| FuncArgs::ByteStr(a1, a2)
        )
    }

    fn char_float_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(seq_2(ch(code_page), float), |(a1, a2)| FuncArgs::CharFloat(a1, a2))
    }

    fn float_args(input: &[u8]) -> NomRes<&[u8], FuncArgs, (), !> {
        map(float, FuncArgs::Float)(input)
    }

    fn float_str_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(
            seq_2(float, string(code_page)),
            |(a1, a2)| FuncArgs::FloatStr(a1, a2)
        )
    }

    fn float_3_byte_args(input: &[u8]) -> NomRes<&[u8], FuncArgs, (), !> {
        map(
            seq_4(float, float, float, le_u8()),
            |(a1_1, a1_2, a1_3, a2)| FuncArgs::Float3Byte([a1_1, a1_2, a1_3], a2)
        )(input)
    }

    fn float_3_int_list_byte_args(input: &[u8]) -> NomRes<&[u8], FuncArgs, (), !> {
        map(
            seq_3(seq_3(float, float, float), int_list, le_u8()),
            |((a1_1, a1_2, a1_3), a2, a3)| FuncArgs::Float3IntListByte(
                [a1_1, a1_2, a1_3], a2, a3
            )
        )(input)
    }

    fn float_4_args(input: &[u8]) -> NomRes<&[u8], FuncArgs, (), !> {
        map(
            seq_4(float, float, float, float),
            |(a1_1, a1_2, a1_3, a1_4)| FuncArgs::Float4([a1_1, a1_2, a1_3, a1_4])
        )(input)
    }

    fn float_4_str_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(
            seq_5(float, float, float, float, string(code_page)),
            |(a1_1, a1_2, a1_3, a1_4, a2)| FuncArgs::Float4Str([a1_1, a1_2, a1_3, a1_4], a2)
        )
    }

    fn int_args(input: &[u8]) -> NomRes<&[u8], FuncArgs, (), !> {
        map(le_i16(), FuncArgs::Int)(input)
    }

    fn int_byte_args(input: &[u8]) -> NomRes<&[u8], FuncArgs, (), !> {
        map(seq_2(le_i16(), le_u8()), |(a1, a2)| FuncArgs::IntByte(a1, a2))(input)
    }

    fn int_2_args(input: &[u8]) -> NomRes<&[u8], FuncArgs, (), !> {
        map(seq_2(le_i16(), le_i16()), |(a1_1, a1_2)| FuncArgs::Int2([a1_1, a1_2]))(input)
    }

    fn str_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(string(code_page), FuncArgs::Str)
    }

    fn str_byte_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(
            seq_2(
                string(code_page),
                le_u8()
            ),
            |(a1, a2)| FuncArgs::StrByte(a1, a2)
        )
    }

    fn str_byte_8_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(
            seq_2(
                string(code_page),
                seq_8(le_u8(), le_u8(), le_u8(), le_u8(), le_u8(), le_u8(), le_u8(), le_u8())
            ),
            |(a1, (a2_1, a2_2, a2_3, a2_4, a2_5, a2_6, a2_7, a2_8))| FuncArgs::StrByte8(
                a1, [a2_1, a2_2, a2_3, a2_4, a2_5, a2_6, a2_7, a2_8]
            )
        )
    }

    fn str_float_2_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(
            seq_2(
                string(code_page),
                seq_2(float, float)
            ),
            |(a1, (a2_1, a2_2))| FuncArgs::StrFloat2(a1, [a2_1, a2_2])
        )
    }

    fn str_int_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(seq_2(string(code_page), le_i16()), |(a1, a2)| FuncArgs::StrInt(a1, a2))
    }

    fn str_int_float_int_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(
            seq_4(string(code_page), le_i16(), float, le_i16()),
            |(a1, a2, a3, a4)| FuncArgs::StrIntFloatInt(a1, a2, a3, a4)
        )
    }

    fn str_int_float_3_byte_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(
            seq_6(string(code_page), le_i16(), float, float, float, le_u8()),
            |(a1, a2, a3_1, a3_2, a3_3, a4)| FuncArgs::StrIntFloat3Byte(a1, a2, [a3_1, a3_2, a3_3], a4)
        )
    }

    fn str_int_2_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(
            seq_3(string(code_page), le_i16(), le_i16()),
            |(a1, a2_1, a2_2)| FuncArgs::StrInt2(a1, [a2_1, a2_2])
        )
    }

    fn str_text_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(seq_2(string(code_page), text(code_page)), |(a1, a2)| FuncArgs::StrText(a1, a2))
    }

    fn str_2_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(seq_2(string(code_page), string(code_page)), |(a1_1, a1_2)| FuncArgs::Str2([a1_1, a1_2]))
    }

    fn str_2_int_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(seq_3(string(code_page), string(code_page), le_i16()), |(a1_1, a1_2, a2)| FuncArgs::Str2Int([a1_1, a1_2], a2))
    }

    fn text_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(text(code_page), FuncArgs::Text)
    }

    fn text_var_list_str_list_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(
            seq_3(text(code_page), var_list(code_page), str_list(code_page)),
            |(a1, a2, a3)| FuncArgs::TextVarListStrList(a1, a2, a3)
        )
    }

    fn var_str_args<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        map(
            seq_2(
                var(code_page),
                string(code_page)
            ),
            |(a1, a2)| FuncArgs::VarStr(a1, a2)
        )
    }

    fn func_args<'a>(code_page: CodePage, func: Func) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], FuncArgs, (), !> {
        move |input| {
            match func.params() {
                FuncParams::None => Ok((input, FuncArgs::None)),
                FuncParams::Byte => byte_args(input),
                FuncParams::ByteStr => byte_str_args(code_page)(input),
                FuncParams::CharFloat => char_float_args(code_page)(input),
                FuncParams::Float => float_args(input),
                FuncParams::FloatStr => float_str_args(code_page)(input),
                FuncParams::Float3Byte => float_3_byte_args(input),
                FuncParams::Float3IntListByte => float_3_int_list_byte_args(input),
                FuncParams::Float4 => float_4_args(input),
                FuncParams::Float4Str => float_4_str_args(code_page)(input),
                FuncParams::Int => int_args(input),
                FuncParams::IntByte => int_byte_args(input),
                FuncParams::Int2 => int_2_args(input),
                FuncParams::Str => str_args(code_page)(input),
                FuncParams::StrByte => str_byte_args(code_page)(input),
                FuncParams::StrByte8 => str_byte_8_args(code_page)(input),
                FuncParams::StrFloat2 => str_float_2_args(code_page)(input),
                FuncParams::StrInt => str_int_args(code_page)(input),
                FuncParams::StrIntFloatInt => str_int_float_int_args(code_page)(input),
                FuncParams::StrIntFloat3Byte => str_int_float_3_byte_args(code_page)(input),
                FuncParams::StrInt2 => str_int_2_args(code_page)(input),
                FuncParams::StrText => str_text_args(code_page)(input),
                FuncParams::Str2 => str_2_args(code_page)(input),
                FuncParams::Str2Int => str_2_int_args(code_page)(input),
                FuncParams::Text => text_args(code_page)(input),
                FuncParams::TextVarListStrList => text_var_list_str_list_args(code_page)(input),
                FuncParams::VarStr => var_str_args(code_page)(input),
            }
        }
    }

    fn func(input: &[u8]) -> NomRes<&[u8], Func, (), !> {
        map_res(le_u16(), |func| Func::n(func).ok_or(()))(input)
    }

    pub fn stmt<'a>(code_page: CodePage) -> impl FnMut(&'a [u8]) -> NomRes<&'a [u8], Stmt, (), !> {
        flat_map(func, move |func| map(func_args(code_page, func), move |args| Stmt { func, args }))
    }

    pub fn stmts(code_page: CodePage, input: &[u8]) -> (&[u8], Vec<Stmt>) {
        result_from_parser(many0(stmt(code_page))(input)).unwrap_or_else(|x| match x {
            NomErr::Error(x) => x,
            NomErr::Failure(x) => x
        })
    }
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Stmt {
    pub func: Func,
    pub args: FuncArgs,
}

impl Stmt {
    fn write(&self, code_page: CodePage, res: &mut Vec<u8>) -> Result<(), String> {
        write_u16(self.func as u16, res);
        self.args.write(code_page, res)?;
        Ok(())
    }
}

const STMT_FUNC_FIELD: &str = name_of!(func in Stmt);
const STMT_ARGS_FIELD: &str = name_of!(args in Stmt);

const TAGGED_STMT_FIELDS: &[&str] = &[
    STMT_ARGS_FIELD,
];

struct TaggedStmt<'a>(&'a Stmt);

struct TaggedStmtBuf(Stmt);

struct TaggedStmtDe(Func);

impl<'a> Serialize for TaggedStmt<'a> {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut serializer = serializer.serialize_struct(name_of!(type Stmt), 1)?;
        serializer.serialize_field(STMT_ARGS_FIELD, &ValueWithSeed(&self.0.args, FuncArgsSerde { params: self.0.func.params() }))?;
        serializer.end()
    }
}

enum TaggedStmtField {
    Args
}

struct TaggedStmtFieldDeVisitor;

impl<'de> de::Visitor<'de> for TaggedStmtFieldDeVisitor {
    type Value = TaggedStmtField;

    fn expecting(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "stmt field")
    }

    fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
        match value {
            STMT_ARGS_FIELD => Ok(TaggedStmtField::Args),
            x => Err(E::unknown_field(x, TAGGED_STMT_FIELDS)),
        }
    }
}

impl<'de> de::Deserialize<'de> for TaggedStmtField {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        deserializer.deserialize_identifier(TaggedStmtFieldDeVisitor)
    }
}

struct TaggedStmtDeVisitor(Func);

impl<'de> de::Visitor<'de> for TaggedStmtDeVisitor {
    type Value = TaggedStmtBuf;

    fn expecting(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "stmt")
    }

    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> where A: de::MapAccess<'de> {
        let mut args = None;
        while let Some(TaggedStmtField::Args) = map.next_key()? {
            if args.replace(map.next_value_seed(FuncArgsSerde { params: self.0.params() })?).is_some() {
                return Err(A::Error::duplicate_field(STMT_ARGS_FIELD));
            }
        }
        let args = args.ok_or_else(|| A::Error::missing_field(STMT_ARGS_FIELD))?;
        Ok(TaggedStmtBuf(Stmt { func: self.0, args }))
    }

    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> where A: de::SeqAccess<'de> {
        let args = seq.next_element_seed(FuncArgsSerde { params: self.0.params() })?
            .ok_or_else(|| A::Error::invalid_length(0, &self))?;
        Ok(TaggedStmtBuf(Stmt { func: self.0, args }))
    }
}

impl<'de> DeserializeSeed<'de> for TaggedStmtDe {
    type Value = TaggedStmtBuf;

    fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
        deserializer.deserialize_struct(name_of!(type Stmt), TAGGED_STMT_FIELDS, TaggedStmtDeVisitor(self.0))
    }
}

impl Serialize for Stmt {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serde_tagged::ser::internal::serialize(
            serializer,
            STMT_FUNC_FIELD,
            &self.func,
            &TaggedStmt(self)
        )
    }
}

struct TaggedStmtDeSeedFactory;

impl<'de> serde_tagged::de::SeedFactory<'de, Func> for TaggedStmtDeSeedFactory {
    type Value = TaggedStmtBuf;
    type Seed = TaggedStmtDe;

    fn seed<E: de::Error>(self, tag: Func) -> Result<Self::Seed, E> {
        Ok(TaggedStmtDe(tag))
    }
}

impl<'de> Deserialize<'de> for Stmt {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        serde_tagged::de::internal::deserialize(
            deserializer,
            STMT_FUNC_FIELD,
            TaggedStmtDeSeedFactory,
        ).map(|x| x.0)
    }
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ScriptData {
    pub stmts: Vec<Stmt>,
    pub raw: Vec<u8>,
}

impl ScriptData {
    pub fn to_bytes(&self, code_page: CodePage) -> Result<Vec<u8>, String> {
        let mut bytes = Vec::new();
        for stmt in &self.stmts {
            stmt.write(code_page, &mut bytes)?;
        }
        if parser::stmt(code_page)(&self.raw).is_ok() {
            return Err("known func in raw bytes".into());
        }
        bytes.extend_from_slice(&self.raw);
        Ok(bytes)
    }

    pub fn from_bytes(code_page: CodePage, bytes: &[u8]) -> ScriptData {
        let (bytes, stmts) = parser::stmts(code_page, bytes);
        ScriptData { stmts, raw: bytes.into() }
    }
}

#[derive(Clone)]
pub struct ScriptDataSerde {
    pub code_page: Option<CodePage>,
}

const SCRIPT_DATA_STMTS_FIELD: &str = name_of!(stmts in ScriptData);
const SCRIPT_DATA_RAW_FIELD: &str = name_of!(raw in ScriptData);

const SCRIPT_DATA_FIELDS: &[&str] = &[
    SCRIPT_DATA_STMTS_FIELD,
    SCRIPT_DATA_RAW_FIELD,
];

impl SerializeSeed for ScriptDataSerde {
    type Value = ScriptData;

    fn serialize<S: Serializer>(&self, value: &Self::Value, serializer: S) -> Result<S::Ok, S::Error> {
        if serializer.is_human_readable() {
            let mut serializer = serializer.serialize_struct(name_of!(type ScriptData), 2)?;
            serializer.serialize_field(SCRIPT_DATA_STMTS_FIELD, &value.stmts)?;
            serializer.serialize_field(SCRIPT_DATA_RAW_FIELD, &ValueWithSeed(&value.raw[..], HexDump))?;
            serializer.end()
        } else {
            let Some(code_page) = self.code_page else {
                return Err(S::Error::custom("code page required for binary serialization"));
            };
            value.to_bytes(code_page).map_err(S::Error::custom)?.serialize(serializer)
        }
    }
}

enum ScriptDataField {
    Stmts,
    Raw,
}

struct ScriptDataFieldDeVisitor;

impl<'de> de::Visitor<'de> for ScriptDataFieldDeVisitor {
    type Value = ScriptDataField;

    fn expecting(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "script data field")
    }

    fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
        match value {
            SCRIPT_DATA_STMTS_FIELD => Ok(ScriptDataField::Stmts),
            SCRIPT_DATA_RAW_FIELD => Ok(ScriptDataField::Raw),
            x => Err(E::unknown_field(x, SCRIPT_DATA_FIELDS)),
        }
    }
}

impl<'de> de::Deserialize<'de> for ScriptDataField {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        deserializer.deserialize_identifier(ScriptDataFieldDeVisitor)
    }
}

struct ScriptDataDeVisitor;

impl<'de> de::Visitor<'de> for ScriptDataDeVisitor {
    type Value = ScriptData;

    fn expecting(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "script data")
    }

    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> where A: de::MapAccess<'de> {
        let mut stmts = None;
        let mut raw = None;
        while let Some(field) = map.next_key()? {
            match field {
                ScriptDataField::Stmts =>
                    if stmts.replace(map.next_value()?).is_some() {
                        return Err(A::Error::duplicate_field(SCRIPT_DATA_STMTS_FIELD));
                    },
                ScriptDataField::Raw =>
                    if raw.replace(map.next_value_seed(HexDump)?).is_some() {
                        return Err(A::Error::duplicate_field(SCRIPT_DATA_RAW_FIELD));
                    },
            }
        }
        let stmts = stmts.ok_or_else(|| A::Error::missing_field(SCRIPT_DATA_STMTS_FIELD))?;
        let raw = raw.ok_or_else(|| A::Error::missing_field(SCRIPT_DATA_RAW_FIELD))?;
        Ok(ScriptData { stmts, raw })
    }

    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> where A: de::SeqAccess<'de> {
        let stmts = seq.next_element()?
            .ok_or_else(|| A::Error::invalid_length(0, &self))?;
        let raw = seq.next_element_seed(HexDump)?
            .ok_or_else(|| A::Error::invalid_length(1, &self))?;
        Ok(ScriptData { stmts, raw })
    }
}

impl<'de> DeserializeSeed<'de> for ScriptDataSerde {
    type Value = ScriptData;

    fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
        if deserializer.is_human_readable() {
            deserializer.deserialize_struct(name_of!(type ScriptData), SCRIPT_DATA_FIELDS, ScriptDataDeVisitor)
        } else {
            let Some(code_page) = self.code_page else {
                return Err(D::Error::custom("code page required for binary deserialization"));
            };
            Ok(ScriptData::from_bytes(code_page, &<Vec<u8>>::deserialize(deserializer)?))
        }
    }
}