Skip to main content

uesave/
lib.rs

1/*!
2A library for reading and writing Unreal Engine save files (commonly referred to
3as GVAS).
4
5It has been tested on an extensive set of object structures and can fully read
6and write Deep Rock Galactic save files (and likely a lot more).
7
8There is a small binary utility to quickly convert saves to and from a plain
9text JSON format which can be used for manual save editing.
10
11# Example
12
13```
14use std::fs::File;
15
16use uesave::{Property, Save};
17
18let save = Save::read(&mut File::open("drg-save-test.sav")?)?;
19match save.root.properties["NumberOfGamesPlayed"] {
20    Property::Int(value) => {
21        assert_eq!(2173, value);
22    }
23    _ => {}
24}
25# Ok::<(), Box<dyn std::error::Error>>(())
26
27```
28*/
29
30mod archive;
31mod context;
32mod error;
33mod serialization;
34
35#[cfg(test)]
36mod tests;
37
38pub use archive::{ArchiveReader, ArchiveType, ArchiveWriter, SaveGameArchiveType};
39pub use context::{PropertySchemas, Scope, Types};
40pub use error::{Error, ParseError};
41
42use byteorder::{ReadBytesExt, WriteBytesExt, LE};
43use context::SaveGameArchive;
44use std::{
45    borrow::Cow,
46    cell::RefCell,
47    io::{Cursor, Read, Seek, Write},
48    rc::Rc,
49};
50
51use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
52
53#[cfg(feature = "tracing")]
54use tracing::instrument;
55
56type Result<T, E = Error> = std::result::Result<T, E>;
57
58struct SeekReader<R: Read> {
59    inner: R,
60    buffer: Vec<u8>,
61    position: usize,
62    reached_eof: bool,
63}
64
65impl<R: Read> SeekReader<R> {
66    fn new(inner: R) -> Self {
67        Self {
68            inner,
69            buffer: vec![],
70            position: 0,
71            reached_eof: false,
72        }
73    }
74    fn position(&self) -> usize {
75        self.position
76    }
77    fn ensure_buffered(&mut self, min_bytes: usize) -> std::io::Result<()> {
78        if self.reached_eof {
79            return Ok(());
80        }
81
82        let available = self.buffer.len().saturating_sub(self.position);
83        if available >= min_bytes {
84            return Ok(());
85        }
86
87        let needed = min_bytes - available;
88
89        // Reserve space for the additional bytes we need to read
90        self.buffer.reserve(needed);
91
92        // Read more data from the underlying reader
93        let mut temp_buf = vec![0; needed];
94        let mut total_read = 0;
95
96        while total_read < needed && !self.reached_eof {
97            let bytes_read = self.inner.read(&mut temp_buf[total_read..])?;
98            if bytes_read == 0 {
99                self.reached_eof = true;
100                break;
101            }
102            total_read += bytes_read;
103        }
104
105        // Append the read data to our buffer
106        self.buffer.extend_from_slice(&temp_buf[..total_read]);
107
108        Ok(())
109    }
110}
111impl<R: Read> Seek for SeekReader<R> {
112    // fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
113    //     match pos {
114    //         std::io::SeekFrom::Current(0) => Ok(self.read_bytes as u64),
115    //         _ => unimplemented!(),
116    //     }
117    // }
118    fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
119        let new_position = match pos {
120            std::io::SeekFrom::Start(offset) => offset as i64,
121            std::io::SeekFrom::Current(offset) => self.position as i64 + offset,
122            std::io::SeekFrom::End(_) => {
123                return Err(std::io::Error::new(
124                    std::io::ErrorKind::Unsupported,
125                    "Seeking from end is not supported for non-seekable readers",
126                ));
127            }
128        };
129
130        if new_position < 0 {
131            return Err(std::io::Error::new(
132                std::io::ErrorKind::InvalidInput,
133                "Cannot seek to a negative position",
134            ));
135        }
136
137        let new_position = new_position as usize;
138
139        // If seeking within already buffered data, just update position
140        if new_position <= self.buffer.len() {
141            self.position = new_position;
142            return Ok(new_position as u64);
143        }
144
145        // If seeking beyond buffered data, we need to read more
146        let bytes_needed = new_position - self.buffer.len();
147        self.position = self.buffer.len();
148
149        // Read and buffer bytes until we reach the target position
150        let mut temp_buf = vec![0; bytes_needed.min(8192)];
151        let mut remaining = bytes_needed;
152
153        while remaining > 0 {
154            let to_read = remaining.min(temp_buf.len());
155            let bytes_read = self.read(&mut temp_buf[..to_read])?;
156            if bytes_read == 0 {
157                // Hit EOF before reaching target position
158                return Ok(self.position as u64);
159            }
160            remaining -= bytes_read;
161        }
162
163        Ok(new_position as u64)
164    }
165}
166impl<R: Read> Read for SeekReader<R> {
167    // fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
168    //     self.reader.read(buf).inspect(|s| self.read_bytes += s)
169    // }
170    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
171        if buf.is_empty() {
172            return Ok(0);
173        }
174
175        // Ensure we have data available
176        self.ensure_buffered(1)?;
177
178        // Copy data from our buffer to the output buffer
179        let available = self.buffer.len() - self.position;
180        if available == 0 {
181            return Ok(0); // EOF
182        }
183
184        let to_copy = buf.len().min(available);
185        buf[..to_copy].copy_from_slice(&self.buffer[self.position..self.position + to_copy]);
186        self.position += to_copy;
187
188        Ok(to_copy)
189    }
190}
191
192#[cfg_attr(feature = "tracing", instrument(skip_all))]
193fn read_optional_uuid<A: ArchiveReader>(ar: &mut A) -> Result<Option<FGuid>> {
194    Ok(if ar.read_u8()? > 0 {
195        Some(FGuid::read(ar)?)
196    } else {
197        None
198    })
199}
200fn write_optional_uuid<A: ArchiveWriter>(ar: &mut A, id: Option<FGuid>) -> Result<()> {
201    if let Some(id) = id {
202        ar.write_u8(1)?;
203        id.write(ar)?;
204    } else {
205        ar.write_u8(0)?;
206    }
207    Ok(())
208}
209
210#[cfg_attr(feature = "tracing", instrument(skip_all, ret))]
211fn read_string<A: ArchiveReader>(ar: &mut A) -> Result<String> {
212    let len = ar.read_i32::<LE>()?;
213    if len < 0 {
214        let chars = read_array((-len) as u32, ar, |r| Ok(r.read_u16::<LE>()?))?;
215        let length = chars.iter().position(|&c| c == 0).unwrap_or(chars.len());
216        Ok(String::from_utf16(&chars[..length]).unwrap())
217    } else {
218        let mut chars = vec![0; len as usize];
219        ar.read_exact(&mut chars)?;
220        let length = chars.iter().position(|&c| c == 0).unwrap_or(chars.len());
221        Ok(String::from_utf8_lossy(&chars[..length]).into_owned())
222    }
223}
224#[cfg_attr(feature = "tracing", instrument(skip(ar)))]
225fn write_string<A: ArchiveWriter>(ar: &mut A, string: &str) -> Result<()> {
226    if string.is_empty() {
227        ar.write_u32::<LE>(0)?;
228    } else {
229        write_string_trailing(ar, string, None)?;
230    }
231    Ok(())
232}
233
234#[cfg_attr(feature = "tracing", instrument(skip_all))]
235fn read_string_trailing<A: ArchiveReader>(ar: &mut A) -> Result<(String, Vec<u8>)> {
236    let len = ar.read_i32::<LE>()?;
237    if len < 0 {
238        let bytes = (-len) as usize * 2;
239        let mut chars = vec![];
240        let mut rest = vec![];
241        let mut read = 0;
242        while read < bytes {
243            let next = ar.read_u16::<LE>()?;
244            read += 2;
245            if next == 0 {
246                rest.extend(next.to_le_bytes());
247                break;
248            } else {
249                chars.push(next);
250            }
251        }
252        while read < bytes {
253            rest.push(ar.read_u8()?);
254            read += 1;
255        }
256        Ok((String::from_utf16(&chars).unwrap(), rest))
257    } else {
258        let bytes = len as usize;
259        let mut chars = vec![];
260        let mut rest = vec![];
261        let mut read = 0;
262        while read < bytes {
263            let next = ar.read_u8()?;
264            read += 1;
265            if next == 0 {
266                rest.push(next);
267                break;
268            } else {
269                chars.push(next);
270            }
271        }
272        while read < bytes {
273            rest.push(ar.read_u8()?);
274            read += 1;
275        }
276        Ok((String::from_utf8(chars).unwrap(), rest))
277    }
278}
279#[cfg_attr(feature = "tracing", instrument(skip_all))]
280fn write_string_trailing<A: ArchiveWriter>(
281    ar: &mut A,
282    string: &str,
283    trailing: Option<&[u8]>,
284) -> Result<()> {
285    if string.is_empty() || string.is_ascii() {
286        ar.write_u32::<LE>((string.len() + trailing.map(|t| t.len()).unwrap_or(1)) as u32)?;
287        ar.write_all(string.as_bytes())?;
288        ar.write_all(trailing.unwrap_or(&[0]))?;
289    } else {
290        let chars: Vec<u16> = string.encode_utf16().collect();
291        ar.write_i32::<LE>(-((chars.len() + trailing.map(|t| t.len()).unwrap_or(2) / 2) as i32))?;
292        for c in chars {
293            ar.write_u16::<LE>(c)?;
294        }
295        ar.write_all(trailing.unwrap_or(&[0, 0]))?;
296    }
297    Ok(())
298}
299
300#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
301pub struct PropertyKey(pub u32, pub String);
302impl From<String> for PropertyKey {
303    fn from(value: String) -> Self {
304        Self(0, value)
305    }
306}
307impl From<&str> for PropertyKey {
308    fn from(value: &str) -> Self {
309        Self(0, value.to_string())
310    }
311}
312
313struct PropertyKeyVisitor;
314impl Visitor<'_> for PropertyKeyVisitor {
315    type Value = PropertyKey;
316    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
317        formatter.write_str(
318            "a property key in the form of key name and index seperated by '_' e.g. property_2",
319        )
320    }
321    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
322    where
323        E: serde::de::Error,
324    {
325        let (name_str, index_str) = value
326            .rsplit_once('_')
327            .ok_or_else(|| serde::de::Error::custom("property key does not contain a '_'"))?;
328        let index: u32 = index_str.parse().map_err(serde::de::Error::custom)?;
329
330        Ok(PropertyKey(index, name_str.to_string()))
331    }
332}
333impl<'de> Deserialize<'de> for PropertyKey {
334    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
335    where
336        D: Deserializer<'de>,
337    {
338        deserializer.deserialize_str(PropertyKeyVisitor)
339    }
340}
341impl Serialize for PropertyKey {
342    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
343    where
344        S: Serializer,
345    {
346        serializer.serialize_str(&format!("{}_{}", self.1, self.0))
347    }
348}
349
350#[derive(Debug, Clone, Default, PartialEq, Serialize)]
351#[serde(bound(serialize = "T::ObjectRef: Serialize"))]
352pub struct Properties<T: ArchiveType = SaveGameArchiveType>(
353    pub indexmap::IndexMap<PropertyKey, Property<T>>,
354);
355impl<T: ArchiveType> Properties<T> {
356    pub fn insert(&mut self, k: impl Into<PropertyKey>, v: Property<T>) -> Option<Property<T>> {
357        self.0.insert(k.into(), v)
358    }
359}
360impl<K, T: ArchiveType> std::ops::Index<K> for Properties<T>
361where
362    K: Into<PropertyKey>,
363{
364    type Output = Property<T>;
365    fn index(&self, index: K) -> &Self::Output {
366        self.0.index(&index.into())
367    }
368}
369impl<K, T: ArchiveType> std::ops::IndexMut<K> for Properties<T>
370where
371    K: Into<PropertyKey>,
372{
373    fn index_mut(&mut self, index: K) -> &mut Property<T> {
374        self.0.index_mut(&index.into())
375    }
376}
377impl<'a, T: ArchiveType> IntoIterator for &'a Properties<T> {
378    type Item = <&'a indexmap::IndexMap<PropertyKey, Property<T>> as IntoIterator>::Item;
379    type IntoIter = <&'a indexmap::IndexMap<PropertyKey, Property<T>> as IntoIterator>::IntoIter;
380    fn into_iter(self) -> Self::IntoIter {
381        self.0.iter()
382    }
383}
384
385#[cfg_attr(feature = "tracing", instrument(skip_all))]
386pub fn read_properties_until_none<T: ArchiveType, A: ArchiveReader<ArchiveType = T>>(
387    ar: &mut A,
388) -> Result<Properties<T>> {
389    let mut properties = Properties::default();
390    while let Some((name, prop)) = read_property(ar)? {
391        properties.insert(name, prop);
392    }
393    Ok(properties)
394}
395#[cfg_attr(feature = "tracing", instrument(skip_all))]
396pub fn write_properties_none_terminated<T: ArchiveType, A: ArchiveWriter<ArchiveType = T>>(
397    ar: &mut A,
398    properties: &Properties<T>,
399) -> Result<()> {
400    for p in properties {
401        write_property(p, ar)?;
402    }
403    ar.write_string("None")?;
404    Ok(())
405}
406
407#[cfg_attr(feature = "tracing", instrument(skip_all))]
408fn read_property<T: ArchiveType, A: ArchiveReader<ArchiveType = T>>(
409    ar: &mut A,
410) -> Result<Option<(PropertyKey, Property<T>)>> {
411    if let Some(mut tag) = PropertyTagFull::read(ar)? {
412        let tag_name = tag.name.to_string();
413        ar.scope().push(&tag_name);
414        let result = Property::read(ar, tag.clone());
415        ar.scope().pop();
416        let (value, updated_tag_data) = result?;
417
418        // If type information was refined during reading (e.g., array of structs in older UE versions),
419        // update the tag data and record the complete schema
420        if let Some(new_data) = updated_tag_data {
421            tag.data = new_data;
422        }
423
424        let key = PropertyKey(tag.index, tag_name.clone());
425
426        // Record the final, complete schema
427        ar.scope().push(&tag_name);
428        ar.record_schema(ar.path().to_string(), tag.into_partial());
429        ar.scope().pop();
430
431        Ok(Some((key, value)))
432    } else {
433        Ok(None)
434    }
435}
436#[cfg_attr(feature = "tracing", instrument(skip_all))]
437fn write_property<T: ArchiveType, A: ArchiveWriter<ArchiveType = T>>(
438    prop: (&PropertyKey, &Property<T>),
439    ar: &mut A,
440) -> Result<()> {
441    ar.scope().push(&prop.0 .1);
442    let result = (|| {
443        let tag_partial = ar
444            .get_schema(&ar.path())
445            .ok_or_else(|| Error::MissingPropertySchema(ar.path()))?;
446
447        let mut tag = tag_partial.into_full(&prop.0 .1, 0, prop.0 .0, prop.1);
448
449        // Write tag with placeholder size
450        tag.size = 0;
451        let tag_start = ar.stream_position()?;
452        tag.write(ar)?;
453        let data_start = ar.stream_position()?;
454
455        // Write the actual property data
456        prop.1.write(ar, &tag)?;
457        let data_end = ar.stream_position()?;
458
459        // Calculate actual size
460        let size = (data_end - data_start) as u32;
461        tag.size = size;
462
463        // Seek back and rewrite the tag with correct size
464        ar.seek(std::io::SeekFrom::Start(tag_start))?;
465        tag.write(ar)?;
466
467        // Seek to end to continue writing
468        ar.seek(std::io::SeekFrom::Start(data_end))?;
469        Ok(())
470    })();
471    ar.scope().pop();
472    result
473}
474
475#[cfg_attr(feature = "tracing", instrument(skip_all))]
476fn read_array<T, F, A: ArchiveReader>(length: u32, ar: &mut A, f: F) -> Result<Vec<T>>
477where
478    F: Fn(&mut A) -> Result<T>,
479{
480    (0..length).map(|_| f(ar)).collect()
481}
482
483#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
484pub struct FGuid {
485    a: u32,
486    b: u32,
487    c: u32,
488    d: u32,
489}
490
491impl FGuid {
492    pub fn new(a: u32, b: u32, c: u32, d: u32) -> Self {
493        Self { a, b, c, d }
494    }
495
496    pub fn nil() -> Self {
497        Self::default()
498    }
499
500    pub fn is_nil(&self) -> bool {
501        self.a == 0 && self.b == 0 && self.c == 0 && self.d == 0
502    }
503
504    pub fn parse_str(s: &str) -> Result<Self, Error> {
505        let s = s.replace("-", "");
506        if s.len() != 32 {
507            return Err(Error::Other("Invalid GUID string length".into()));
508        }
509
510        let parse_hex_u32 = |start: usize| -> Result<u32, Error> {
511            u32::from_str_radix(&s[start..start + 8], 16)
512                .map_err(|_| Error::Other("Invalid hex in GUID".into()))
513        };
514
515        Ok(Self {
516            a: parse_hex_u32(0)?,
517            b: parse_hex_u32(8)?,
518            c: parse_hex_u32(16)?,
519            d: parse_hex_u32(24)?,
520        })
521    }
522}
523
524impl std::fmt::Display for FGuid {
525    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
526        let b = self.b.to_le_bytes();
527        let c = self.c.to_le_bytes();
528
529        write!(
530            f,
531            "{:08x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:08x}",
532            self.a, b[3], b[2], b[1], b[0], c[3], c[2], c[1], c[0], self.d,
533        )
534    }
535}
536
537impl Serialize for FGuid {
538    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
539    where
540        S: Serializer,
541    {
542        serializer.serialize_str(&self.to_string())
543    }
544}
545
546impl<'de> Deserialize<'de> for FGuid {
547    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
548    where
549        D: Deserializer<'de>,
550    {
551        struct FGuidVisitor;
552
553        impl<'de> Visitor<'de> for FGuidVisitor {
554            type Value = FGuid;
555
556            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
557                formatter.write_str("a UUID string in format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
558            }
559
560            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
561            where
562                E: serde::de::Error,
563            {
564                FGuid::parse_str(value).map_err(|e| E::custom(format!("Invalid UUID: {}", e)))
565            }
566        }
567
568        deserializer.deserialize_str(FGuidVisitor)
569    }
570}
571
572impl FGuid {
573    #[cfg_attr(feature = "tracing", instrument(name = "FGuid_read", skip_all))]
574    fn read<A: ArchiveReader>(ar: &mut A) -> Result<FGuid> {
575        Ok(Self {
576            a: ar.read_u32::<LE>()?,
577            b: ar.read_u32::<LE>()?,
578            c: ar.read_u32::<LE>()?,
579            d: ar.read_u32::<LE>()?,
580        })
581    }
582}
583impl FGuid {
584    #[cfg_attr(feature = "tracing", instrument(name = "FGuid_write", skip_all))]
585    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
586        ar.write_u32::<LE>(self.a)?;
587        ar.write_u32::<LE>(self.b)?;
588        ar.write_u32::<LE>(self.c)?;
589        ar.write_u32::<LE>(self.d)?;
590        Ok(())
591    }
592}
593
594#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
595struct PropertyTagFull<'a> {
596    name: Cow<'a, str>,
597    id: Option<FGuid>,
598    size: u32,
599    index: u32,
600    data: PropertyTagDataFull,
601}
602#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
603enum PropertyTagDataFull {
604    Array(std::boxed::Box<PropertyTagDataFull>),
605    Struct {
606        struct_type: StructType,
607        id: FGuid,
608    },
609    Set {
610        key_type: std::boxed::Box<PropertyTagDataFull>,
611    },
612    Map {
613        key_type: std::boxed::Box<PropertyTagDataFull>,
614        value_type: std::boxed::Box<PropertyTagDataFull>,
615    },
616    Byte(Option<String>),
617    Enum(String, Option<String>),
618    Bool(bool),
619    Other(PropertyType),
620}
621#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
622pub struct PropertyTagPartial {
623    #[serde(skip_serializing_if = "Option::is_none")]
624    pub id: Option<FGuid>,
625    pub data: PropertyTagDataPartial,
626}
627#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
628pub enum PropertyTagDataPartial {
629    Array(std::boxed::Box<PropertyTagDataPartial>),
630    Struct {
631        struct_type: StructType,
632        id: FGuid,
633    },
634    Set {
635        key_type: std::boxed::Box<PropertyTagDataPartial>,
636    },
637    Map {
638        key_type: std::boxed::Box<PropertyTagDataPartial>,
639        value_type: std::boxed::Box<PropertyTagDataPartial>,
640    },
641    Byte(Option<String>),
642    Enum(String, Option<String>),
643    Other(PropertyType),
644}
645impl PropertyTagDataFull {
646    fn into_partial(self) -> PropertyTagDataPartial {
647        match self {
648            Self::Array(inner) => PropertyTagDataPartial::Array(inner.into_partial().into()),
649            Self::Struct { struct_type, id } => PropertyTagDataPartial::Struct { struct_type, id },
650            Self::Set { key_type } => PropertyTagDataPartial::Set {
651                key_type: key_type.into_partial().into(),
652            },
653            Self::Map {
654                key_type,
655                value_type,
656            } => PropertyTagDataPartial::Map {
657                key_type: key_type.into_partial().into(),
658                value_type: value_type.into_partial().into(),
659            },
660            Self::Byte(a) => PropertyTagDataPartial::Byte(a),
661            Self::Enum(a, b) => PropertyTagDataPartial::Enum(a, b),
662            Self::Bool(_) => PropertyTagDataPartial::Other(PropertyType::BoolProperty),
663            Self::Other(t) => PropertyTagDataPartial::Other(t),
664        }
665    }
666}
667impl PropertyTagDataPartial {
668    fn into_full<T: ArchiveType>(self, prop: &Property<T>) -> PropertyTagDataFull {
669        match self {
670            Self::Array(inner) => PropertyTagDataFull::Array(inner.into_full(prop).into()),
671            Self::Struct { struct_type, id } => PropertyTagDataFull::Struct { struct_type, id },
672            Self::Set { key_type } => PropertyTagDataFull::Set {
673                key_type: key_type.into_full(prop).into(),
674            },
675            Self::Map {
676                key_type,
677                value_type,
678            } => PropertyTagDataFull::Map {
679                key_type: key_type.into_full(prop).into(),
680                value_type: value_type.into_full(prop).into(),
681            },
682            Self::Byte(a) => PropertyTagDataFull::Byte(a),
683            Self::Enum(a, b) => PropertyTagDataFull::Enum(a, b),
684            Self::Other(PropertyType::BoolProperty) => PropertyTagDataFull::Bool(match prop {
685                Property::Bool(value) => *value,
686                _ => false,
687            }),
688            Self::Other(t) => PropertyTagDataFull::Other(t),
689        }
690    }
691}
692
693impl PropertyTagDataFull {
694    fn basic_type(&self) -> PropertyType {
695        match self {
696            Self::Array(_) => PropertyType::ArrayProperty,
697            Self::Struct { .. } => PropertyType::StructProperty,
698            Self::Set { .. } => PropertyType::SetProperty,
699            Self::Map { .. } => PropertyType::MapProperty,
700            Self::Byte(_) => PropertyType::ByteProperty,
701            Self::Enum(_, _) => PropertyType::EnumProperty,
702            Self::Bool(_) => PropertyType::BoolProperty,
703            Self::Other(property_type) => *property_type,
704        }
705    }
706    fn has_raw_struct(&self) -> bool {
707        match self {
708            Self::Array(inner) => inner.has_raw_struct(),
709            Self::Struct { struct_type, .. } => struct_type.raw(),
710            Self::Set { key_type } => key_type.has_raw_struct(),
711            Self::Map {
712                key_type,
713                value_type,
714            } => key_type.has_raw_struct() || value_type.has_raw_struct(),
715            Self::Byte(_) => false,
716            Self::Enum(_, _) => false,
717            Self::Bool(_) => false,
718            Self::Other(_) => false,
719        }
720    }
721    fn from_type(inner_type: PropertyType, struct_type: Option<StructType>) -> Self {
722        match inner_type {
723            PropertyType::BoolProperty => Self::Bool(false),
724            PropertyType::ByteProperty => Self::Byte(None),
725            PropertyType::EnumProperty => Self::Enum("".to_string(), None),
726            PropertyType::ArrayProperty => unreachable!("array of array is invalid"),
727            PropertyType::SetProperty => unreachable!("array of set is invalid"),
728            PropertyType::MapProperty => unreachable!("array of map is invalid"),
729            PropertyType::StructProperty => Self::Struct {
730                struct_type: struct_type.unwrap_or(StructType::Struct(None)),
731                id: Default::default(),
732            },
733            other => Self::Other(other),
734        }
735    }
736}
737bitflags::bitflags! {
738    #[derive(Debug, Clone, Copy)]
739    struct EPropertyTagFlags : u8 {
740        const None = 0x00;
741        const HasArrayIndex = 0x01;
742        const HasPropertyGuid = 0x02;
743        const HasPropertyExtensions = 0x04;
744        const HasBinaryOrNativeSerialize = 0x08;
745        const BoolTrue = 0x10;
746    }
747}
748impl PropertyTagPartial {
749    fn into_full<'a, T: ArchiveType>(
750        self,
751        name: &'a str,
752        size: u32,
753        index: u32,
754        prop: &Property<T>,
755    ) -> PropertyTagFull<'a> {
756        PropertyTagFull {
757            name: name.into(),
758            id: self.id,
759            size,
760            index,
761            data: self.data.into_full(prop),
762        }
763    }
764}
765impl PropertyTagFull<'_> {
766    fn into_partial(self) -> PropertyTagPartial {
767        PropertyTagPartial {
768            id: self.id,
769            data: self.data.into_partial(),
770        }
771    }
772    #[cfg_attr(feature = "tracing", instrument(name = "PropertyTag_read", skip_all))]
773    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Option<Self>> {
774        let name = ar.read_string()?;
775        if name == "None" {
776            return Ok(None);
777        }
778        if ar.version().property_tag() {
779            let root_node = read_node(ar)?;
780
781            #[derive(Default, Debug)]
782            struct Node {
783                name: String,
784                inner: Vec<Node>,
785            }
786            fn read_node<A: ArchiveReader>(ar: &mut A) -> Result<Node> {
787                Ok(Node {
788                    name: ar.read_string()?,
789                    inner: read_array(ar.read_u32::<LE>()?, ar, read_node)?,
790                })
791            }
792            fn read_path(node: &Node) -> Result<String> {
793                let name = node;
794                assert_eq!(1, name.inner.len());
795                let package = &name.inner[0];
796                assert_eq!(0, package.inner.len());
797                Ok(format!("{}.{}", package.name, name.name))
798            }
799            fn read_type(node: &Node, flags: EPropertyTagFlags) -> Result<PropertyTagDataFull> {
800                Ok(match node.name.as_str() {
801                    "ArrayProperty" => {
802                        PropertyTagDataFull::Array(read_type(&node.inner[0], flags)?.into())
803                    }
804                    "StructProperty" => {
805                        let raw = flags.contains(EPropertyTagFlags::HasBinaryOrNativeSerialize);
806                        let struct_type = StructType::from_full(&read_path(&node.inner[0])?, raw);
807                        let id = match node.inner.len() {
808                            1 => Default::default(),
809                            2 => FGuid::parse_str(&node.inner[1].name)?,
810                            _ => unimplemented!(),
811                        };
812                        PropertyTagDataFull::Struct { struct_type, id }
813                    }
814                    "SetProperty" => PropertyTagDataFull::Set {
815                        key_type: read_type(&node.inner[0], flags)?.into(),
816                    },
817                    "MapProperty" => PropertyTagDataFull::Map {
818                        key_type: read_type(&node.inner[0], flags)?.into(),
819                        value_type: read_type(&node.inner[1], flags)?.into(),
820                    },
821                    "ByteProperty" => {
822                        let inner = match node.inner.len() {
823                            0 => None,
824                            1 => Some(read_path(&node.inner[0])?),
825                            _ => unimplemented!(),
826                        };
827                        PropertyTagDataFull::Byte(inner)
828                    }
829                    "EnumProperty" => {
830                        assert_eq!(2, node.inner.len());
831                        let inner = read_path(&node.inner[0])?;
832                        let container = &node.inner[1];
833                        assert_eq!(0, container.inner.len());
834                        PropertyTagDataFull::Enum(inner, Some(container.name.to_owned()))
835                    }
836                    "BoolProperty" => {
837                        PropertyTagDataFull::Bool(flags.contains(EPropertyTagFlags::BoolTrue))
838                    }
839                    other => {
840                        assert_eq!(0, node.inner.len());
841                        PropertyTagDataFull::Other(PropertyType::try_from(other)?)
842                    }
843                })
844            }
845
846            let size = ar.read_u32::<LE>()?;
847
848            let flags = EPropertyTagFlags::from_bits(ar.read_u8()?)
849                .ok_or_else(|| error::Error::Other("unknown EPropertyTagFlags bits".into()))?;
850
851            let mut tag = Self {
852                name: name.into(),
853                size,
854                index: 0,
855                id: None,
856                data: read_type(&root_node, flags)?,
857            };
858
859            if flags.contains(EPropertyTagFlags::HasArrayIndex) {
860                tag.index = ar.read_u32::<LE>()?;
861            }
862            if flags.contains(EPropertyTagFlags::HasPropertyGuid) {
863                tag.id = Some(FGuid::read(ar)?);
864            }
865            if flags.contains(EPropertyTagFlags::HasPropertyExtensions) {
866                unimplemented!();
867            }
868
869            Ok(Some(tag))
870        } else {
871            ar.scope().push(&name.clone());
872            let result = (|| {
873                let type_ = PropertyType::read(ar)?;
874                let size = ar.read_u32::<LE>()?;
875                let index = ar.read_u32::<LE>()?;
876                let data = match type_ {
877                    PropertyType::BoolProperty => {
878                        let value = ar.read_u8()? > 0;
879                        PropertyTagDataFull::Bool(value)
880                    }
881                    PropertyType::IntProperty
882                    | PropertyType::Int8Property
883                    | PropertyType::Int16Property
884                    | PropertyType::Int64Property
885                    | PropertyType::UInt8Property
886                    | PropertyType::UInt16Property
887                    | PropertyType::UInt32Property
888                    | PropertyType::UInt64Property
889                    | PropertyType::FloatProperty
890                    | PropertyType::DoubleProperty
891                    | PropertyType::StrProperty
892                    | PropertyType::ObjectProperty
893                    | PropertyType::FieldPathProperty
894                    | PropertyType::SoftObjectProperty
895                    | PropertyType::NameProperty
896                    | PropertyType::TextProperty
897                    | PropertyType::DelegateProperty
898                    | PropertyType::MulticastDelegateProperty
899                    | PropertyType::MulticastInlineDelegateProperty
900                    | PropertyType::MulticastSparseDelegateProperty => {
901                        PropertyTagDataFull::Other(type_)
902                    }
903                    PropertyType::ByteProperty => {
904                        let enum_type = ar.read_string()?;
905                        PropertyTagDataFull::Byte((enum_type != "None").then_some(enum_type))
906                    }
907                    PropertyType::EnumProperty => {
908                        let enum_type = ar.read_string()?;
909                        PropertyTagDataFull::Enum(enum_type, None)
910                    }
911                    PropertyType::ArrayProperty => {
912                        let inner_type = PropertyType::read(ar)?;
913
914                        PropertyTagDataFull::Array(std::boxed::Box::new(
915                            PropertyTagDataFull::from_type(inner_type, None),
916                        ))
917                    }
918                    PropertyType::SetProperty => {
919                        let key_type = PropertyType::read(ar)?;
920                        let key_struct_type = match key_type {
921                            PropertyType::StructProperty => {
922                                Some(ar.get_type_or(&StructType::Guid)?)
923                            }
924                            _ => None,
925                        };
926
927                        let key_type =
928                            PropertyTagDataFull::from_type(key_type, key_struct_type.clone())
929                                .into();
930
931                        PropertyTagDataFull::Set { key_type }
932                    }
933                    PropertyType::MapProperty => {
934                        let key_type = PropertyType::read(ar)?;
935                        let key_struct_type = match key_type {
936                            PropertyType::StructProperty => {
937                                ar.scope().push("Key");
938                                let result = ar.get_type_or(&StructType::Guid);
939                                ar.scope().pop();
940                                Some(result?)
941                            }
942                            _ => None,
943                        };
944                        let value_type = PropertyType::read(ar)?;
945                        let value_struct_type = match value_type {
946                            PropertyType::StructProperty => {
947                                ar.scope().push("Value");
948                                let result = ar.get_type_or(&StructType::Struct(None));
949                                ar.scope().pop();
950                                Some(result?)
951                            }
952                            _ => None,
953                        };
954
955                        let key_type =
956                            PropertyTagDataFull::from_type(key_type, key_struct_type.clone())
957                                .into();
958                        let value_type =
959                            PropertyTagDataFull::from_type(value_type, value_struct_type.clone())
960                                .into();
961
962                        PropertyTagDataFull::Map {
963                            key_type,
964                            value_type,
965                        }
966                    }
967                    PropertyType::StructProperty => {
968                        let struct_type = StructType::read(ar)?;
969                        let struct_id = FGuid::read(ar)?;
970                        PropertyTagDataFull::Struct {
971                            struct_type,
972                            id: struct_id,
973                        }
974                    }
975                };
976                let id = if ar.version().property_guid() {
977                    read_optional_uuid(ar)?
978                } else {
979                    None
980                };
981                Ok(Some(Self {
982                    name: name.into(),
983                    size,
984                    index,
985                    id,
986                    data,
987                }))
988            })();
989            ar.scope().pop();
990            result
991        }
992    }
993    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
994        ar.write_string(&self.name)?;
995
996        if ar.version().property_tag() {
997            fn write_node<A: ArchiveWriter>(
998                ar: &mut A,
999                name: &str,
1000                inner_count: u32,
1001            ) -> Result<()> {
1002                ar.write_string(name)?;
1003                ar.write_u32::<LE>(inner_count)?;
1004                Ok(())
1005            }
1006            fn write_full_type<A: ArchiveWriter>(ar: &mut A, full_type: &str) -> Result<()> {
1007                let (a, b) = full_type.split_once('.').unwrap(); // TODO
1008                write_node(ar, b, 1)?;
1009                write_node(ar, a, 0)?;
1010                Ok(())
1011            }
1012            fn write_nodes<A: ArchiveWriter>(
1013                ar: &mut A,
1014                flags: &mut EPropertyTagFlags,
1015                data: &PropertyTagDataFull,
1016            ) -> Result<()> {
1017                match data {
1018                    PropertyTagDataFull::Array(inner) => {
1019                        write_node(ar, "ArrayProperty", 1)?;
1020                        write_nodes(ar, flags, inner)?;
1021                    }
1022                    PropertyTagDataFull::Struct { struct_type, id } => {
1023                        write_node(ar, "StructProperty", if id.is_nil() { 1 } else { 2 })?;
1024                        match struct_type {
1025                            StructType::Struct(Some(_)) => {}
1026                            _ => *flags |= EPropertyTagFlags::HasBinaryOrNativeSerialize,
1027                        }
1028                        write_full_type(ar, struct_type.full_str())?;
1029
1030                        if !id.is_nil() {
1031                            write_node(ar, &id.to_string(), 0)?;
1032                        }
1033                    }
1034                    PropertyTagDataFull::Set { key_type } => {
1035                        write_node(ar, "SetProperty", 1)?;
1036                        write_nodes(ar, flags, key_type)?;
1037                    }
1038                    PropertyTagDataFull::Map {
1039                        key_type,
1040                        value_type,
1041                    } => {
1042                        write_node(ar, "MapProperty", 2)?;
1043                        write_nodes(ar, flags, key_type)?;
1044                        write_nodes(ar, flags, value_type)?;
1045                    }
1046                    PropertyTagDataFull::Byte(enum_type) => {
1047                        write_node(ar, "ByteProperty", if enum_type.is_some() { 1 } else { 0 })?;
1048                        if let Some(enum_type) = enum_type {
1049                            write_full_type(ar, enum_type)?;
1050                        }
1051                    }
1052                    PropertyTagDataFull::Enum(enum_type, container) => {
1053                        write_node(ar, "EnumProperty", 2)?;
1054                        write_full_type(ar, enum_type)?;
1055                        write_node(ar, container.as_ref().unwrap(), 0)?;
1056                    }
1057                    PropertyTagDataFull::Bool(value) => {
1058                        if *value {
1059                            *flags |= EPropertyTagFlags::BoolTrue;
1060                        }
1061                        write_node(ar, "BoolProperty", 0)?;
1062                    }
1063                    PropertyTagDataFull::Other(property_type) => {
1064                        write_node(ar, property_type.get_name(), 0)?;
1065                    }
1066                }
1067                Ok(())
1068            }
1069
1070            let mut flags = EPropertyTagFlags::empty();
1071            write_nodes(ar, &mut flags, &self.data)?;
1072
1073            ar.write_u32::<LE>(self.size)?;
1074
1075            if self.index != 0 {
1076                flags |= EPropertyTagFlags::HasArrayIndex;
1077            }
1078            if self.id.is_some() {
1079                flags |= EPropertyTagFlags::HasPropertyGuid;
1080            }
1081
1082            ar.write_u8(flags.bits())?;
1083
1084            if self.index != 0 {
1085                ar.write_u32::<LE>(self.index)?;
1086            }
1087        } else {
1088            self.data.basic_type().write(ar)?;
1089            ar.write_u32::<LE>(self.size)?;
1090            ar.write_u32::<LE>(self.index)?;
1091            match &self.data {
1092                PropertyTagDataFull::Array(inner_type) => {
1093                    inner_type.basic_type().write(ar)?;
1094                }
1095                PropertyTagDataFull::Struct { struct_type, id } => {
1096                    struct_type.write(ar)?;
1097                    id.write(ar)?;
1098                }
1099                PropertyTagDataFull::Set { key_type, .. } => {
1100                    key_type.basic_type().write(ar)?;
1101                }
1102                PropertyTagDataFull::Map {
1103                    key_type,
1104                    value_type,
1105                    ..
1106                } => {
1107                    key_type.basic_type().write(ar)?;
1108                    value_type.basic_type().write(ar)?;
1109                }
1110                PropertyTagDataFull::Byte(enum_type) => {
1111                    ar.write_string(enum_type.as_deref().unwrap_or("None"))?;
1112                }
1113                PropertyTagDataFull::Enum(enum_type, _) => {
1114                    ar.write_string(enum_type)?;
1115                }
1116                PropertyTagDataFull::Bool(value) => {
1117                    ar.write_u8(*value as u8)?;
1118                }
1119                PropertyTagDataFull::Other(_) => {}
1120            }
1121            if ar.version().property_guid() {
1122                write_optional_uuid(ar, self.id)?;
1123            }
1124        }
1125        Ok(())
1126    }
1127}
1128
1129macro_rules! define_property_types {
1130    ($($variant:ident),* $(,)?) => {
1131        #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1132        pub enum PropertyType {
1133            $($variant,)*
1134        }
1135
1136        impl PropertyType {
1137            fn get_name(&self) -> &str {
1138                match self {
1139                    $(PropertyType::$variant => stringify!($variant),)*
1140                }
1141            }
1142
1143            #[cfg_attr(feature = "tracing", instrument(name = "PropertyType_read", skip_all))]
1144            fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
1145                Self::try_from(&ar.read_string()?)
1146            }
1147
1148            fn try_from(name: &str) -> Result<Self> {
1149                match name {
1150                    $(stringify!($variant) => Ok(PropertyType::$variant),)*
1151                    _ => Err(Error::UnknownPropertyType(format!("{name:?}"))),
1152                }
1153            }
1154
1155            fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
1156                ar.write_string(self.get_name())?;
1157                Ok(())
1158            }
1159        }
1160    };
1161}
1162
1163define_property_types! {
1164    IntProperty,
1165    Int8Property,
1166    Int16Property,
1167    Int64Property,
1168    UInt8Property,
1169    UInt16Property,
1170    UInt32Property,
1171    UInt64Property,
1172    FloatProperty,
1173    DoubleProperty,
1174    BoolProperty,
1175    ByteProperty,
1176    EnumProperty,
1177    ArrayProperty,
1178    ObjectProperty,
1179    StrProperty,
1180    FieldPathProperty,
1181    SoftObjectProperty,
1182    NameProperty,
1183    TextProperty,
1184    DelegateProperty,
1185    MulticastDelegateProperty,
1186    MulticastInlineDelegateProperty,
1187    MulticastSparseDelegateProperty,
1188    SetProperty,
1189    MapProperty,
1190    StructProperty,
1191}
1192
1193macro_rules! define_struct_types {
1194    ($(($package:literal, $variant:ident)),* $(,)?) => {
1195        #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1196        pub enum StructType {
1197            $($variant,)*
1198            Raw(String),
1199            Struct(Option<String>),
1200        }
1201
1202        impl From<&str> for StructType {
1203            fn from(t: &str) -> Self {
1204                match t {
1205                    $(stringify!($variant) => StructType::$variant,)*
1206                    "Struct" => StructType::Struct(None),
1207                    _ => StructType::Struct(Some(t.to_owned())),
1208                }
1209            }
1210        }
1211
1212        impl From<String> for StructType {
1213            fn from(t: String) -> Self {
1214                match t.as_str() {
1215                    $(stringify!($variant) => StructType::$variant,)*
1216                    "Struct" => StructType::Struct(None),
1217                    _ => StructType::Struct(Some(t)),
1218                }
1219            }
1220        }
1221
1222        impl StructType {
1223            pub fn from_full(t: &str, raw: bool) -> Self {
1224                match t {
1225                    $(concat!($package, ".", stringify!($variant)) => StructType::$variant,)*
1226                    "/Script/CoreUObject.Struct" => StructType::Struct(None),
1227                    _ if raw => StructType::Raw(t.to_owned()),
1228                    _ => StructType::Struct(Some(t.to_owned())),
1229                }
1230            }
1231
1232            pub fn full_str(&self) -> &str {
1233                match self {
1234                    $(StructType::$variant => concat!($package, ".", stringify!($variant)),)*
1235                    StructType::Raw(t) => t,
1236                    StructType::Struct(Some(t)) => t,
1237                    _ => unreachable!(),
1238                }
1239            }
1240
1241            pub fn as_str(&self) -> &str {
1242                match self {
1243                    $(StructType::$variant => stringify!($variant),)*
1244                    StructType::Raw(t) => t,
1245                    StructType::Struct(Some(t)) => t,
1246                    _ => unreachable!(),
1247                }
1248            }
1249
1250            #[cfg_attr(feature = "tracing", instrument(name = "StructType_read", skip_all))]
1251            fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
1252                Ok(ar.read_string()?.into())
1253            }
1254
1255            fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
1256                ar.write_string(self.as_str())?;
1257                Ok(())
1258            }
1259
1260            fn raw(&self) -> bool {
1261                matches!(self, StructType::Raw(_))
1262            }
1263        }
1264    };
1265}
1266
1267define_struct_types! {
1268    ("/Script/CoreUObject", Guid),
1269    ("/Script/CoreUObject", DateTime),
1270    ("/Script/CoreUObject", Timespan),
1271    ("/Script/CoreUObject", Vector2D),
1272    ("/Script/CoreUObject", Vector),
1273    ("/Script/CoreUObject", Vector4),
1274    ("/Script/CoreUObject", IntVector),
1275    ("/Script/CoreUObject", Box),
1276    ("/Script/CoreUObject", Box2D),
1277    ("/Script/CoreUObject", IntPoint),
1278    ("/Script/CoreUObject", Quat),
1279    ("/Script/CoreUObject", Rotator),
1280    ("/Script/CoreUObject", LinearColor),
1281    ("/Script/CoreUObject", Color),
1282    ("/Script/CoreUObject", SoftObjectPath),
1283    ("/Script/CoreUObject", SoftClassPath),
1284    ("/Script/GameplayTags", GameplayTagContainer),
1285    ("/Script/Engine", UniqueNetIdRepl),
1286    ("/Script/Engine", KeyHandleMap),
1287    ("/Script/Engine", RichCurveKey),
1288    ("/Script/Engine", SkeletalMeshSamplingLODBuiltData),
1289    ("/Script/Engine", PerPlatformFloat),
1290}
1291
1292type DateTime = u64;
1293type Timespan = i64;
1294type Int8 = i8;
1295type Int16 = i16;
1296type Int = i32;
1297type Int64 = i64;
1298type UInt8 = u8;
1299type UInt16 = u16;
1300type UInt32 = u32;
1301type UInt64 = u64;
1302type Bool = bool;
1303type Enum = String;
1304
1305#[derive(Debug, Clone, Copy, PartialEq)]
1306pub struct Float(pub f32);
1307#[derive(Debug, Clone, Copy, PartialEq)]
1308pub struct Double(pub f64);
1309
1310impl std::fmt::Display for Float {
1311    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1312        self.0.fmt(f)
1313    }
1314}
1315impl std::fmt::Display for Double {
1316    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1317        self.0.fmt(f)
1318    }
1319}
1320impl From<f32> for Float {
1321    fn from(value: f32) -> Self {
1322        Self(value)
1323    }
1324}
1325impl From<f64> for Float {
1326    fn from(value: f64) -> Self {
1327        Self(value as f32)
1328    }
1329}
1330impl From<Float> for f32 {
1331    fn from(val: Float) -> Self {
1332        val.0
1333    }
1334}
1335impl From<Float> for f64 {
1336    fn from(val: Float) -> Self {
1337        val.0 as f64
1338    }
1339}
1340impl From<f32> for Double {
1341    fn from(value: f32) -> Self {
1342        Self(value as f64)
1343    }
1344}
1345impl From<f64> for Double {
1346    fn from(value: f64) -> Self {
1347        Self(value)
1348    }
1349}
1350impl From<Double> for f32 {
1351    fn from(val: Double) -> Self {
1352        val.0 as f32
1353    }
1354}
1355impl From<Double> for f64 {
1356    fn from(val: Double) -> Self {
1357        val.0
1358    }
1359}
1360impl<'de> Deserialize<'de> for Float {
1361    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1362    where
1363        D: Deserializer<'de>,
1364    {
1365        struct FloatVisitor;
1366
1367        impl serde::de::Visitor<'_> for FloatVisitor {
1368            type Value = f32;
1369
1370            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1371                formatter.write_str("a float or string representation of NaN/Infinity")
1372            }
1373            fn visit_i8<E>(self, value: i8) -> Result<Self::Value, E> {
1374                Ok(value as f32)
1375            }
1376            fn visit_u8<E>(self, value: u8) -> Result<Self::Value, E> {
1377                Ok(value as f32)
1378            }
1379            fn visit_i16<E>(self, value: i16) -> Result<Self::Value, E> {
1380                Ok(value as f32)
1381            }
1382            fn visit_u16<E>(self, value: u16) -> Result<Self::Value, E> {
1383                Ok(value as f32)
1384            }
1385            fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E> {
1386                Ok(value as f32)
1387            }
1388            fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E> {
1389                Ok(value as f32)
1390            }
1391            fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> {
1392                Ok(value as f32)
1393            }
1394            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> {
1395                Ok(value as f32)
1396            }
1397            fn visit_f32<E>(self, value: f32) -> Result<Self::Value, E> {
1398                Ok(value)
1399            }
1400            fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E> {
1401                Ok(value as f32)
1402            }
1403            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
1404            where
1405                E: serde::de::Error,
1406            {
1407                match value {
1408                    "NaN" => Ok(f32::NAN),
1409                    "-NaN" => Ok(-f32::NAN),
1410                    "Infinity" => Ok(f32::INFINITY),
1411                    "-Infinity" => Ok(f32::NEG_INFINITY),
1412                    _ => Err(E::custom(format!(
1413                        "unxpected string value in place of float '{value}'"
1414                    ))),
1415                }
1416            }
1417
1418            fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
1419            where
1420                E: serde::de::Error,
1421            {
1422                self.visit_str(&value)
1423            }
1424        }
1425
1426        let value = deserializer.deserialize_any(FloatVisitor)?;
1427        Ok(Self(value))
1428    }
1429}
1430impl<'de> Deserialize<'de> for Double {
1431    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1432    where
1433        D: Deserializer<'de>,
1434    {
1435        struct FloatVisitor;
1436
1437        impl serde::de::Visitor<'_> for FloatVisitor {
1438            type Value = f64;
1439
1440            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1441                formatter.write_str("a float or string representation of NaN/Infinity")
1442            }
1443            fn visit_i8<E>(self, value: i8) -> Result<Self::Value, E> {
1444                Ok(value as f64)
1445            }
1446            fn visit_u8<E>(self, value: u8) -> Result<Self::Value, E> {
1447                Ok(value as f64)
1448            }
1449            fn visit_i16<E>(self, value: i16) -> Result<Self::Value, E> {
1450                Ok(value as f64)
1451            }
1452            fn visit_u16<E>(self, value: u16) -> Result<Self::Value, E> {
1453                Ok(value as f64)
1454            }
1455            fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E> {
1456                Ok(value as f64)
1457            }
1458            fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E> {
1459                Ok(value as f64)
1460            }
1461            fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> {
1462                Ok(value as f64)
1463            }
1464            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> {
1465                Ok(value as f64)
1466            }
1467            fn visit_f32<E>(self, value: f32) -> Result<Self::Value, E> {
1468                Ok(value as f64)
1469            }
1470            fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E> {
1471                Ok(value)
1472            }
1473            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
1474            where
1475                E: serde::de::Error,
1476            {
1477                match value {
1478                    "NaN" => Ok(f64::NAN),
1479                    "-NaN" => Ok(-f64::NAN),
1480                    "Infinity" => Ok(f64::INFINITY),
1481                    "-Infinity" => Ok(f64::NEG_INFINITY),
1482                    _ => Err(E::custom(format!(
1483                        "unxpected string value in place of float '{value}'"
1484                    ))),
1485                }
1486            }
1487
1488            fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
1489            where
1490                E: serde::de::Error,
1491            {
1492                self.visit_str(&value)
1493            }
1494        }
1495
1496        let value = deserializer.deserialize_any(FloatVisitor)?;
1497        Ok(Self(value))
1498    }
1499}
1500impl Serialize for Float {
1501    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1502    where
1503        S: Serializer,
1504    {
1505        let value = self.0;
1506        let sign = if value.is_sign_negative() { "-" } else { "" };
1507        if value.is_nan() {
1508            serializer.serialize_str(&format!("{sign}NaN"))
1509        } else if value.is_infinite() {
1510            serializer.serialize_str(&format!("{sign}Infinity"))
1511        } else {
1512            serializer.serialize_f32(value)
1513        }
1514    }
1515}
1516impl Serialize for Double {
1517    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1518    where
1519        S: Serializer,
1520    {
1521        let value = self.0;
1522        let sign = if value.is_sign_negative() { "-" } else { "" };
1523        if value.is_nan() {
1524            serializer.serialize_str(&format!("{sign}NaN"))
1525        } else if value.is_infinite() {
1526            serializer.serialize_str(&format!("{sign}Infinity"))
1527        } else {
1528            serializer.serialize_f64(value)
1529        }
1530    }
1531}
1532
1533#[derive(Debug, Clone, PartialEq, Serialize)]
1534pub struct MapEntry<T: ArchiveType = SaveGameArchiveType> {
1535    pub key: Property<T>,
1536    pub value: Property<T>,
1537}
1538impl<T: ArchiveType> MapEntry<T> {
1539    #[cfg_attr(feature = "tracing", instrument(name = "MapEntry_read", skip_all))]
1540    fn read<A: ArchiveReader<ArchiveType = T>>(
1541        ar: &mut A,
1542        key_type: &PropertyTagDataFull,
1543        value_type: &PropertyTagDataFull,
1544    ) -> Result<MapEntry<T>> {
1545        let key = Property::read_value(ar, key_type)?;
1546        let value = Property::read_value(ar, value_type)?;
1547        Ok(Self { key, value })
1548    }
1549    fn write<A: ArchiveWriter<ArchiveType = T>>(&self, ar: &mut A) -> Result<()> {
1550        self.key.write_value(ar)?;
1551        self.value.write_value(ar)?;
1552        Ok(())
1553    }
1554}
1555
1556#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1557pub struct FieldPath {
1558    path: Vec<String>,
1559    owner: String,
1560}
1561impl FieldPath {
1562    #[cfg_attr(feature = "tracing", instrument(name = "FieldPath_read", skip_all))]
1563    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
1564        Ok(Self {
1565            path: read_array(ar.read_u32::<LE>()?, ar, |ar| ar.read_string())?,
1566            owner: ar.read_string()?,
1567        })
1568    }
1569    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
1570        ar.write_u32::<LE>(self.path.len() as u32)?;
1571        for p in &self.path {
1572            ar.write_string(p)?;
1573        }
1574        ar.write_string(&self.owner)?;
1575        Ok(())
1576    }
1577}
1578
1579#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1580#[serde(bound(
1581    serialize = "T::ObjectRef: Serialize",
1582    deserialize = "T::ObjectRef: Deserialize<'de>"
1583))]
1584pub struct Delegate<T: ArchiveType = SaveGameArchiveType> {
1585    pub object: T::ObjectRef,
1586    pub delegate: String,
1587}
1588impl<T: ArchiveType> Delegate<T> {
1589    #[cfg_attr(feature = "tracing", instrument(name = "Delegate_read", skip_all))]
1590    fn read<A: ArchiveReader<ArchiveType = T>>(ar: &mut A) -> Result<Self> {
1591        Ok(Self {
1592            object: ar.read_object_ref()?,
1593            delegate: ar.read_string()?,
1594        })
1595    }
1596    fn write<A: ArchiveWriter<ArchiveType = T>>(&self, ar: &mut A) -> Result<()> {
1597        ar.write_object_ref(&self.object)?;
1598        ar.write_string(&self.delegate)?;
1599        Ok(())
1600    }
1601}
1602
1603#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1604#[serde(bound(
1605    serialize = "T::ObjectRef: Serialize",
1606    deserialize = "T::ObjectRef: Deserialize<'de>"
1607))]
1608pub struct MulticastDelegate<T: ArchiveType = SaveGameArchiveType>(pub Vec<Delegate<T>>);
1609impl<T: ArchiveType> MulticastDelegate<T> {
1610    #[cfg_attr(
1611        feature = "tracing",
1612        instrument(name = "MulticastDelegate_read", skip_all)
1613    )]
1614    fn read<A: ArchiveReader<ArchiveType = T>>(ar: &mut A) -> Result<Self> {
1615        Ok(Self(read_array(ar.read_u32::<LE>()?, ar, Delegate::read)?))
1616    }
1617    fn write<A: ArchiveWriter<ArchiveType = T>>(&self, ar: &mut A) -> Result<()> {
1618        ar.write_u32::<LE>(self.0.len() as u32)?;
1619        for entry in &self.0 {
1620            entry.write(ar)?;
1621        }
1622        Ok(())
1623    }
1624}
1625
1626#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1627#[serde(bound(
1628    serialize = "T::ObjectRef: Serialize",
1629    deserialize = "T::ObjectRef: Deserialize<'de>"
1630))]
1631pub struct MulticastInlineDelegate<T: ArchiveType = SaveGameArchiveType>(pub Vec<Delegate<T>>);
1632impl<T: ArchiveType> MulticastInlineDelegate<T> {
1633    #[cfg_attr(
1634        feature = "tracing",
1635        instrument(name = "MulticastInlineDelegate_read", skip_all)
1636    )]
1637    fn read<A: ArchiveReader<ArchiveType = T>>(ar: &mut A) -> Result<Self> {
1638        Ok(Self(read_array(ar.read_u32::<LE>()?, ar, Delegate::read)?))
1639    }
1640    fn write<A: ArchiveWriter<ArchiveType = T>>(&self, ar: &mut A) -> Result<()> {
1641        ar.write_u32::<LE>(self.0.len() as u32)?;
1642        for entry in &self.0 {
1643            entry.write(ar)?;
1644        }
1645        Ok(())
1646    }
1647}
1648
1649#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1650#[serde(bound(
1651    serialize = "T::ObjectRef: Serialize",
1652    deserialize = "T::ObjectRef: Deserialize<'de>"
1653))]
1654pub struct MulticastSparseDelegate<T: ArchiveType = SaveGameArchiveType>(pub Vec<Delegate<T>>);
1655impl<T: ArchiveType> MulticastSparseDelegate<T> {
1656    #[cfg_attr(
1657        feature = "tracing",
1658        instrument(name = "MulticastSparseDelegate_read", skip_all)
1659    )]
1660    fn read<A: ArchiveReader<ArchiveType = T>>(ar: &mut A) -> Result<Self> {
1661        Ok(Self(read_array(ar.read_u32::<LE>()?, ar, Delegate::read)?))
1662    }
1663    fn write<A: ArchiveWriter<ArchiveType = T>>(&self, ar: &mut A) -> Result<()> {
1664        ar.write_u32::<LE>(self.0.len() as u32)?;
1665        for entry in &self.0 {
1666            entry.write(ar)?;
1667        }
1668        Ok(())
1669    }
1670}
1671
1672#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1673pub struct LinearColor {
1674    pub r: Float,
1675    pub g: Float,
1676    pub b: Float,
1677    pub a: Float,
1678}
1679impl LinearColor {
1680    #[cfg_attr(feature = "tracing", instrument(name = "LinearColor_read", skip_all))]
1681    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
1682        Ok(Self {
1683            r: ar.read_f32::<LE>()?.into(),
1684            g: ar.read_f32::<LE>()?.into(),
1685            b: ar.read_f32::<LE>()?.into(),
1686            a: ar.read_f32::<LE>()?.into(),
1687        })
1688    }
1689    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
1690        ar.write_f32::<LE>(self.r.into())?;
1691        ar.write_f32::<LE>(self.g.into())?;
1692        ar.write_f32::<LE>(self.b.into())?;
1693        ar.write_f32::<LE>(self.a.into())?;
1694        Ok(())
1695    }
1696}
1697#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1698pub struct Quat {
1699    pub x: Double,
1700    pub y: Double,
1701    pub z: Double,
1702    pub w: Double,
1703}
1704impl Quat {
1705    #[cfg_attr(feature = "tracing", instrument(name = "Quat_read", skip_all))]
1706    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
1707        if ar.version().large_world_coordinates() {
1708            Ok(Self {
1709                x: ar.read_f64::<LE>()?.into(),
1710                y: ar.read_f64::<LE>()?.into(),
1711                z: ar.read_f64::<LE>()?.into(),
1712                w: ar.read_f64::<LE>()?.into(),
1713            })
1714        } else {
1715            Ok(Self {
1716                x: ar.read_f32::<LE>()?.into(),
1717                y: ar.read_f32::<LE>()?.into(),
1718                z: ar.read_f32::<LE>()?.into(),
1719                w: ar.read_f32::<LE>()?.into(),
1720            })
1721        }
1722    }
1723    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
1724        if ar.version().large_world_coordinates() {
1725            ar.write_f64::<LE>(self.x.into())?;
1726            ar.write_f64::<LE>(self.y.into())?;
1727            ar.write_f64::<LE>(self.z.into())?;
1728            ar.write_f64::<LE>(self.w.into())?;
1729        } else {
1730            ar.write_f32::<LE>(self.x.into())?;
1731            ar.write_f32::<LE>(self.y.into())?;
1732            ar.write_f32::<LE>(self.z.into())?;
1733            ar.write_f32::<LE>(self.w.into())?;
1734        }
1735        Ok(())
1736    }
1737}
1738#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1739pub struct Rotator {
1740    pub x: Double,
1741    pub y: Double,
1742    pub z: Double,
1743}
1744impl Rotator {
1745    #[cfg_attr(feature = "tracing", instrument(name = "Rotator_read", skip_all))]
1746    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
1747        if ar.version().large_world_coordinates() {
1748            Ok(Self {
1749                x: ar.read_f64::<LE>()?.into(),
1750                y: ar.read_f64::<LE>()?.into(),
1751                z: ar.read_f64::<LE>()?.into(),
1752            })
1753        } else {
1754            Ok(Self {
1755                x: ar.read_f32::<LE>()?.into(),
1756                y: ar.read_f32::<LE>()?.into(),
1757                z: ar.read_f32::<LE>()?.into(),
1758            })
1759        }
1760    }
1761    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
1762        if ar.version().large_world_coordinates() {
1763            ar.write_f64::<LE>(self.x.into())?;
1764            ar.write_f64::<LE>(self.y.into())?;
1765            ar.write_f64::<LE>(self.z.into())?;
1766        } else {
1767            ar.write_f32::<LE>(self.x.into())?;
1768            ar.write_f32::<LE>(self.y.into())?;
1769            ar.write_f32::<LE>(self.z.into())?;
1770        }
1771        Ok(())
1772    }
1773}
1774#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1775pub struct Color {
1776    pub r: u8,
1777    pub g: u8,
1778    pub b: u8,
1779    pub a: u8,
1780}
1781impl Color {
1782    #[cfg_attr(feature = "tracing", instrument(name = "Color_read", skip_all))]
1783    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
1784        Ok(Self {
1785            r: ar.read_u8()?,
1786            g: ar.read_u8()?,
1787            b: ar.read_u8()?,
1788            a: ar.read_u8()?,
1789        })
1790    }
1791    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
1792        ar.write_u8(self.r)?;
1793        ar.write_u8(self.g)?;
1794        ar.write_u8(self.b)?;
1795        ar.write_u8(self.a)?;
1796        Ok(())
1797    }
1798}
1799#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1800pub struct Vector {
1801    pub x: Double,
1802    pub y: Double,
1803    pub z: Double,
1804}
1805impl Vector {
1806    #[cfg_attr(feature = "tracing", instrument(name = "Vector_read", skip_all))]
1807    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
1808        if ar.version().large_world_coordinates() {
1809            Ok(Self {
1810                x: ar.read_f64::<LE>()?.into(),
1811                y: ar.read_f64::<LE>()?.into(),
1812                z: ar.read_f64::<LE>()?.into(),
1813            })
1814        } else {
1815            Ok(Self {
1816                x: ar.read_f32::<LE>()?.into(),
1817                y: ar.read_f32::<LE>()?.into(),
1818                z: ar.read_f32::<LE>()?.into(),
1819            })
1820        }
1821    }
1822    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
1823        if ar.version().large_world_coordinates() {
1824            ar.write_f64::<LE>(self.x.into())?;
1825            ar.write_f64::<LE>(self.y.into())?;
1826            ar.write_f64::<LE>(self.z.into())?;
1827        } else {
1828            ar.write_f32::<LE>(self.x.into())?;
1829            ar.write_f32::<LE>(self.y.into())?;
1830            ar.write_f32::<LE>(self.z.into())?;
1831        }
1832        Ok(())
1833    }
1834}
1835#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1836pub struct Vector2D {
1837    pub x: Double,
1838    pub y: Double,
1839}
1840impl Vector2D {
1841    #[cfg_attr(feature = "tracing", instrument(name = "Vector2D_read", skip_all))]
1842    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
1843        if ar.version().large_world_coordinates() {
1844            Ok(Self {
1845                x: ar.read_f64::<LE>()?.into(),
1846                y: ar.read_f64::<LE>()?.into(),
1847            })
1848        } else {
1849            Ok(Self {
1850                x: ar.read_f32::<LE>()?.into(),
1851                y: ar.read_f32::<LE>()?.into(),
1852            })
1853        }
1854    }
1855    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
1856        if ar.version().large_world_coordinates() {
1857            ar.write_f64::<LE>(self.x.into())?;
1858            ar.write_f64::<LE>(self.y.into())?;
1859        } else {
1860            ar.write_f32::<LE>(self.x.into())?;
1861            ar.write_f32::<LE>(self.y.into())?;
1862        }
1863        Ok(())
1864    }
1865}
1866#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1867pub struct Box2D {
1868    pub min: Vector2D,
1869    pub max: Vector2D,
1870    pub is_valid: bool,
1871}
1872impl Box2D {
1873    #[cfg_attr(feature = "tracing", instrument(name = "Box2D_read", skip_all))]
1874    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
1875        Ok(Self {
1876            min: Vector2D::read(ar)?,
1877            max: Vector2D::read(ar)?,
1878            is_valid: ar.read_u8()? > 0,
1879        })
1880    }
1881    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
1882        self.min.write(ar)?;
1883        self.max.write(ar)?;
1884        ar.write_u8(self.is_valid as u8)?;
1885        Ok(())
1886    }
1887}
1888#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1889pub struct Vector4 {
1890    pub x: Double,
1891    pub y: Double,
1892    pub z: Double,
1893    pub w: Double,
1894}
1895impl Vector4 {
1896    #[cfg_attr(feature = "tracing", instrument(name = "Vector4_read", skip_all))]
1897    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
1898        if ar.version().large_world_coordinates() {
1899            Ok(Self {
1900                x: ar.read_f64::<LE>()?.into(),
1901                y: ar.read_f64::<LE>()?.into(),
1902                z: ar.read_f64::<LE>()?.into(),
1903                w: ar.read_f64::<LE>()?.into(),
1904            })
1905        } else {
1906            Ok(Self {
1907                x: ar.read_f32::<LE>()?.into(),
1908                y: ar.read_f32::<LE>()?.into(),
1909                z: ar.read_f32::<LE>()?.into(),
1910                w: ar.read_f32::<LE>()?.into(),
1911            })
1912        }
1913    }
1914    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
1915        if ar.version().large_world_coordinates() {
1916            ar.write_f64::<LE>(self.x.into())?;
1917            ar.write_f64::<LE>(self.y.into())?;
1918            ar.write_f64::<LE>(self.z.into())?;
1919            ar.write_f64::<LE>(self.w.into())?;
1920        } else {
1921            ar.write_f32::<LE>(self.x.into())?;
1922            ar.write_f32::<LE>(self.y.into())?;
1923            ar.write_f32::<LE>(self.z.into())?;
1924            ar.write_f32::<LE>(self.w.into())?;
1925        }
1926        Ok(())
1927    }
1928}
1929#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1930pub struct IntVector {
1931    pub x: i32,
1932    pub y: i32,
1933    pub z: i32,
1934}
1935impl IntVector {
1936    #[cfg_attr(feature = "tracing", instrument(name = "IntVector_read", skip_all))]
1937    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
1938        Ok(Self {
1939            x: ar.read_i32::<LE>()?,
1940            y: ar.read_i32::<LE>()?,
1941            z: ar.read_i32::<LE>()?,
1942        })
1943    }
1944    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
1945        ar.write_i32::<LE>(self.x)?;
1946        ar.write_i32::<LE>(self.y)?;
1947        ar.write_i32::<LE>(self.z)?;
1948        Ok(())
1949    }
1950}
1951#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1952pub struct Box {
1953    pub min: Vector,
1954    pub max: Vector,
1955    pub is_valid: bool,
1956}
1957impl Box {
1958    #[cfg_attr(feature = "tracing", instrument(name = "Box_read", skip_all))]
1959    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
1960        Ok(Self {
1961            min: Vector::read(ar)?,
1962            max: Vector::read(ar)?,
1963            is_valid: ar.read_u8()? > 0,
1964        })
1965    }
1966    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
1967        self.min.write(ar)?;
1968        self.max.write(ar)?;
1969        ar.write_u8(self.is_valid as u8)?;
1970        Ok(())
1971    }
1972}
1973#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1974pub struct IntPoint {
1975    pub x: i32,
1976    pub y: i32,
1977}
1978impl IntPoint {
1979    #[cfg_attr(feature = "tracing", instrument(name = "IntPoint_read", skip_all))]
1980    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
1981        Ok(Self {
1982            x: ar.read_i32::<LE>()?,
1983            y: ar.read_i32::<LE>()?,
1984        })
1985    }
1986    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
1987        ar.write_i32::<LE>(self.x)?;
1988        ar.write_i32::<LE>(self.y)?;
1989        Ok(())
1990    }
1991}
1992
1993#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1994pub struct FKeyHandleMap {}
1995impl FKeyHandleMap {
1996    #[cfg_attr(feature = "tracing", instrument(name = "FKeyHandleMap_read", skip_all))]
1997    fn read<A: ArchiveReader>(_ar: &mut A) -> Result<Self> {
1998        Ok(Self {})
1999    }
2000    fn write<A: ArchiveWriter>(&self, _ar: &mut A) -> Result<()> {
2001        Ok(())
2002    }
2003}
2004
2005#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2006pub struct FRichCurveKey {
2007    /// Interpolation mode between this key and the next
2008    pub interp_mode: u8,
2009    /// Mode for tangents at this key
2010    pub tangent_mode: u8,
2011    /// If either tangent at this key is 'weighted'
2012    pub tangent_weight_mode: u8,
2013    /// Time at this key
2014    pub time: Float,
2015    /// Value at this key
2016    pub value: Float,
2017    /// If RCIM_Cubic, the arriving tangent at this key
2018    pub arrive_tangent: Float,
2019    /// If RCTWM_WeightedArrive or RCTWM_WeightedBoth, the weight of the left tangent
2020    pub arrive_tangent_weight: Float,
2021    /// If RCIM_Cubic, the leaving tangent at this key
2022    pub leave_tangent: Float,
2023    /// If RCTWM_WeightedLeave or RCTWM_WeightedBoth, the weight of the right tangent
2024    pub leave_tangent_weight: Float,
2025}
2026impl FRichCurveKey {
2027    #[cfg_attr(feature = "tracing", instrument(name = "FRichCurveKey_read", skip_all))]
2028    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
2029        Ok(Self {
2030            interp_mode: ar.read_u8()?,
2031            tangent_mode: ar.read_u8()?,
2032            tangent_weight_mode: ar.read_u8()?,
2033            time: ar.read_f32::<LE>()?.into(),
2034            value: ar.read_f32::<LE>()?.into(),
2035            arrive_tangent: ar.read_f32::<LE>()?.into(),
2036            arrive_tangent_weight: ar.read_f32::<LE>()?.into(),
2037            leave_tangent: ar.read_f32::<LE>()?.into(),
2038            leave_tangent_weight: ar.read_f32::<LE>()?.into(),
2039        })
2040    }
2041    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
2042        ar.write_u8(self.interp_mode)?;
2043        ar.write_u8(self.tangent_mode)?;
2044        ar.write_u8(self.tangent_weight_mode)?;
2045        ar.write_f32::<LE>(self.time.into())?;
2046        ar.write_f32::<LE>(self.value.into())?;
2047        ar.write_f32::<LE>(self.arrive_tangent.into())?;
2048        ar.write_f32::<LE>(self.arrive_tangent_weight.into())?;
2049        ar.write_f32::<LE>(self.leave_tangent.into())?;
2050        ar.write_f32::<LE>(self.leave_tangent_weight.into())?;
2051        Ok(())
2052    }
2053}
2054
2055#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2056pub struct FWeightedRandomSampler {
2057    pub prob: Vec<Float>,
2058    pub alias: Vec<i32>,
2059    pub total_weight: Float,
2060}
2061impl FWeightedRandomSampler {
2062    #[cfg_attr(
2063        feature = "tracing",
2064        instrument(name = "FWeightedRandomSampler_read", skip_all)
2065    )]
2066    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
2067        Ok(Self {
2068            prob: read_array(ar.read_u32::<LE>()?, ar, |r| Ok(r.read_f32::<LE>()?.into()))?,
2069            alias: read_array(ar.read_u32::<LE>()?, ar, |r| Ok(r.read_i32::<LE>()?))?,
2070            total_weight: ar.read_f32::<LE>()?.into(),
2071        })
2072    }
2073    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
2074        ar.write_u32::<LE>(self.prob.len() as u32)?;
2075        for p in &self.prob {
2076            ar.write_f32::<LE>((*p).into())?;
2077        }
2078        ar.write_u32::<LE>(self.alias.len() as u32)?;
2079        for a in &self.alias {
2080            ar.write_i32::<LE>(*a)?;
2081        }
2082        ar.write_f32::<LE>(self.total_weight.into())?;
2083        Ok(())
2084    }
2085}
2086
2087#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2088pub struct FSkeletalMeshSamplingLODBuiltData {
2089    pub weighted_random_sampler: FWeightedRandomSampler,
2090}
2091impl FSkeletalMeshSamplingLODBuiltData {
2092    #[cfg_attr(
2093        feature = "tracing",
2094        instrument(name = "SkeletalMeshSamplingLODBuiltData_read", skip_all)
2095    )]
2096    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
2097        Ok(Self {
2098            weighted_random_sampler: FWeightedRandomSampler::read(ar)?,
2099        })
2100    }
2101    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
2102        self.weighted_random_sampler.write(ar)?;
2103        Ok(())
2104    }
2105}
2106
2107#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2108pub struct FPerPlatformFloat {
2109    pub is_cooked: bool,
2110    pub value: Float,
2111}
2112impl FPerPlatformFloat {
2113    #[cfg_attr(
2114        feature = "tracing",
2115        instrument(name = "FPerPlatformFloat_read", skip_all)
2116    )]
2117    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
2118        let is_cooked = ar.read_u32::<LE>()? != 0;
2119        assert!(
2120            is_cooked,
2121            "TODO implement !is_cooked (read map of platform => value)"
2122        );
2123        Ok(Self {
2124            is_cooked,
2125            value: ar.read_f32::<LE>()?.into(),
2126        })
2127    }
2128    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
2129        ar.write_u32::<LE>(self.is_cooked as u32)?;
2130        ar.write_f32::<LE>(self.value.into())?;
2131        Ok(())
2132    }
2133}
2134
2135#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2136pub enum SoftObjectPath {
2137    Old {
2138        asset_path_name: String,
2139        sub_path_string: String,
2140    },
2141    New {
2142        asset_path_name: String,
2143        package_name: String,
2144        asset_name: (String, Vec<u8>),
2145    },
2146}
2147impl SoftObjectPath {
2148    #[cfg_attr(
2149        feature = "tracing",
2150        instrument(name = "SoftObjectPath_read", skip_all)
2151    )]
2152    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
2153        Ok(if ar.version().remove_asset_path_fnames() {
2154            Self::New {
2155                asset_path_name: ar.read_string()?,
2156                package_name: ar.read_string()?,
2157                asset_name: ar.read_string_trailing()?,
2158            }
2159        } else {
2160            Self::Old {
2161                asset_path_name: ar.read_string()?,
2162                sub_path_string: ar.read_string()?,
2163            }
2164        })
2165    }
2166    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
2167        match self {
2168            Self::Old {
2169                asset_path_name,
2170                sub_path_string,
2171            } => {
2172                ar.write_string(asset_path_name)?;
2173                ar.write_string(sub_path_string)?;
2174            }
2175            Self::New {
2176                asset_path_name,
2177                package_name,
2178                asset_name: (asset_name, trailing),
2179            } => {
2180                ar.write_string(asset_path_name)?;
2181                ar.write_string(package_name)?;
2182                ar.write_string_trailing(asset_name, Some(trailing))?;
2183            }
2184        }
2185        Ok(())
2186    }
2187}
2188
2189#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2190pub struct SoftClassPath(pub SoftObjectPath);
2191impl SoftClassPath {
2192    #[cfg_attr(feature = "tracing", instrument(name = "SoftClassPath_read", skip_all))]
2193    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
2194        Ok(Self(SoftObjectPath::read(ar)?))
2195    }
2196    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
2197        self.0.write(ar)
2198    }
2199}
2200
2201#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2202pub struct GameplayTag {
2203    pub name: String,
2204}
2205impl GameplayTag {
2206    #[cfg_attr(feature = "tracing", instrument(name = "GameplayTag_read", skip_all))]
2207    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
2208        Ok(Self {
2209            name: ar.read_string()?,
2210        })
2211    }
2212    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
2213        ar.write_string(&self.name)?;
2214        Ok(())
2215    }
2216}
2217
2218#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2219pub struct GameplayTagContainer {
2220    pub gameplay_tags: Vec<GameplayTag>,
2221}
2222impl GameplayTagContainer {
2223    #[cfg_attr(
2224        feature = "tracing",
2225        instrument(name = "GameplayTagContainer_read", skip_all)
2226    )]
2227    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
2228        Ok(Self {
2229            gameplay_tags: read_array(ar.read_u32::<LE>()?, ar, GameplayTag::read)?,
2230        })
2231    }
2232    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
2233        ar.write_u32::<LE>(self.gameplay_tags.len() as u32)?;
2234        for entry in &self.gameplay_tags {
2235            entry.write(ar)?;
2236        }
2237        Ok(())
2238    }
2239}
2240
2241#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2242pub struct UniqueNetIdRepl {
2243    pub inner: Option<UniqueNetIdReplInner>,
2244}
2245#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2246pub struct UniqueNetIdReplInner {
2247    pub size: std::num::NonZeroU32,
2248    pub type_: String,
2249    pub contents: String,
2250}
2251impl UniqueNetIdRepl {
2252    #[cfg_attr(
2253        feature = "tracing",
2254        instrument(name = "UniqueNetIdRepl_read", skip_all)
2255    )]
2256    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
2257        let size = ar.read_u32::<LE>()?;
2258        let inner = if let Ok(size) = size.try_into() {
2259            Some(UniqueNetIdReplInner {
2260                size,
2261                type_: ar.read_string()?,
2262                contents: ar.read_string()?,
2263            })
2264        } else {
2265            None
2266        };
2267        Ok(Self { inner })
2268    }
2269    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
2270        match &self.inner {
2271            Some(inner) => {
2272                ar.write_u32::<LE>(inner.size.into())?;
2273                ar.write_string(&inner.type_)?;
2274                ar.write_string(&inner.contents)?;
2275            }
2276            None => ar.write_u32::<LE>(0)?,
2277        }
2278        Ok(())
2279    }
2280}
2281
2282#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2283pub struct FFormatArgumentData {
2284    name: String,
2285    value: FFormatArgumentDataValue,
2286}
2287impl FFormatArgumentData {
2288    #[cfg_attr(
2289        feature = "tracing",
2290        instrument(name = "FFormatArgumentData_read", skip_all)
2291    )]
2292    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
2293        Ok(Self {
2294            name: read_string(ar)?,
2295            value: FFormatArgumentDataValue::read(ar)?,
2296        })
2297    }
2298}
2299impl FFormatArgumentData {
2300    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
2301        write_string(ar, &self.name)?;
2302        self.value.write(ar)?;
2303        Ok(())
2304    }
2305}
2306// very similar to FFormatArgumentValue but serializes ints as 32 bits (TODO changes to 64 bit
2307// again at some later UE version)
2308#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2309pub enum FFormatArgumentDataValue {
2310    Int(i32),
2311    UInt(u32),
2312    Float(Float),
2313    Double(Double),
2314    Text(std::boxed::Box<Text>),
2315    Gender(u64),
2316}
2317impl FFormatArgumentDataValue {
2318    #[cfg_attr(
2319        feature = "tracing",
2320        instrument(name = "FFormatArgumentDataValue_read", skip_all)
2321    )]
2322    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
2323        let type_ = ar.read_u8()?;
2324        match type_ {
2325            0 => Ok(Self::Int(ar.read_i32::<LE>()?)),
2326            1 => Ok(Self::UInt(ar.read_u32::<LE>()?)),
2327            2 => Ok(Self::Float(ar.read_f32::<LE>()?.into())),
2328            3 => Ok(Self::Double(ar.read_f64::<LE>()?.into())),
2329            4 => Ok(Self::Text(std::boxed::Box::new(Text::read(ar)?))),
2330            5 => Ok(Self::Gender(ar.read_u64::<LE>()?)),
2331            _ => Err(Error::Other(format!(
2332                "unimplemented variant for FFormatArgumentDataValue 0x{type_:x}"
2333            ))),
2334        }
2335    }
2336}
2337impl FFormatArgumentDataValue {
2338    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
2339        match self {
2340            Self::Int(value) => {
2341                ar.write_u8(0)?;
2342                ar.write_i32::<LE>(*value)?;
2343            }
2344            Self::UInt(value) => {
2345                ar.write_u8(1)?;
2346                ar.write_u32::<LE>(*value)?;
2347            }
2348            Self::Float(value) => {
2349                ar.write_u8(2)?;
2350                ar.write_f32::<LE>((*value).into())?;
2351            }
2352            Self::Double(value) => {
2353                ar.write_u8(3)?;
2354                ar.write_f64::<LE>((*value).into())?;
2355            }
2356            Self::Text(value) => {
2357                ar.write_u8(4)?;
2358                value.write(ar)?;
2359            }
2360            Self::Gender(value) => {
2361                ar.write_u8(5)?;
2362                ar.write_u64::<LE>(*value)?;
2363            }
2364        };
2365        Ok(())
2366    }
2367}
2368
2369#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2370pub enum FFormatArgumentValue {
2371    Int(i64),
2372    UInt(u64),
2373    Float(Float),
2374    Double(Double),
2375    Text(std::boxed::Box<Text>),
2376    Gender(u64),
2377}
2378
2379impl FFormatArgumentValue {
2380    #[cfg_attr(
2381        feature = "tracing",
2382        instrument(name = "FFormatArgumentValue_read", skip_all)
2383    )]
2384    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
2385        let type_ = ar.read_u8()?;
2386        match type_ {
2387            0 => Ok(Self::Int(ar.read_i64::<LE>()?)),
2388            1 => Ok(Self::UInt(ar.read_u64::<LE>()?)),
2389            2 => Ok(Self::Float(ar.read_f32::<LE>()?.into())),
2390            3 => Ok(Self::Double(ar.read_f64::<LE>()?.into())),
2391            4 => Ok(Self::Text(std::boxed::Box::new(Text::read(ar)?))),
2392            5 => Ok(Self::Gender(ar.read_u64::<LE>()?)),
2393            _ => Err(Error::Other(format!(
2394                "unimplemented variant for FFormatArgumentValue 0x{type_:x}"
2395            ))),
2396        }
2397    }
2398}
2399impl FFormatArgumentValue {
2400    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
2401        match self {
2402            Self::Int(value) => {
2403                ar.write_u8(0)?;
2404                ar.write_i64::<LE>(*value)?;
2405            }
2406            Self::UInt(value) => {
2407                ar.write_u8(1)?;
2408                ar.write_u64::<LE>(*value)?;
2409            }
2410            Self::Float(value) => {
2411                ar.write_u8(2)?;
2412                ar.write_f32::<LE>((*value).into())?;
2413            }
2414            Self::Double(value) => {
2415                ar.write_u8(3)?;
2416                ar.write_f64::<LE>((*value).into())?;
2417            }
2418            Self::Text(value) => {
2419                ar.write_u8(4)?;
2420                value.write(ar)?;
2421            }
2422            Self::Gender(value) => {
2423                ar.write_u8(5)?;
2424                ar.write_u64::<LE>(*value)?;
2425            }
2426        };
2427        Ok(())
2428    }
2429}
2430
2431#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2432pub struct FNumberFormattingOptions {
2433    always_sign: bool,
2434    use_grouping: bool,
2435    rounding_mode: i8, // TODO enum ERoundingMode
2436    minimum_integral_digits: i32,
2437    maximum_integral_digits: i32,
2438    minimum_fractional_digits: i32,
2439    maximum_fractional_digits: i32,
2440}
2441impl FNumberFormattingOptions {
2442    #[cfg_attr(
2443        feature = "tracing",
2444        instrument(name = "FNumberFormattingOptions_read", skip_all)
2445    )]
2446    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
2447        Ok(Self {
2448            always_sign: ar.read_u32::<LE>()? != 0,
2449            use_grouping: ar.read_u32::<LE>()? != 0,
2450            rounding_mode: ar.read_i8()?,
2451            minimum_integral_digits: ar.read_i32::<LE>()?,
2452            maximum_integral_digits: ar.read_i32::<LE>()?,
2453            minimum_fractional_digits: ar.read_i32::<LE>()?,
2454            maximum_fractional_digits: ar.read_i32::<LE>()?,
2455        })
2456    }
2457}
2458impl FNumberFormattingOptions {
2459    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
2460        ar.write_u32::<LE>(self.always_sign as u32)?;
2461        ar.write_u32::<LE>(self.use_grouping as u32)?;
2462        ar.write_i8(self.rounding_mode)?;
2463        ar.write_i32::<LE>(self.minimum_integral_digits)?;
2464        ar.write_i32::<LE>(self.maximum_integral_digits)?;
2465        ar.write_i32::<LE>(self.minimum_fractional_digits)?;
2466        ar.write_i32::<LE>(self.maximum_fractional_digits)?;
2467        Ok(())
2468    }
2469}
2470
2471#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2472pub struct Text {
2473    flags: u32,
2474    variant: TextVariant,
2475}
2476#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2477pub enum TextVariant {
2478    // -0x1
2479    None {
2480        culture_invariant: Option<String>,
2481    },
2482    // 0x0
2483    Base {
2484        namespace: (String, Vec<u8>),
2485        key: String,
2486        source_string: String,
2487    },
2488    // 0x3
2489    ArgumentFormat {
2490        // aka ArgumentDataFormat
2491        format_text: std::boxed::Box<Text>,
2492        arguments: Vec<FFormatArgumentData>,
2493    },
2494    // 0x4
2495    AsNumber {
2496        source_value: FFormatArgumentValue,
2497        format_options: Option<FNumberFormattingOptions>,
2498        culture_name: String,
2499    },
2500    // 0x7
2501    AsDate {
2502        source_date_time: DateTime,
2503        date_style: i8, // TODO EDateTimeStyle::Type
2504        time_zone: String,
2505        culture_name: String,
2506    },
2507    StringTableEntry {
2508        // 0xb
2509        table: String,
2510        key: String,
2511    },
2512}
2513
2514impl Text {
2515    #[cfg_attr(feature = "tracing", instrument(name = "Text_read", skip_all))]
2516    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
2517        let flags = ar.read_u32::<LE>()?;
2518        let text_history_type = ar.read_i8()?;
2519        let variant = match text_history_type {
2520            -0x1 => Ok(TextVariant::None {
2521                culture_invariant: (ar.read_u32::<LE>()? != 0) // bHasCultureInvariantString
2522                    .then(|| read_string(ar))
2523                    .transpose()?,
2524            }),
2525            0x0 => Ok(TextVariant::Base {
2526                namespace: read_string_trailing(ar)?,
2527                key: read_string(ar)?,
2528                source_string: read_string(ar)?,
2529            }),
2530            0x3 => Ok(TextVariant::ArgumentFormat {
2531                format_text: std::boxed::Box::new(Text::read(ar)?),
2532                arguments: read_array(ar.read_u32::<LE>()?, ar, FFormatArgumentData::read)?,
2533            }),
2534            0x4 => Ok(TextVariant::AsNumber {
2535                source_value: FFormatArgumentValue::read(ar)?,
2536                format_options: (ar.read_u32::<LE>()? != 0) // bHasFormatOptions
2537                    .then(|| FNumberFormattingOptions::read(ar))
2538                    .transpose()?,
2539                culture_name: ar.read_string()?,
2540            }),
2541            0x7 => Ok(TextVariant::AsDate {
2542                source_date_time: ar.read_u64::<LE>()?,
2543                date_style: ar.read_i8()?,
2544                time_zone: ar.read_string()?,
2545                culture_name: ar.read_string()?,
2546            }),
2547            0xb => Ok({
2548                TextVariant::StringTableEntry {
2549                    table: ar.read_string()?,
2550                    key: read_string(ar)?,
2551                }
2552            }),
2553            _ => Err(Error::Other(format!(
2554                "unimplemented variant for FTextHistory 0x{text_history_type:x}"
2555            ))),
2556        }?;
2557        Ok(Self { flags, variant })
2558    }
2559}
2560impl Text {
2561    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
2562        ar.write_u32::<LE>(self.flags)?;
2563        match &self.variant {
2564            TextVariant::None { culture_invariant } => {
2565                ar.write_i8(-0x1)?;
2566                ar.write_u32::<LE>(culture_invariant.is_some() as u32)?;
2567                if let Some(culture_invariant) = culture_invariant {
2568                    write_string(ar, culture_invariant)?;
2569                }
2570            }
2571            TextVariant::Base {
2572                namespace,
2573                key,
2574                source_string,
2575            } => {
2576                ar.write_i8(0x0)?;
2577                // This particular string sometimes includes the trailing null byte and sometimes
2578                // does not. To preserve byte-for-byte equality we save the trailing bytes (null or
2579                // not) to the JSON so they can be retored later.
2580                write_string_trailing(ar, &namespace.0, Some(&namespace.1))?;
2581                write_string(ar, key)?;
2582                write_string(ar, source_string)?;
2583            }
2584            TextVariant::ArgumentFormat {
2585                format_text,
2586                arguments,
2587            } => {
2588                ar.write_i8(0x3)?;
2589                format_text.write(ar)?;
2590                ar.write_u32::<LE>(arguments.len() as u32)?;
2591                for a in arguments {
2592                    a.write(ar)?;
2593                }
2594            }
2595            TextVariant::AsNumber {
2596                source_value,
2597                format_options,
2598                culture_name,
2599            } => {
2600                ar.write_i8(0x4)?;
2601                source_value.write(ar)?;
2602                ar.write_u32::<LE>(format_options.is_some() as u32)?;
2603                if let Some(format_options) = format_options {
2604                    format_options.write(ar)?;
2605                }
2606                ar.write_string(culture_name)?;
2607            }
2608            TextVariant::AsDate {
2609                source_date_time,
2610                date_style,
2611                time_zone,
2612                culture_name,
2613            } => {
2614                ar.write_i8(0x7)?;
2615                ar.write_u64::<LE>(*source_date_time)?;
2616                ar.write_i8(*date_style)?;
2617                ar.write_string(time_zone)?;
2618                ar.write_string(culture_name)?;
2619            }
2620            TextVariant::StringTableEntry { table, key } => {
2621                ar.write_i8(0xb)?;
2622                ar.write_string(table)?;
2623                write_string(ar, key)?;
2624            }
2625        }
2626        Ok(())
2627    }
2628}
2629
2630/// Just a plain byte, or an enum in which case the variant will be a String
2631#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2632#[serde(untagged)]
2633pub enum Byte {
2634    Byte(u8),
2635    Label(String),
2636}
2637/// Vectorized [`Byte`]
2638#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2639pub enum ByteArray {
2640    Byte(Vec<u8>),
2641    Label(Vec<String>),
2642}
2643
2644#[derive(Debug, Clone, PartialEq, Serialize)]
2645#[serde(untagged)]
2646#[serde(bound(serialize = "T::ObjectRef: Serialize, T::SoftObjectPath: Serialize"))]
2647pub enum StructValue<T: ArchiveType = SaveGameArchiveType> {
2648    Guid(FGuid),
2649    DateTime(DateTime),
2650    Timespan(Timespan),
2651    Vector2D(Vector2D),
2652    Vector(Vector),
2653    Vector4(Vector4),
2654    IntVector(IntVector),
2655    Box(Box),
2656    Box2D(Box2D),
2657    IntPoint(IntPoint),
2658    Quat(Quat),
2659    LinearColor(LinearColor),
2660    Color(Color),
2661    Rotator(Rotator),
2662    SoftObjectPath(T::SoftObjectPath),
2663    SoftClassPath(T::SoftObjectPath),
2664    GameplayTagContainer(GameplayTagContainer),
2665    UniqueNetIdRepl(UniqueNetIdRepl),
2666    KeyHandleMap(FKeyHandleMap),
2667    RichCurveKey(FRichCurveKey),
2668    SkeletalMeshSamplingLODBuiltData(FSkeletalMeshSamplingLODBuiltData),
2669    PerPlatformFloat(FPerPlatformFloat),
2670    /// Raw struct data for other unknown structs serialized with HasBinaryOrNativeSerialize
2671    Raw(Vec<u8>),
2672    /// User defined struct which is simply a list of properties
2673    Struct(Properties<T>),
2674}
2675
2676/// Vectorized properties to avoid storing the variant with each value
2677#[derive(Debug, Clone, PartialEq, Serialize)]
2678#[serde(untagged)]
2679#[serde(bound(serialize = "T::ObjectRef: Serialize, T::SoftObjectPath: Serialize"))]
2680pub enum ValueVec<T: ArchiveType = SaveGameArchiveType> {
2681    Int8(Vec<Int8>),
2682    Int16(Vec<Int16>),
2683    Int(Vec<Int>),
2684    Int64(Vec<Int64>),
2685    UInt8(Vec<UInt8>),
2686    UInt16(Vec<UInt16>),
2687    UInt32(Vec<UInt32>),
2688    UInt64(Vec<UInt64>),
2689    Float(Vec<Float>),
2690    Double(Vec<Double>),
2691    Bool(Vec<bool>),
2692    Byte(ByteArray),
2693    Enum(Vec<Enum>),
2694    Str(Vec<String>),
2695    Text(Vec<Text>),
2696    SoftObject(Vec<T::SoftObjectPath>),
2697    Name(Vec<String>),
2698    Object(Vec<T::ObjectRef>),
2699    Box(Vec<Box>),
2700    Box2D(Vec<Box2D>),
2701    Struct(Vec<StructValue<T>>),
2702}
2703
2704impl<T: ArchiveType> StructValue<T> {
2705    #[cfg_attr(feature = "tracing", instrument(name = "StructValue_read", skip_all))]
2706    fn read<A: ArchiveReader<ArchiveType = T>>(
2707        ar: &mut A,
2708        t: &StructType,
2709    ) -> Result<StructValue<T>> {
2710        Ok(match t {
2711            StructType::Guid => StructValue::Guid(FGuid::read(ar)?),
2712            StructType::DateTime => StructValue::DateTime(ar.read_u64::<LE>()?),
2713            StructType::Timespan => StructValue::Timespan(ar.read_i64::<LE>()?),
2714            StructType::Vector2D => StructValue::Vector2D(Vector2D::read(ar)?),
2715            StructType::Vector => StructValue::Vector(Vector::read(ar)?),
2716            StructType::Vector4 => StructValue::Vector4(Vector4::read(ar)?),
2717            StructType::IntVector => StructValue::IntVector(IntVector::read(ar)?),
2718            StructType::Box => StructValue::Box(Box::read(ar)?),
2719            StructType::Box2D => StructValue::Box2D(Box2D::read(ar)?),
2720            StructType::IntPoint => StructValue::IntPoint(IntPoint::read(ar)?),
2721            StructType::Quat => StructValue::Quat(Quat::read(ar)?),
2722            StructType::LinearColor => StructValue::LinearColor(LinearColor::read(ar)?),
2723            StructType::Color => StructValue::Color(Color::read(ar)?),
2724            StructType::Rotator => StructValue::Rotator(Rotator::read(ar)?),
2725            StructType::SoftObjectPath => StructValue::SoftObjectPath(ar.read_soft_object_path()?),
2726            StructType::SoftClassPath => StructValue::SoftClassPath(ar.read_soft_object_path()?),
2727            StructType::GameplayTagContainer => {
2728                StructValue::GameplayTagContainer(GameplayTagContainer::read(ar)?)
2729            }
2730            StructType::UniqueNetIdRepl => StructValue::UniqueNetIdRepl(UniqueNetIdRepl::read(ar)?),
2731            StructType::KeyHandleMap => StructValue::KeyHandleMap(FKeyHandleMap::read(ar)?),
2732            StructType::RichCurveKey => StructValue::RichCurveKey(FRichCurveKey::read(ar)?),
2733            StructType::SkeletalMeshSamplingLODBuiltData => {
2734                StructValue::SkeletalMeshSamplingLODBuiltData(
2735                    FSkeletalMeshSamplingLODBuiltData::read(ar)?,
2736                )
2737            }
2738            StructType::PerPlatformFloat => {
2739                StructValue::PerPlatformFloat(FPerPlatformFloat::read(ar)?)
2740            }
2741            StructType::Raw(_) => unreachable!("should be handled at property level"),
2742            StructType::Struct(_) => StructValue::Struct(read_properties_until_none(ar)?),
2743        })
2744    }
2745    fn write<A: ArchiveWriter<ArchiveType = T>>(&self, ar: &mut A) -> Result<()> {
2746        match self {
2747            StructValue::Guid(v) => v.write(ar)?,
2748            StructValue::DateTime(v) => ar.write_u64::<LE>(*v)?,
2749            StructValue::Timespan(v) => ar.write_i64::<LE>(*v)?,
2750            StructValue::Vector2D(v) => v.write(ar)?,
2751            StructValue::Vector(v) => v.write(ar)?,
2752            StructValue::Vector4(v) => v.write(ar)?,
2753            StructValue::IntVector(v) => v.write(ar)?,
2754            StructValue::Box(v) => v.write(ar)?,
2755            StructValue::Box2D(v) => v.write(ar)?,
2756            StructValue::IntPoint(v) => v.write(ar)?,
2757            StructValue::Quat(v) => v.write(ar)?,
2758            StructValue::LinearColor(v) => v.write(ar)?,
2759            StructValue::Color(v) => v.write(ar)?,
2760            StructValue::Rotator(v) => v.write(ar)?,
2761            StructValue::SoftObjectPath(v) => ar.write_soft_object_path(v)?,
2762            StructValue::SoftClassPath(v) => ar.write_soft_object_path(v)?,
2763            StructValue::GameplayTagContainer(v) => v.write(ar)?,
2764            StructValue::UniqueNetIdRepl(v) => v.write(ar)?,
2765            StructValue::KeyHandleMap(v) => v.write(ar)?,
2766            StructValue::RichCurveKey(v) => v.write(ar)?,
2767            StructValue::SkeletalMeshSamplingLODBuiltData(v) => v.write(ar)?,
2768            StructValue::PerPlatformFloat(v) => v.write(ar)?,
2769            StructValue::Raw(v) => ar.write_all(v)?,
2770            StructValue::Struct(v) => write_properties_none_terminated(ar, v)?,
2771        }
2772        Ok(())
2773    }
2774}
2775impl<T: ArchiveType> ValueVec<T> {
2776    #[cfg_attr(feature = "tracing", instrument(name = "ValueVec_read", skip_all))]
2777    fn read<A: ArchiveReader<ArchiveType = T>>(
2778        ar: &mut A,
2779        t: &PropertyType,
2780        size: u32,
2781        count: u32,
2782    ) -> Result<ValueVec<T>> {
2783        Ok(match t {
2784            PropertyType::IntProperty => {
2785                ValueVec::Int(read_array(count, ar, |r| Ok(r.read_i32::<LE>()?))?)
2786            }
2787            PropertyType::Int16Property => {
2788                ValueVec::Int16(read_array(count, ar, |r| Ok(r.read_i16::<LE>()?))?)
2789            }
2790            PropertyType::Int64Property => {
2791                ValueVec::Int64(read_array(count, ar, |r| Ok(r.read_i64::<LE>()?))?)
2792            }
2793            PropertyType::UInt16Property => {
2794                ValueVec::UInt16(read_array(count, ar, |r| Ok(r.read_u16::<LE>()?))?)
2795            }
2796            PropertyType::UInt32Property => {
2797                ValueVec::UInt32(read_array(count, ar, |r| Ok(r.read_u32::<LE>()?))?)
2798            }
2799            PropertyType::FloatProperty => {
2800                ValueVec::Float(read_array(count, ar, |r| Ok(r.read_f32::<LE>()?.into()))?)
2801            }
2802            PropertyType::DoubleProperty => {
2803                ValueVec::Double(read_array(count, ar, |r| Ok(r.read_f64::<LE>()?.into()))?)
2804            }
2805            PropertyType::BoolProperty => {
2806                ValueVec::Bool(read_array(count, ar, |r| Ok(r.read_u8()? > 0))?)
2807            }
2808            PropertyType::ByteProperty => {
2809                if size == count {
2810                    ValueVec::Byte(ByteArray::Byte(read_array(
2811                        count,
2812                        ar,
2813                        |r| Ok(r.read_u8()?),
2814                    )?))
2815                } else {
2816                    ValueVec::Byte(ByteArray::Label(read_array(count, ar, |r| {
2817                        r.read_string()
2818                    })?))
2819                }
2820            }
2821            PropertyType::EnumProperty => {
2822                ValueVec::Enum(read_array(count, ar, |r| r.read_string())?)
2823            }
2824            PropertyType::StrProperty => ValueVec::Str(read_array(count, ar, |r| read_string(r))?),
2825            PropertyType::TextProperty => ValueVec::Text(read_array(count, ar, Text::read)?),
2826            PropertyType::SoftObjectProperty => {
2827                ValueVec::SoftObject(read_array(count, ar, |r| r.read_soft_object_path())?)
2828            }
2829            PropertyType::NameProperty => {
2830                ValueVec::Name(read_array(count, ar, |r| r.read_string())?)
2831            }
2832            PropertyType::ObjectProperty => {
2833                ValueVec::Object(read_array(count, ar, |r| r.read_object_ref())?)
2834            }
2835            _ => return Err(Error::UnknownVecType(format!("{t:?}"))),
2836        })
2837    }
2838    fn write<A: ArchiveWriter<ArchiveType = T>>(&self, ar: &mut A) -> Result<()> {
2839        match &self {
2840            ValueVec::Int8(v) => {
2841                ar.write_u32::<LE>(v.len() as u32)?;
2842                for i in v {
2843                    ar.write_i8(*i)?;
2844                }
2845            }
2846            ValueVec::Int16(v) => {
2847                ar.write_u32::<LE>(v.len() as u32)?;
2848                for i in v {
2849                    ar.write_i16::<LE>(*i)?;
2850                }
2851            }
2852            ValueVec::Int(v) => {
2853                ar.write_u32::<LE>(v.len() as u32)?;
2854                for i in v {
2855                    ar.write_i32::<LE>(*i)?;
2856                }
2857            }
2858            ValueVec::Int64(v) => {
2859                ar.write_u32::<LE>(v.len() as u32)?;
2860                for i in v {
2861                    ar.write_i64::<LE>(*i)?;
2862                }
2863            }
2864            ValueVec::UInt8(v) => {
2865                ar.write_u32::<LE>(v.len() as u32)?;
2866                for i in v {
2867                    ar.write_u8(*i)?;
2868                }
2869            }
2870            ValueVec::UInt16(v) => {
2871                ar.write_u32::<LE>(v.len() as u32)?;
2872                for i in v {
2873                    ar.write_u16::<LE>(*i)?;
2874                }
2875            }
2876            ValueVec::UInt32(v) => {
2877                ar.write_u32::<LE>(v.len() as u32)?;
2878                for i in v {
2879                    ar.write_u32::<LE>(*i)?;
2880                }
2881            }
2882            ValueVec::UInt64(v) => {
2883                ar.write_u32::<LE>(v.len() as u32)?;
2884                for i in v {
2885                    ar.write_u64::<LE>(*i)?;
2886                }
2887            }
2888            ValueVec::Float(v) => {
2889                ar.write_u32::<LE>(v.len() as u32)?;
2890                for i in v {
2891                    ar.write_f32::<LE>((*i).into())?;
2892                }
2893            }
2894            ValueVec::Double(v) => {
2895                ar.write_u32::<LE>(v.len() as u32)?;
2896                for i in v {
2897                    ar.write_f64::<LE>((*i).into())?;
2898                }
2899            }
2900            ValueVec::Bool(v) => {
2901                ar.write_u32::<LE>(v.len() as u32)?;
2902                for b in v {
2903                    ar.write_u8(*b as u8)?;
2904                }
2905            }
2906            ValueVec::Byte(v) => match v {
2907                ByteArray::Byte(b) => {
2908                    ar.write_u32::<LE>(b.len() as u32)?;
2909                    for b in b {
2910                        ar.write_u8(*b)?;
2911                    }
2912                }
2913                ByteArray::Label(l) => {
2914                    ar.write_u32::<LE>(l.len() as u32)?;
2915                    for l in l {
2916                        ar.write_string(l)?;
2917                    }
2918                }
2919            },
2920            ValueVec::Enum(v) => {
2921                ar.write_u32::<LE>(v.len() as u32)?;
2922                for i in v {
2923                    ar.write_string(i)?;
2924                }
2925            }
2926            ValueVec::Str(v) => {
2927                ar.write_u32::<LE>(v.len() as u32)?;
2928                for i in v {
2929                    write_string(ar, i)?;
2930                }
2931            }
2932            ValueVec::Name(v) => {
2933                ar.write_u32::<LE>(v.len() as u32)?;
2934                for i in v {
2935                    ar.write_string(i)?;
2936                }
2937            }
2938            ValueVec::Object(v) => {
2939                ar.write_u32::<LE>(v.len() as u32)?;
2940                for i in v {
2941                    ar.write_object_ref(i)?;
2942                }
2943            }
2944            ValueVec::Text(v) => {
2945                ar.write_u32::<LE>(v.len() as u32)?;
2946                for i in v {
2947                    i.write(ar)?;
2948                }
2949            }
2950            ValueVec::SoftObject(v) => {
2951                ar.write_u32::<LE>(v.len() as u32)?;
2952                for i in v {
2953                    ar.write_soft_object_path(i)?;
2954                }
2955            }
2956            ValueVec::Box(v) => {
2957                ar.write_u32::<LE>(v.len() as u32)?;
2958                for i in v {
2959                    i.write(ar)?;
2960                }
2961            }
2962            ValueVec::Box2D(v) => {
2963                ar.write_u32::<LE>(v.len() as u32)?;
2964                for i in v {
2965                    i.write(ar)?;
2966                }
2967            }
2968            ValueVec::Struct(v) => {
2969                ar.write_u32::<LE>(v.len() as u32)?;
2970                for i in v {
2971                    i.write(ar)?;
2972                }
2973            }
2974        }
2975        Ok(())
2976    }
2977}
2978impl<T: ArchiveType> ValueVec<T> {
2979    #[cfg_attr(
2980        feature = "tracing",
2981        instrument(name = "ValueVec_read_array", skip_all)
2982    )]
2983    fn read_array<A: ArchiveReader<ArchiveType = T>>(
2984        ar: &mut A,
2985        tag: PropertyTagDataFull,
2986        size: u32,
2987    ) -> Result<(ValueVec<T>, Option<PropertyTagDataFull>)> {
2988        let count = ar.read_u32::<LE>()?;
2989        Ok(match tag {
2990            PropertyTagDataFull::Struct { struct_type, id: _ } => {
2991                let (struct_type, updated) = if !ar.version().property_tag() {
2992                    // outer tag shows Struct but struct_type is unknown
2993                    if ar.version().array_inner_tag() {
2994                        // this is where the actual inner struct type is determined
2995                        let inner_tag = PropertyTagFull::read(ar)?.unwrap();
2996                        match inner_tag.data {
2997                            PropertyTagDataFull::Struct { struct_type, id } => {
2998                                // Return the discovered type information to update the outer tag
2999                                (
3000                                    struct_type.clone(),
3001                                    Some(PropertyTagDataFull::Struct { struct_type, id }),
3002                                )
3003                            }
3004                            _ => {
3005                                return Err(Error::Other(format!(
3006                                    "expected StructProperty tag, found {inner_tag:?}"
3007                                )))
3008                            }
3009                        }
3010                    } else {
3011                        // TODO prior to 4.12 struct type is unknown so should be able to
3012                        // manually specify like Sets/Maps
3013                        (StructType::Struct(None), None)
3014                    }
3015                } else {
3016                    (struct_type, None)
3017                };
3018
3019                let mut value = vec![];
3020                for _ in 0..count {
3021                    value.push(StructValue::read(ar, &struct_type)?);
3022                }
3023                (ValueVec::Struct(value), updated)
3024            }
3025            _ => (ValueVec::read(ar, &tag.basic_type(), size, count)?, None),
3026        })
3027    }
3028    fn write_array<A: ArchiveWriter<ArchiveType = T>>(
3029        &self,
3030        ar: &mut A,
3031        tag: &PropertyTagFull,
3032    ) -> Result<()> {
3033        match &self {
3034            ValueVec::Struct(value) => {
3035                ar.write_u32::<LE>(value.len() as u32)?;
3036
3037                if !ar.version().property_tag() && ar.version().array_inner_tag() {
3038                    // Extract struct type info from tag for older UE versions
3039                    let (struct_type, id) = match &tag.data {
3040                        PropertyTagDataFull::Array(inner) => match &**inner {
3041                            PropertyTagDataFull::Struct { struct_type, id } => (struct_type, id),
3042                            _ => {
3043                                return Err(Error::Other(
3044                                    "Array tag must contain Struct type".into(),
3045                                ))
3046                            }
3047                        },
3048                        _ => return Err(Error::Other("Expected Array tag".into())),
3049                    };
3050
3051                    // Write inner property tag for older format
3052                    ar.write_string(&tag.name)?;
3053                    PropertyType::StructProperty.write(ar)?;
3054
3055                    // Write placeholder size
3056                    let size_pos = ar.stream_position()?;
3057                    ar.write_u32::<LE>(0)?;
3058                    ar.write_u32::<LE>(0)?;
3059                    struct_type.write(ar)?;
3060                    id.write(ar)?;
3061                    ar.write_u8(0)?;
3062
3063                    // Write data and measure size
3064                    let data_start = ar.stream_position()?;
3065                    for v in value {
3066                        v.write(ar)?;
3067                    }
3068                    let data_end = ar.stream_position()?;
3069                    let size = (data_end - data_start) as u32;
3070
3071                    // Seek back and write actual size
3072                    ar.seek(std::io::SeekFrom::Start(size_pos))?;
3073                    ar.write_u32::<LE>(size)?;
3074                    ar.seek(std::io::SeekFrom::Start(data_end))?;
3075                } else {
3076                    for v in value {
3077                        v.write(ar)?;
3078                    }
3079                }
3080            }
3081            _ => {
3082                self.write(ar)?;
3083            }
3084        }
3085        Ok(())
3086    }
3087    #[cfg_attr(feature = "tracing", instrument(name = "ValueVec_read_set", skip_all))]
3088    fn read_set<A: ArchiveReader<ArchiveType = T>>(
3089        ar: &mut A,
3090        t: &PropertyTagDataFull,
3091        size: u32,
3092    ) -> Result<ValueVec<T>> {
3093        let count = ar.read_u32::<LE>()?;
3094        Ok(match t {
3095            PropertyTagDataFull::Struct { struct_type, .. } => {
3096                ValueVec::Struct(read_array(count, ar, |r| {
3097                    StructValue::read(r, struct_type)
3098                })?)
3099            }
3100            _ => ValueVec::read(ar, &t.basic_type(), size, count)?,
3101        })
3102    }
3103}
3104
3105/// Properties consist of a value and are present in [`Root`] and [`StructValue::Struct`]
3106/// Property schemas (tags) are stored separately in [`PropertySchemas`]
3107#[derive(Debug, Clone, PartialEq, Serialize)]
3108#[serde(untagged)]
3109#[serde(bound(serialize = "T::ObjectRef: Serialize, T::SoftObjectPath: Serialize"))]
3110pub enum Property<T: ArchiveType = SaveGameArchiveType> {
3111    Int8(Int8),
3112    Int16(Int16),
3113    Int(Int),
3114    Int64(Int64),
3115    UInt8(UInt8),
3116    UInt16(UInt16),
3117    UInt32(UInt32),
3118    UInt64(UInt64),
3119    Float(Float),
3120    Double(Double),
3121    Bool(Bool),
3122    Byte(Byte),
3123    Enum(Enum),
3124    Str(String),
3125    FieldPath(FieldPath),
3126    SoftObject(T::SoftObjectPath),
3127    Name(String),
3128    Object(T::ObjectRef),
3129    Text(Text),
3130    Delegate(Delegate<T>),
3131    MulticastDelegate(MulticastDelegate<T>),
3132    MulticastInlineDelegate(MulticastInlineDelegate<T>),
3133    MulticastSparseDelegate(MulticastSparseDelegate<T>),
3134    Set(ValueVec<T>),
3135    Map(Vec<MapEntry<T>>),
3136    Struct(StructValue<T>),
3137    Array(ValueVec<T>),
3138    /// Raw property data when parsing fails
3139    Raw(Vec<u8>),
3140}
3141
3142impl<T: ArchiveType> Property<T> {
3143    #[cfg_attr(feature = "tracing", instrument(name = "Property_read", skip_all))]
3144    fn read<A: ArchiveReader<ArchiveType = T>>(
3145        ar: &mut A,
3146        tag: PropertyTagFull,
3147    ) -> Result<(Property<T>, Option<PropertyTagDataFull>)> {
3148        if tag.data.has_raw_struct() {
3149            let mut raw = vec![0; tag.size as usize];
3150            ar.read_exact(&mut raw)?;
3151            return Ok((Property::Raw(raw), None));
3152        }
3153        // Save the current position before attempting to parse
3154        let start_position = ar.stream_position()?;
3155
3156        // Try to parse the property directly from the stream
3157        let (inner, updated_tag_data) = match Self::try_read_inner(ar, &tag) {
3158            Ok(result) => result,
3159            Err(e) => {
3160                // Check if we should convert errors to raw properties
3161                if ar.error_to_raw() {
3162                    // Parsing failed, seek back to start and read raw data
3163                    if ar.log() {
3164                        eprintln!("Warning: Failed to parse property '{}': {}", ar.path(), e);
3165                    }
3166                    ar.seek(std::io::SeekFrom::Start(start_position))?;
3167                    let mut property_data = vec![0u8; tag.size as usize];
3168                    ar.read_exact(&mut property_data)?;
3169                    (Property::Raw(property_data), None)
3170                } else {
3171                    // Error mode: return the error immediately
3172                    return Err(e);
3173                }
3174            }
3175        };
3176
3177        Ok((inner, updated_tag_data))
3178    }
3179
3180    fn try_read_inner<A: ArchiveReader<ArchiveType = T>>(
3181        ar: &mut A,
3182        tag: &PropertyTagFull,
3183    ) -> Result<(Property<T>, Option<PropertyTagDataFull>)> {
3184        let (inner, updated_tag_data) = match &tag.data {
3185            // Collection types need special handling
3186            PropertyTagDataFull::Set { key_type } => {
3187                ar.read_u32::<LE>()?;
3188                (
3189                    Property::Set(ValueVec::read_set(ar, key_type, tag.size - 8)?),
3190                    None,
3191                )
3192            }
3193            PropertyTagDataFull::Map {
3194                key_type,
3195                value_type,
3196            } => {
3197                // used to serialize negative difference against template object/CDO
3198                let _keys_to_remove = read_array(ar.read_u32::<LE>()?, ar, |ar| {
3199                    Property::read_value(ar, key_type)
3200                })?;
3201                let count = ar.read_u32::<LE>()?;
3202                let mut value = vec![];
3203
3204                for _ in 0..count {
3205                    value.push(MapEntry::read(ar, key_type, value_type)?)
3206                }
3207
3208                (Property::Map(value), None)
3209            }
3210            PropertyTagDataFull::Array(data) => {
3211                let (array, updated_data) = ValueVec::read_array(ar, *data.clone(), tag.size - 4)?;
3212                (Property::Array(array), updated_data)
3213            }
3214            // Bool is special - value stored in tag for top-level properties
3215            PropertyTagDataFull::Bool(value) => (Property::Bool(*value), None),
3216            PropertyTagDataFull::Other(PropertyType::FieldPathProperty) => {
3217                (Property::FieldPath(FieldPath::read(ar)?), None)
3218            }
3219            PropertyTagDataFull::Other(PropertyType::DelegateProperty) => {
3220                (Property::Delegate(Delegate::read(ar)?), None)
3221            }
3222            PropertyTagDataFull::Other(PropertyType::MulticastDelegateProperty) => (
3223                Property::MulticastDelegate(MulticastDelegate::read(ar)?),
3224                None,
3225            ),
3226            PropertyTagDataFull::Other(PropertyType::MulticastInlineDelegateProperty) => (
3227                Property::MulticastInlineDelegate(MulticastInlineDelegate::read(ar)?),
3228                None,
3229            ),
3230            PropertyTagDataFull::Other(PropertyType::MulticastSparseDelegateProperty) => (
3231                Property::MulticastSparseDelegate(MulticastSparseDelegate::read(ar)?),
3232                None,
3233            ),
3234            // Everything else can use the shared read_value logic
3235            _ => (Property::read_value(ar, &tag.data)?, None),
3236        };
3237
3238        // If we got updated tag data (e.g., from array of structs), wrap it in an Array tag
3239        let updated_tag =
3240            updated_tag_data.map(|data| PropertyTagDataFull::Array(std::boxed::Box::new(data)));
3241
3242        Ok((inner, updated_tag))
3243    }
3244    fn write<A: ArchiveWriter<ArchiveType = T>>(
3245        &self,
3246        ar: &mut A,
3247        tag: &PropertyTagFull,
3248    ) -> Result<()> {
3249        match &self {
3250            Property::Set(value) => {
3251                ar.write_u32::<LE>(0)?;
3252                value.write(ar)?;
3253            }
3254            Property::Map(value) => {
3255                ar.write_u32::<LE>(0)?;
3256                ar.write_u32::<LE>(value.len() as u32)?;
3257                for v in value {
3258                    v.write(ar)?;
3259                }
3260            }
3261            Property::Array(value) => {
3262                value.write_array(ar, tag)?;
3263            }
3264            Property::Raw(value) => {
3265                ar.write_all(value)?;
3266            }
3267            Property::FieldPath(value) => {
3268                value.write(ar)?;
3269            }
3270            Property::Delegate(value) => {
3271                value.write(ar)?;
3272            }
3273            Property::MulticastDelegate(value) => {
3274                value.write(ar)?;
3275            }
3276            Property::MulticastInlineDelegate(value) => {
3277                value.write(ar)?;
3278            }
3279            Property::MulticastSparseDelegate(value) => {
3280                value.write(ar)?;
3281            }
3282            // Bool is special - it's stored in the tag, not in the data
3283            Property::Bool(_) => {}
3284            // Everything else uses shared write_value logic
3285            _ => self.write_value(ar)?,
3286        }
3287        Ok(())
3288    }
3289
3290    #[cfg_attr(
3291        feature = "tracing",
3292        instrument(name = "Property_read_value", skip_all)
3293    )]
3294    fn read_value<A: ArchiveReader<ArchiveType = T>>(
3295        ar: &mut A,
3296        t: &PropertyTagDataFull,
3297    ) -> Result<Property<T>> {
3298        Ok(match t {
3299            PropertyTagDataFull::Array(_) => {
3300                unreachable!("Arrays should be handled at property level")
3301            }
3302            PropertyTagDataFull::Set { .. } => {
3303                unreachable!("Sets should be handled at property level")
3304            }
3305            PropertyTagDataFull::Map { .. } => {
3306                unreachable!("Maps should be handled at property level")
3307            }
3308            PropertyTagDataFull::Struct { struct_type, .. } => {
3309                Property::Struct(StructValue::read(ar, struct_type)?)
3310            }
3311            PropertyTagDataFull::Byte(ref enum_type) => {
3312                let value = if enum_type.is_none() {
3313                    Byte::Byte(ar.read_u8()?)
3314                } else {
3315                    Byte::Label(ar.read_string()?)
3316                };
3317                Property::Byte(value)
3318            }
3319            PropertyTagDataFull::Enum(_, _) => Property::Enum(ar.read_string()?),
3320            PropertyTagDataFull::Bool(_) => Property::Bool(ar.read_u8()? > 0),
3321            PropertyTagDataFull::Other(property_type) => match property_type {
3322                PropertyType::BoolProperty
3323                | PropertyType::ByteProperty
3324                | PropertyType::EnumProperty
3325                | PropertyType::SetProperty
3326                | PropertyType::MapProperty
3327                | PropertyType::StructProperty
3328                | PropertyType::ArrayProperty
3329                | PropertyType::FieldPathProperty
3330                | PropertyType::DelegateProperty
3331                | PropertyType::MulticastDelegateProperty
3332                | PropertyType::MulticastInlineDelegateProperty
3333                | PropertyType::MulticastSparseDelegateProperty => {
3334                    unreachable!("Should be handled by dedicated tag variants")
3335                }
3336                PropertyType::Int8Property => Property::Int8(ar.read_i8()?),
3337                PropertyType::Int16Property => Property::Int16(ar.read_i16::<LE>()?),
3338                PropertyType::IntProperty => Property::Int(ar.read_i32::<LE>()?),
3339                PropertyType::Int64Property => Property::Int64(ar.read_i64::<LE>()?),
3340                PropertyType::UInt8Property => Property::UInt8(ar.read_u8()?),
3341                PropertyType::UInt16Property => Property::UInt16(ar.read_u16::<LE>()?),
3342                PropertyType::UInt32Property => Property::UInt32(ar.read_u32::<LE>()?),
3343                PropertyType::UInt64Property => Property::UInt64(ar.read_u64::<LE>()?),
3344                PropertyType::FloatProperty => Property::Float(ar.read_f32::<LE>()?.into()),
3345                PropertyType::DoubleProperty => Property::Double(ar.read_f64::<LE>()?.into()),
3346                PropertyType::NameProperty => Property::Name(ar.read_string()?),
3347                PropertyType::StrProperty => Property::Str(read_string(ar)?),
3348                PropertyType::SoftObjectProperty => {
3349                    Property::SoftObject(ar.read_soft_object_path()?)
3350                }
3351                PropertyType::ObjectProperty => Property::Object(ar.read_object_ref()?),
3352                PropertyType::TextProperty => Property::Text(Text::read(ar)?),
3353            },
3354        })
3355    }
3356
3357    fn write_value<A: ArchiveWriter<ArchiveType = T>>(&self, ar: &mut A) -> Result<()> {
3358        match &self {
3359            Property::Int8(v) => ar.write_i8(*v)?,
3360            Property::Int16(v) => ar.write_i16::<LE>(*v)?,
3361            Property::Int(v) => ar.write_i32::<LE>(*v)?,
3362            Property::Int64(v) => ar.write_i64::<LE>(*v)?,
3363            Property::UInt8(v) => ar.write_u8(*v)?,
3364            Property::UInt16(v) => ar.write_u16::<LE>(*v)?,
3365            Property::UInt32(v) => ar.write_u32::<LE>(*v)?,
3366            Property::UInt64(v) => ar.write_u64::<LE>(*v)?,
3367            Property::Float(v) => ar.write_f32::<LE>((*v).into())?,
3368            Property::Double(v) => ar.write_f64::<LE>((*v).into())?,
3369            Property::Bool(v) => ar.write_u8(u8::from(*v))?,
3370            Property::Byte(v) => match v {
3371                Byte::Byte(b) => ar.write_u8(*b)?,
3372                Byte::Label(l) => ar.write_string(l)?,
3373            },
3374            Property::Enum(v) => ar.write_string(v)?,
3375            Property::Name(v) => ar.write_string(v)?,
3376            Property::Str(v) => write_string(ar, v)?,
3377            Property::SoftObject(v) => ar.write_soft_object_path(v)?,
3378            Property::Object(v) => ar.write_object_ref(v)?,
3379            Property::Text(v) => v.write(ar)?,
3380            Property::Struct(v) => v.write(ar)?,
3381            Property::Set(_)
3382            | Property::Map(_)
3383            | Property::Array(_)
3384            | Property::FieldPath(_)
3385            | Property::Delegate(_)
3386            | Property::MulticastDelegate(_)
3387            | Property::MulticastInlineDelegate(_)
3388            | Property::MulticastSparseDelegate(_)
3389            | Property::Raw(_) => {
3390                return Err(Error::Other(format!(
3391                    "Property variant {:?} cannot be written in value context (Maps/Sets/Arrays)",
3392                    self
3393                )))
3394            }
3395        };
3396        Ok(())
3397    }
3398}
3399
3400#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
3401pub struct CustomFormatData {
3402    pub id: FGuid,
3403    pub value: i32,
3404}
3405impl CustomFormatData {
3406    #[cfg_attr(
3407        feature = "tracing",
3408        instrument(name = "CustomFormatData_read", skip_all)
3409    )]
3410    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
3411        Ok(CustomFormatData {
3412            id: FGuid::read(ar)?,
3413            value: ar.read_i32::<LE>()?,
3414        })
3415    }
3416}
3417impl CustomFormatData {
3418    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
3419        self.id.write(ar)?;
3420        ar.write_i32::<LE>(self.value)?;
3421        Ok(())
3422    }
3423}
3424
3425#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
3426pub struct PackageVersion {
3427    ue4: u32,
3428    ue5: Option<u32>,
3429}
3430
3431pub trait VersionInfo {
3432    fn engine_version_major(&self) -> u16;
3433    fn engine_version_minor(&self) -> u16;
3434    fn engine_version_patch(&self) -> u16;
3435    fn package_file_version_ue4(&self) -> u32;
3436    fn package_file_version_ue5(&self) -> u32;
3437
3438    /// Whether the engine uses large world coordinates (FVector with doubles)
3439    fn large_world_coordinates(&self) -> bool {
3440        self.engine_version_major() >= 5
3441    }
3442
3443    /// Whether property tags include complete type names
3444    fn property_tag(&self) -> bool {
3445        // PROPERTY_TAG_COMPLETE_TYPE_NAME
3446        (self.engine_version_major(), self.engine_version_minor()) >= (5, 4)
3447    }
3448
3449    /// Whether property tags include GUIDs
3450    fn property_guid(&self) -> bool {
3451        // VER_UE4_PROPERTY_GUID_IN_PROPERTY_TAG
3452        (self.engine_version_major(), self.engine_version_minor()) >= (4, 12)
3453    }
3454
3455    /// Whether array properties have inner type tags
3456    fn array_inner_tag(&self) -> bool {
3457        // VAR_UE4_ARRAY_PROPERTY_INNER_TAGS
3458        (self.engine_version_major(), self.engine_version_minor()) >= (4, 12)
3459    }
3460
3461    /// Whether asset paths should not use FNames
3462    fn remove_asset_path_fnames(&self) -> bool {
3463        // FSOFTOBJECTPATH_REMOVE_ASSET_PATH_FNAMES
3464        self.package_file_version_ue5() >= 1007
3465    }
3466}
3467
3468#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
3469pub struct Header {
3470    pub magic: u32,
3471    pub save_game_version: u32,
3472    pub package_version: PackageVersion,
3473    pub engine_version_major: u16,
3474    pub engine_version_minor: u16,
3475    pub engine_version_patch: u16,
3476    pub engine_version_build: u32,
3477    pub engine_version: String,
3478    pub custom_version: Option<(u32, Vec<CustomFormatData>)>,
3479}
3480impl VersionInfo for Header {
3481    fn engine_version_major(&self) -> u16 {
3482        self.engine_version_major
3483    }
3484    fn engine_version_minor(&self) -> u16 {
3485        self.engine_version_minor
3486    }
3487    fn engine_version_patch(&self) -> u16 {
3488        self.engine_version_patch
3489    }
3490    fn package_file_version_ue4(&self) -> u32 {
3491        self.package_version.ue4
3492    }
3493    fn package_file_version_ue5(&self) -> u32 {
3494        self.package_version.ue5.unwrap_or(0)
3495    }
3496}
3497impl Header {
3498    #[cfg_attr(feature = "tracing", instrument(name = "Header_read", skip_all))]
3499    fn read<A: ArchiveReader>(ar: &mut A) -> Result<Self> {
3500        let magic = ar.read_u32::<LE>()?;
3501        if ar.log() && magic != u32::from_le_bytes(*b"GVAS") {
3502            eprintln!(
3503                "Found non-standard magic: {:02x?} ({}) expected: GVAS, continuing to parse...",
3504                &magic.to_le_bytes(),
3505                String::from_utf8_lossy(&magic.to_le_bytes())
3506            );
3507        }
3508        let save_game_version = ar.read_u32::<LE>()?;
3509        let package_version = PackageVersion {
3510            ue4: ar.read_u32::<LE>()?,
3511            ue5: (save_game_version >= 3 && save_game_version != 34) // TODO 34 is probably a game specific version
3512                .then(|| ar.read_u32::<LE>())
3513                .transpose()?,
3514        };
3515        let engine_version_major = ar.read_u16::<LE>()?;
3516        let engine_version_minor = ar.read_u16::<LE>()?;
3517        let engine_version_patch = ar.read_u16::<LE>()?;
3518        let engine_version_build = ar.read_u32::<LE>()?;
3519        let engine_version = ar.read_string()?;
3520        let custom_version = if (engine_version_major, engine_version_minor) >= (4, 12) {
3521            Some((
3522                ar.read_u32::<LE>()?,
3523                read_array(ar.read_u32::<LE>()?, ar, CustomFormatData::read)?,
3524            ))
3525        } else {
3526            None
3527        };
3528        Ok(Header {
3529            magic,
3530            save_game_version,
3531            package_version,
3532            engine_version_major,
3533            engine_version_minor,
3534            engine_version_patch,
3535            engine_version_build,
3536            engine_version,
3537            custom_version,
3538        })
3539    }
3540}
3541impl Header {
3542    fn write<A: ArchiveWriter>(&self, ar: &mut A) -> Result<()> {
3543        ar.write_u32::<LE>(self.magic)?;
3544        ar.write_u32::<LE>(self.save_game_version)?;
3545        ar.write_u32::<LE>(self.package_version.ue4)?;
3546        if let Some(ue5) = self.package_version.ue5 {
3547            ar.write_u32::<LE>(ue5)?;
3548        }
3549        ar.write_u16::<LE>(self.engine_version_major)?;
3550        ar.write_u16::<LE>(self.engine_version_minor)?;
3551        ar.write_u16::<LE>(self.engine_version_patch)?;
3552        ar.write_u32::<LE>(self.engine_version_build)?;
3553        ar.write_string(&self.engine_version)?;
3554        if let Some((custom_format_version, custom_format)) = &self.custom_version {
3555            ar.write_u32::<LE>(*custom_format_version)?;
3556            ar.write_u32::<LE>(custom_format.len() as u32)?;
3557            for cf in custom_format {
3558                cf.write(ar)?;
3559            }
3560        }
3561        Ok(())
3562    }
3563}
3564
3565/// Root struct inside a save file which holds both the Unreal Engine class name and list of properties
3566#[derive(Debug, PartialEq, Serialize)]
3567pub struct Root {
3568    pub save_game_type: String,
3569    pub properties: Properties,
3570}
3571impl Root {
3572    #[cfg_attr(feature = "tracing", instrument(name = "Root_read", skip_all))]
3573    fn read<A: ArchiveReader<ArchiveType = SaveGameArchiveType>>(ar: &mut A) -> Result<Self> {
3574        let save_game_type = ar.read_string()?;
3575        if ar.version().property_tag() {
3576            ar.read_u8()?;
3577        }
3578        let properties = read_properties_until_none(ar)?;
3579        Ok(Self {
3580            save_game_type,
3581            properties,
3582        })
3583    }
3584    fn write<A: ArchiveWriter<ArchiveType = SaveGameArchiveType>>(&self, ar: &mut A) -> Result<()> {
3585        ar.write_string(&self.save_game_type)?;
3586        if ar.version().property_tag() {
3587            ar.write_u8(0)?;
3588        }
3589        write_properties_none_terminated(ar, &self.properties)?;
3590        Ok(())
3591    }
3592}
3593
3594#[derive(Debug, PartialEq, Serialize)]
3595pub struct Save {
3596    pub header: Header,
3597    /// Property schemas (tags) separated from property data
3598    pub schemas: PropertySchemas,
3599    pub root: Root,
3600    pub extra: Vec<u8>,
3601}
3602impl Save {
3603    /// Reads save from the given reader
3604    #[cfg_attr(feature = "tracing", instrument(name = "Root_read", skip_all))]
3605    pub fn read<R: Read>(reader: &mut R) -> Result<Self, ParseError> {
3606        Self::read_with_types(reader, Types::new())
3607    }
3608    /// Reads save from the given reader using the provided [`Types`]
3609    #[cfg_attr(
3610        feature = "tracing",
3611        instrument(name = "Save_read_with_types", skip_all)
3612    )]
3613    pub fn read_with_types<R: Read>(reader: &mut R, types: Types) -> Result<Self, ParseError> {
3614        SaveReader::new().types(types).read(reader)
3615    }
3616    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
3617        let mut buffer = vec![];
3618        let schemas = Rc::new(RefCell::new(self.schemas.clone()));
3619
3620        let mut archive_writer = SaveGameArchive {
3621            stream: Cursor::new(&mut buffer),
3622            version: Some(self.header.clone()),
3623            types: Rc::new(Types::new()),
3624            scope: Scope::root(),
3625            log: false,
3626            error_to_raw: true,
3627            schemas,
3628        };
3629
3630        self.header.write(&mut archive_writer)?;
3631        self.root.write(&mut archive_writer)?;
3632        archive_writer.write_all(&self.extra)?;
3633
3634        writer.write_all(&buffer)?;
3635        Ok(())
3636    }
3637}
3638
3639pub struct SaveReader {
3640    log: bool,
3641    error_to_raw: bool,
3642    types: Option<Rc<Types>>,
3643}
3644impl Default for SaveReader {
3645    fn default() -> Self {
3646        Self::new()
3647    }
3648}
3649impl SaveReader {
3650    pub fn new() -> Self {
3651        Self {
3652            log: false,
3653            error_to_raw: false,
3654            types: None,
3655        }
3656    }
3657    pub fn log(mut self, log: bool) -> Self {
3658        self.log = log;
3659        self
3660    }
3661    /// Configure whether parsing errors should produce Raw properties (true) or fail immediately (false).
3662    ///
3663    /// When set to `true` (default), if a property cannot be parsed, it will be stored as a
3664    /// `Property::Raw` containing the raw bytes. This allows partial parsing of save files
3665    /// with unknown or corrupted properties.
3666    ///
3667    /// When set to `false`, parsing errors will immediately return an error, allowing you to
3668    /// detect and handle parsing issues explicitly.
3669    pub fn error_to_raw(mut self, error_to_raw: bool) -> Self {
3670        self.error_to_raw = error_to_raw;
3671        self
3672    }
3673    pub fn types(mut self, types: Types) -> Self {
3674        self.types = Some(Rc::new(types));
3675        self
3676    }
3677    pub fn read<S: Read>(self, stream: S) -> Result<Save, ParseError> {
3678        let types = self.types.unwrap_or_else(|| Rc::new(Types::new()));
3679        let schemas = Rc::new(RefCell::new(PropertySchemas::new()));
3680
3681        let stream = SeekReader::new(stream);
3682        let mut reader = SaveGameArchive {
3683            stream,
3684            version: None,
3685            types,
3686            scope: Scope::root(),
3687            log: self.log,
3688            error_to_raw: self.error_to_raw,
3689            schemas: schemas.clone(),
3690        };
3691
3692        let result = || -> Result<_> {
3693            let header = Header::read(&mut reader)?;
3694            reader.set_version(header.clone());
3695
3696            let root = Root::read(&mut reader)?;
3697            let extra = {
3698                let mut buf = vec![];
3699                reader.read_to_end(&mut buf)?;
3700                if reader.log() && buf != [0; 4] {
3701                    eprintln!(
3702                        "{} extra bytes. Save may not have been parsed completely.",
3703                        buf.len()
3704                    );
3705                }
3706                buf
3707            };
3708
3709            Ok((header, root, extra))
3710        }();
3711
3712        let offset = reader.stream_position().unwrap() as usize;
3713
3714        drop(reader);
3715
3716        let schemas = Rc::try_unwrap(schemas)
3717            .expect("Failed to extract schemas")
3718            .into_inner();
3719
3720        result
3721            .map(|(header, root, extra)| Save {
3722                header,
3723                schemas,
3724                root,
3725                extra,
3726            })
3727            .map_err(|e| error::ParseError { offset, error: e })
3728    }
3729}