uasset/serialization/
implementations.rs

1use binread::{BinRead, BinReaderExt};
2use bit_field::BitField;
3use std::{
4    io::{Read, Seek, SeekFrom},
5    marker::PhantomData,
6    mem::size_of,
7    num::NonZeroU32,
8};
9
10use crate::{archive::{SerializedFlags, SerializedObjectVersion}, serialization::{
11    ArrayStreamInfo, Deferrable, Parseable, ReadInfo, SingleItemStreamInfo, Skippable,
12    StreamInfo,
13}, AssetHeader, Error, NameReference, ObjectExport, ObjectImport, ObjectVersion, ObjectVersionUE5, Result, ThumbnailInfo};
14
15impl<T> Deferrable for T
16where
17    T: BinRead,
18{
19    type StreamInfoType = SingleItemStreamInfo;
20}
21
22impl<T> Skippable for T
23where
24    T: BinRead + Sized,
25{
26    fn seek_past_with_info<R>(reader: &mut R, stream_info: &Self::StreamInfoType) -> Result<()>
27    where
28        R: Seek + Read,
29    {
30        reader.seek(SeekFrom::Start(stream_info.offset + size_of::<T>() as u64))?;
31        Ok(())
32    }
33}
34
35impl<T> Parseable for T
36where
37    T: BinRead,
38{
39    type ParsedType = T;
40
41    fn parse_with_info_seekless<R>(
42        reader: &mut R,
43        _read_info: &<Self::StreamInfoType as StreamInfo>::ReadInfoType,
44    ) -> Result<Self::ParsedType>
45    where
46        R: Seek + Read,
47    {
48        Ok(reader.read_le()?)
49    }
50}
51
52fn skip_string<R>(reader: &mut R) -> Result<()>
53where
54    R: Seek + Read,
55{
56    let length: i32 = reader.read_le()?;
57    let (length, character_width) = if length < 0 {
58        // For UCS2 strings
59        (-length, 2)
60    } else {
61        // For ASCII strings
62        (length, 1)
63    };
64
65    reader.seek(SeekFrom::Current(length as i64 * character_width))?;
66
67    Ok(())
68}
69
70fn parse_string<R>(reader: &mut R) -> Result<String>
71where
72    R: Seek + Read,
73{
74    let length: i32 = reader.read_le()?;
75    if length != 0 {
76        let utf8_bytes = {
77            if length < 0 {
78                // Omit the trailing \0
79                let length = -length as usize - 1;
80                // Each UCS-2 code point can map to at most 3 UTF-8 bytes (it only encodes the basic multilingual plane of UTF8).
81                let mut utf8_bytes = Vec::with_capacity(3 * length);
82                // We could use as_mut_ptr + ptr::write + from_raw_parts_in, since we know that we'll never go out of bounds for the capacity we've reserved.
83                for _ in 0..length {
84                    let ch: u16 = reader.read_le()?;
85                    if (0x000..0x0080).contains(&ch) {
86                        utf8_bytes.push(ch as u8);
87                    } else if (0x0080..0x0800).contains(&ch) {
88                        let first = 0b1100_0000 + ch.get_bits(6..11) as u8;
89                        let last = 0b1000_0000 + ch.get_bits(0..6) as u8;
90
91                        utf8_bytes.push(first);
92                        utf8_bytes.push(last);
93                    } else {
94                        let first = 0b1110_0000 + ch.get_bits(12..16) as u8;
95                        let mid = 0b1000_0000 + ch.get_bits(6..12) as u8;
96                        let last = 0b1000_0000 + ch.get_bits(0..6) as u8;
97
98                        utf8_bytes.push(first);
99                        utf8_bytes.push(mid);
100                        utf8_bytes.push(last);
101                    }
102                }
103
104                // Skip the trailing \0
105                reader.seek(SeekFrom::Current(2))?;
106
107                utf8_bytes.shrink_to_fit();
108                utf8_bytes
109            } else {
110                // Omit the trailing \0
111                let length = length - 1;
112                let mut utf8_bytes = Vec::new();
113                utf8_bytes.resize(length as usize, 0u8);
114
115                reader.read_exact(&mut utf8_bytes)?;
116                // Skip the trailing \0
117                reader.seek(SeekFrom::Current(1))?;
118
119                utf8_bytes
120            }
121        };
122        String::from_utf8(utf8_bytes).map_err(Error::InvalidString)
123    } else {
124        Ok(String::new())
125    }
126}
127
128#[derive(Debug)]
129pub struct UnrealString {}
130
131impl Deferrable for UnrealString {
132    type StreamInfoType = SingleItemStreamInfo;
133}
134
135impl Skippable for UnrealString {
136    fn seek_past_with_info<R>(reader: &mut R, stream_info: &Self::StreamInfoType) -> Result<()>
137    where
138        R: Seek + Read,
139    {
140        reader.seek(SeekFrom::Start(stream_info.offset))?;
141        skip_string(reader)
142    }
143}
144
145impl Parseable for UnrealString {
146    type ParsedType = String;
147
148    fn parse_with_info_seekless<R>(
149        reader: &mut R,
150        _read_info: &<Self::StreamInfoType as StreamInfo>::ReadInfoType,
151    ) -> Result<Self::ParsedType>
152    where
153        R: Seek + Read,
154    {
155        parse_string(reader)
156    }
157}
158
159#[derive(Debug)]
160pub struct UnrealNameEntryWithHash {}
161
162impl Deferrable for UnrealNameEntryWithHash {
163    type StreamInfoType = SingleItemStreamInfo;
164}
165
166impl Skippable for UnrealNameEntryWithHash {
167    fn seek_past_with_info<R>(reader: &mut R, stream_info: &Self::StreamInfoType) -> Result<()>
168    where
169        R: Seek + Read,
170    {
171        reader.seek(SeekFrom::Start(stream_info.offset))?;
172        skip_string(reader)?;
173        // Seek past two hashes that are no longer used, NonCasePreservingHash and CasePreservingHash
174        reader.seek(SeekFrom::Current(size_of::<[u16; 2]>() as i64))?;
175        Ok(())
176    }
177}
178
179impl Parseable for UnrealNameEntryWithHash {
180    type ParsedType = String;
181
182    fn parse_with_info_seekless<R>(
183        reader: &mut R,
184        _read_info: &<Self::StreamInfoType as StreamInfo>::ReadInfoType,
185    ) -> Result<Self::ParsedType>
186    where
187        R: Seek + Read,
188    {
189        let string = parse_string(reader)?;
190        let _hash: u32 = reader.read_le()?;
191        Ok(string)
192    }
193}
194
195#[derive(Debug)]
196pub struct UnrealArray<ElementType>
197where
198    ElementType: Sized,
199{
200    elements: Vec<ElementType>,
201}
202
203impl<ElementType> Deferrable for UnrealArray<ElementType> {
204    type StreamInfoType = ArrayStreamInfo;
205}
206
207impl<ElementType, ElementTypeStreamInfo> Skippable for UnrealArray<ElementType>
208where
209    ElementType: Skippable<StreamInfoType = ElementTypeStreamInfo>,
210    ElementTypeStreamInfo: StreamInfo,
211{
212    fn seek_past_with_info<R>(reader: &mut R, stream_info: &Self::StreamInfoType) -> Result<()>
213    where
214        R: Seek + Read,
215    {
216        reader.seek(SeekFrom::Start(stream_info.offset))?;
217
218        for _ in 0..stream_info.count {
219            let element_stream_info = ElementTypeStreamInfo::from_current_position(reader)?;
220            ElementType::seek_past_with_info(reader, &element_stream_info)?;
221        }
222
223        Ok(())
224    }
225}
226
227impl<ElementType, ElementStreamInfoType> Parseable for UnrealArray<ElementType>
228where
229    ElementType: Parseable<StreamInfoType = ElementStreamInfoType>,
230    ElementStreamInfoType: StreamInfo,
231{
232    type ParsedType = Vec<ElementType::ParsedType>;
233
234    fn parse_with_info_seekless<R>(
235        reader: &mut R,
236        read_info: &<Self::StreamInfoType as StreamInfo>::ReadInfoType,
237    ) -> Result<Self::ParsedType>
238    where
239        R: Seek
240            + Read
241            + SerializedObjectVersion<ObjectVersion>
242            + SerializedObjectVersion<ObjectVersionUE5>
243            + SerializedFlags,
244    {
245        let mut elements = Vec::with_capacity(read_info.count as usize);
246        for _ in 0..read_info.count {
247            let read_info = ElementStreamInfoType::ReadInfoType::from_current_position(reader)?;
248            elements.push(ElementType::parse_with_info_seekless(reader, &read_info)?);
249        }
250
251        Ok(elements)
252    }
253}
254
255#[derive(Debug)]
256pub struct UnrealArrayIterator<'a, ElementType, R>
257where
258    ElementType: Parseable,
259{
260    package: &'a mut AssetHeader<R>,
261    stream_info: ArrayStreamInfo,
262    next_index: u64,
263    phantom: PhantomData<ElementType>,
264}
265
266impl<'a, ElementType, ElementStreamInfoType, R> UnrealArrayIterator<'a, ElementType, R>
267where
268    ElementType: Parseable<StreamInfoType = ElementStreamInfoType>,
269    ElementStreamInfoType: StreamInfo,
270    R: Seek + Read,
271{
272    pub fn new(package: &'a mut AssetHeader<R>, stream_info: ArrayStreamInfo) -> Result<Self> {
273        package.archive.seek(SeekFrom::Start(stream_info.offset))?;
274        Ok(Self {
275            package,
276            stream_info,
277            next_index: 0,
278            phantom: PhantomData,
279        })
280    }
281}
282
283impl<'a, ElementType, ElementStreamInfoType, R> Iterator for UnrealArrayIterator<'a, ElementType, R>
284where
285    ElementType: Parseable<StreamInfoType = ElementStreamInfoType>,
286    ElementStreamInfoType: StreamInfo,
287    R: Seek + Read,
288{
289    type Item = Result<ElementType::ParsedType>;
290
291    fn next(&mut self) -> Option<Self::Item> {
292        if self.next_index < self.stream_info.count {
293            self.next_index += 1;
294            Some(
295                ElementStreamInfoType::ReadInfoType::from_current_position(
296                    &mut self.package.archive,
297                )
298                .and_then(|read_info| {
299                    ElementType::parse_with_info_seekless(&mut self.package.archive, &read_info)
300                }),
301            )
302        } else {
303            None
304        }
305    }
306}
307
308/// Size of `FGuid`
309const GUID_SIZE: u64 = 16;
310pub struct UnrealGuid {}
311
312impl Deferrable for UnrealGuid {
313    type StreamInfoType = SingleItemStreamInfo;
314}
315
316impl Skippable for UnrealGuid {
317    fn seek_past_with_info<R>(reader: &mut R, stream_info: &Self::StreamInfoType) -> Result<()>
318    where
319        R: Seek + Read,
320    {
321        reader.seek(SeekFrom::Start(stream_info.offset + GUID_SIZE))?;
322        Ok(())
323    }
324}
325
326/// Size of `FCustomVersion`, when serializing with `ECustomVersionSerializationFormat::Optimized`
327const CUSTOM_VERSION_SIZE: u64 = 20;
328pub struct UnrealCustomVersion {}
329
330impl Deferrable for UnrealCustomVersion {
331    type StreamInfoType = SingleItemStreamInfo;
332}
333
334impl Skippable for UnrealCustomVersion {
335    fn seek_past_with_info<R>(reader: &mut R, stream_info: &Self::StreamInfoType) -> Result<()>
336    where
337        R: Seek + Read,
338    {
339        reader.seek(SeekFrom::Start(stream_info.offset + CUSTOM_VERSION_SIZE))?;
340        Ok(())
341    }
342}
343
344/// Size of `FGuidCustomVersion_DEPRECATED` excluding the `FString`, when serializing with `ECustomVersionSerializationFormat::Guid`
345const GUID_CUSTOM_VERSION_PREFIX_SIZE: u64 = 20;
346pub struct UnrealGuidCustomVersion {}
347
348impl Deferrable for UnrealGuidCustomVersion {
349    type StreamInfoType = SingleItemStreamInfo;
350}
351
352impl Skippable for UnrealGuidCustomVersion {
353    fn seek_past_with_info<R>(reader: &mut R, stream_info: &Self::StreamInfoType) -> Result<()>
354    where
355        R: Seek + Read,
356    {
357        reader.seek(SeekFrom::Start(
358            stream_info.offset + GUID_CUSTOM_VERSION_PREFIX_SIZE,
359        ))?;
360        UnrealString::seek_past(reader)?;
361        Ok(())
362    }
363}
364
365/// Size of `FGenerationInfo`
366const GENERATION_INFO_SIZE: u64 = 8;
367pub struct UnrealGenerationInfo {}
368
369impl Deferrable for UnrealGenerationInfo {
370    type StreamInfoType = SingleItemStreamInfo;
371}
372
373impl Skippable for UnrealGenerationInfo {
374    fn seek_past_with_info<R>(reader: &mut R, stream_info: &Self::StreamInfoType) -> Result<()>
375    where
376        R: Seek + Read,
377    {
378        reader.seek(SeekFrom::Start(stream_info.offset + GENERATION_INFO_SIZE))?;
379        Ok(())
380    }
381}
382
383/// Size of `FCompressedChunk`
384const COMPRESSED_CHUNK_SIZE: u64 = 16;
385pub struct UnrealCompressedChunk {}
386
387impl Deferrable for UnrealCompressedChunk {
388    type StreamInfoType = SingleItemStreamInfo;
389}
390
391impl Skippable for UnrealCompressedChunk {
392    fn seek_past_with_info<R>(reader: &mut R, stream_info: &Self::StreamInfoType) -> Result<()>
393    where
394        R: Seek + Read,
395    {
396        reader.seek(SeekFrom::Start(stream_info.offset + COMPRESSED_CHUNK_SIZE))?;
397        Ok(())
398    }
399}
400
401#[derive(Clone, Debug)]
402pub struct UnrealEngineVersion {
403    pub major: u16,
404    pub minor: u16,
405    pub patch: u16,
406    pub changelist: u32,
407    pub is_licensee_version: bool,
408    pub branch_name: String,
409}
410
411impl UnrealEngineVersion {
412    pub const LICENSEE_BIT_MASK: u32 = 0x80000000;
413    pub const CHANGELIST_MASK: u32 = 0x7fffffff;
414
415    pub fn empty() -> Self {
416        Self {
417            major: 0,
418            minor: 0,
419            patch: 0,
420            changelist: 0,
421            is_licensee_version: false,
422            branch_name: String::new(),
423        }
424    }
425
426    pub fn from_changelist(changelist: u32) -> Self {
427        Self {
428            major: 4,
429            changelist: changelist & Self::CHANGELIST_MASK,
430            is_licensee_version: (changelist & Self::LICENSEE_BIT_MASK) != 0,
431            ..Self::empty()
432        }
433    }
434
435    pub fn is_empty(&self) -> bool {
436        self.changelist == 0 && !self.is_licensee_version
437    }
438}
439
440impl Deferrable for UnrealEngineVersion {
441    type StreamInfoType = SingleItemStreamInfo;
442}
443
444impl Parseable for UnrealEngineVersion {
445    type ParsedType = UnrealEngineVersion;
446
447    fn parse_with_info_seekless<R>(
448        reader: &mut R,
449        _read_info: &<Self::StreamInfoType as StreamInfo>::ReadInfoType,
450    ) -> Result<Self::ParsedType>
451    where
452        R: Seek
453            + Read
454            + SerializedObjectVersion<ObjectVersion>
455            + SerializedObjectVersion<ObjectVersionUE5>
456            + SerializedFlags,
457    {
458        let major = reader.read_le()?;
459        let minor = reader.read_le()?;
460        let patch = reader.read_le()?;
461        let changelist = reader.read_le()?;
462        let branch_name = UnrealString::parse_inline(reader)?;
463
464        Ok(Self::ParsedType {
465            major,
466            minor,
467            patch,
468            branch_name,
469            ..Self::from_changelist(changelist)
470        })
471    }
472}
473
474#[derive(Debug)]
475pub struct UnrealNameReference {}
476
477impl Deferrable for UnrealNameReference {
478    type StreamInfoType = SingleItemStreamInfo;
479}
480
481impl Parseable for UnrealNameReference {
482    type ParsedType = NameReference;
483
484    fn parse_with_info_seekless<R>(
485        reader: &mut R,
486        _read_info: &<Self::StreamInfoType as StreamInfo>::ReadInfoType,
487    ) -> Result<Self::ParsedType>
488    where
489        R: Seek + Read,
490    {
491        let index = reader.read_le()?;
492        let number = NonZeroU32::new(reader.read_le()?);
493        Ok(Self::ParsedType { index, number })
494    }
495}
496
497#[derive(Debug)]
498pub struct UnrealObjectExport {}
499
500impl Deferrable for UnrealObjectExport {
501    type StreamInfoType = SingleItemStreamInfo;
502}
503
504impl Parseable for UnrealObjectExport {
505    type ParsedType = ObjectExport;
506
507    fn parse_with_info_seekless<R>(
508        reader: &mut R,
509        _read_info: &<Self::StreamInfoType as StreamInfo>::ReadInfoType,
510    ) -> Result<Self::ParsedType>
511    where
512        R: Seek
513        + Read
514        + SerializedObjectVersion<ObjectVersion>
515        + SerializedObjectVersion<ObjectVersionUE5>
516        + SerializedFlags,
517    {
518        let class_index = reader.read_le()?;
519        let super_index = reader.read_le()?;
520
521        let template_index = if reader.serialized_with(ObjectVersion::VER_UE4_TemplateIndex_IN_COOKED_EXPORTS) {
522            reader.read_le()?
523        } else { 0 };
524
525        let outer_index = reader.read_le()?;
526        let object_name = UnrealNameReference::parse_inline(reader)?;
527        let object_flags = reader.read_le()?;
528
529        let (serial_size, serial_offset) = if reader.serialized_with(ObjectVersion::VER_UE4_64BIT_EXPORTMAP_SERIALSIZES) {
530            (reader.read_le()?, reader.read_le()?)
531        } else { (reader.read_le::<i32>()? as i64, reader.read_le::<i32>()? as i64) };
532
533        let forced_export = reader.read_le::<u32>()? != 0;
534        let not_for_client = reader.read_le::<u32>()? != 0;
535        let not_for_server = reader.read_le::<u32>()? != 0;
536
537        if !reader.serialized_with(ObjectVersionUE5::REMOVE_OBJECT_EXPORT_PACKAGE_GUID) {
538            let _package_guid = UnrealGuid::seek_past(reader)?;
539        }
540
541        let is_inherited_instance = if reader.serialized_with(ObjectVersionUE5::TRACK_OBJECT_EXPORT_IS_INHERITED) {
542            reader.read_le::<u32>()? != 0
543        } else {
544            false
545        };
546
547        let package_flags = reader.read_le()?;
548
549        let not_always_loaded_for_editor_game = if reader.serialized_with(ObjectVersion::VER_UE4_LOAD_FOR_EDITOR_GAME)
550        {
551            reader.read_le::<u32>()? != 0
552        } else { true };
553
554
555        let is_asset = if reader.serialized_with(ObjectVersion::VER_UE4_COOKED_ASSETS_IN_EDITOR_SUPPORT)
556        {
557            reader.read_le::<u32>()? != 0
558        } else { false };
559
560        let generate_public_hash = if reader.serialized_with(ObjectVersionUE5::OPTIONAL_RESOURCES)
561        {
562            reader.read_le::<u32>()? != 0
563        } else { false };
564
565        let (first_export_dependency,
566            serialization_before_serialization_dependencies,
567            create_before_serialization_dependencies,
568            serialization_before_create_dependencies,
569            create_before_create_dependencies) = if reader.serialized_with(ObjectVersion::VER_UE4_PRELOAD_DEPENDENCIES_IN_COOKED_EXPORTS)
570        {
571            (reader.read_le()?, reader.read_le()?, reader.read_le()?, reader.read_le()?, reader.read_le()?)
572        } else { (-1, -1, -1, -1, -1) };
573
574        let (script_serialization_start_offset, script_serialization_end_offset) = if reader.serialized_with(ObjectVersionUE5::SCRIPT_SERIALIZATION_OFFSET) {
575            (reader.read_le()?, reader.read_le()?)
576        } else { (0, 0) };
577
578        Ok(Self::ParsedType {
579            outer_index,
580            object_name,
581            class_index,
582            super_index,
583            template_index,
584            object_flags,
585            serial_size,
586            serial_offset,
587            script_serialization_start_offset,
588            script_serialization_end_offset,
589            forced_export,
590            not_for_client,
591            not_for_server,
592            not_always_loaded_for_editor_game,
593            is_asset,
594            is_inherited_instance,
595            generate_public_hash,
596            package_flags,
597            first_export_dependency,
598            serialization_before_serialization_dependencies,
599            create_before_serialization_dependencies,
600            serialization_before_create_dependencies,
601            create_before_create_dependencies,
602        })
603    }
604}
605
606#[derive(Debug)]
607pub struct UnrealClassImport {}
608
609impl Deferrable for UnrealClassImport {
610    type StreamInfoType = SingleItemStreamInfo;
611}
612
613impl Parseable for UnrealClassImport {
614    type ParsedType = ObjectImport;
615
616    fn parse_with_info_seekless<R>(
617        reader: &mut R,
618        _read_info: &<Self::StreamInfoType as StreamInfo>::ReadInfoType,
619    ) -> Result<Self::ParsedType>
620    where
621        R: Seek
622            + Read
623            + SerializedObjectVersion<ObjectVersion>
624            + SerializedObjectVersion<ObjectVersionUE5>
625            + SerializedFlags,
626    {
627        let class_package = UnrealNameReference::parse_inline(reader)?;
628        let class_name = UnrealNameReference::parse_inline(reader)?;
629        let outer_index = reader.read_le()?;
630        let object_name = UnrealNameReference::parse_inline(reader)?;
631        let package_name = if reader
632            .serialized_with(ObjectVersion::VER_UE4_NON_OUTER_PACKAGE_IMPORT)
633            && reader.serialized_with_editoronly_data()
634        {
635            Some(UnrealNameReference::parse_inline(reader)?)
636        } else {
637            None
638        };
639
640        let import_optional = if reader.serialized_with(ObjectVersionUE5::OPTIONAL_RESOURCES) {
641            reader.read_le::<u32>()? != 0
642        } else {
643            false
644        };
645
646        Ok(Self::ParsedType {
647            outer_index,
648            object_name,
649            class_package,
650            class_name,
651            package_name,
652            import_optional,
653        })
654    }
655}
656
657#[derive(Debug)]
658pub struct UnrealThumbnailInfo {}
659
660impl Deferrable for UnrealThumbnailInfo {
661    type StreamInfoType = SingleItemStreamInfo;
662}
663
664impl Parseable for UnrealThumbnailInfo {
665    type ParsedType = ThumbnailInfo;
666
667    fn parse_with_info_seekless<R>(
668        reader: &mut R,
669        _read_info: &<Self::StreamInfoType as StreamInfo>::ReadInfoType,
670    ) -> Result<Self::ParsedType>
671    where
672        R: Seek
673            + Read
674            + SerializedObjectVersion<ObjectVersion>
675            + SerializedObjectVersion<ObjectVersionUE5>
676            + SerializedFlags,
677    {
678        let object_class_name = UnrealString::parse_inline(reader)?;
679        let object_path_without_package_name = UnrealString::parse_inline(reader)?;
680        let file_offset = reader.read_le()?;
681        Ok(Self::ParsedType {
682            object_class_name,
683            object_path_without_package_name,
684            file_offset,
685        })
686    }
687}