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 (-length, 2)
60 } else {
61 (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 let length = -length as usize - 1;
80 let mut utf8_bytes = Vec::with_capacity(3 * length);
82 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 reader.seek(SeekFrom::Current(2))?;
106
107 utf8_bytes.shrink_to_fit();
108 utf8_bytes
109 } else {
110 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 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 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
308const 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
326const 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
344const 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
365const 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
383const 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}