Skip to content

Commit

Permalink
Create enums for SaveGameVersion and UE5Version
Browse files Browse the repository at this point in the history
  • Loading branch information
scottanderson authored and localcc committed Sep 29, 2024
1 parent b1e0b97 commit d577451
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 29 deletions.
62 changes: 35 additions & 27 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,14 @@ pub mod engine_version;
pub mod error;
/// Game version enumeration.
pub mod game_version;
/// Object version information.
pub mod object_version;
/// Extensions for `Ord`.
mod ord_ext;
/// Property types.
pub mod properties;
/// Savegame version information.
pub mod savegame_version;
pub(crate) mod scoped_stack_entry;
/// Various types.
pub mod types;
Expand All @@ -98,8 +102,10 @@ use flate2::read::ZlibDecoder;
use flate2::write::ZlibEncoder;
use flate2::Compression;
use indexmap::IndexMap;
use object_version::EUnrealEngineObjectUE5Version;
use ord_ext::OrdExt;
use properties::{Property, PropertyOptions, PropertyTrait};
use savegame_version::SaveGameVersion;
use types::Guid;

/// The four bytes 'GVAS' appear at the beginning of every GVAS file.
Expand All @@ -125,10 +131,10 @@ pub enum GvasHeader {
},
/// Version 3
Version3 {
/// File format version.
/// File format version (UE4).
package_file_version: u32,
/// Unknown.
unknown: u32,
/// File format version (UE5).
package_file_version_ue5: u32,
/// Unreal Engine version.
engine_version: FEngineVersion,
/// Custom version format.
Expand Down Expand Up @@ -171,7 +177,10 @@ impl GvasHeader {
}

let save_game_file_version = cursor.read_u32::<LittleEndian>()?;
if !save_game_file_version.between(2, 3) {
if !save_game_file_version.between(
SaveGameVersion::AddedCustomVersions as u32,
SaveGameVersion::PackageFileSummaryVersionChange as u32,
) {
Err(DeserializeError::InvalidHeader(
format!("GVAS version {save_game_file_version} not supported").into_boxed_str(),
))?
Expand All @@ -186,9 +195,21 @@ impl GvasHeader {
}

// This field is only present in the v3 header
let unknown = match save_game_file_version {
3 => Some(cursor.read_u32::<LittleEndian>()?),
_ => None,
let package_file_version_ue5 = if save_game_file_version
>= SaveGameVersion::PackageFileSummaryVersionChange as u32
{
let version = cursor.read_u32::<LittleEndian>()?;
if !version.between(
EUnrealEngineObjectUE5Version::InitialVersion as u32,
EUnrealEngineObjectUE5Version::DataResources as u32,
) {
Err(DeserializeError::InvalidHeader(
format!("UE5 Package file version {version} is not supported").into_boxed_str(),
))?
}
Some(version)
} else {
None
};

let engine_version = FEngineVersion::read(cursor)?;
Expand All @@ -209,17 +230,17 @@ impl GvasHeader {

let save_game_class_name = cursor.read_string()?;

Ok(match unknown {
Ok(match package_file_version_ue5 {
None => GvasHeader::Version2 {
package_file_version,
engine_version,
custom_version_format,
custom_versions,
save_game_class_name,
},
Some(unknown) => GvasHeader::Version3 {
Some(package_file_version_ue5) => GvasHeader::Version3 {
package_file_version,
unknown,
package_file_version_ue5,
engine_version,
custom_version_format,
custom_versions,
Expand Down Expand Up @@ -271,7 +292,7 @@ impl GvasHeader {

GvasHeader::Version3 {
package_file_version,
unknown,
package_file_version_ue5,
engine_version,
custom_version_format,
custom_versions,
Expand All @@ -280,7 +301,7 @@ impl GvasHeader {
let mut len = 24;
cursor.write_u32::<LittleEndian>(3)?;
cursor.write_u32::<LittleEndian>(*package_file_version)?;
cursor.write_u32::<LittleEndian>(*unknown)?;
cursor.write_u32::<LittleEndian>(*package_file_version_ue5)?;
len += engine_version.write(cursor)?;
cursor.write_u32::<LittleEndian>(*custom_version_format)?;
cursor.write_u32::<LittleEndian>(custom_versions.len() as u32)?;
Expand Down Expand Up @@ -329,21 +350,8 @@ trait GvasHeaderTrait {
impl GvasHeaderTrait for GvasHeader {
fn use_large_world_coordinates(&self) -> bool {
match self {
GvasHeader::Version2 {
package_file_version: _,
engine_version: _,
custom_version_format: _,
custom_versions: _,
save_game_class_name: _,
} => false,
GvasHeader::Version3 {
package_file_version: _,
unknown: _,
engine_version: _,
custom_version_format: _,
custom_versions: _,
save_game_class_name: _,
} => true,
GvasHeader::Version2 { .. } => false,
GvasHeader::Version3 { .. } => true,
}
}
}
Expand Down
47 changes: 47 additions & 0 deletions src/object_version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use num_enum::IntoPrimitive;

/// UE5 object versions.
#[derive(IntoPrimitive)]
#[repr(u32)]
pub enum EUnrealEngineObjectUE5Version {
/// The original UE5 version, at the time this was added the UE4 version was 522, so UE5 will start from 1000 to show a clear difference
InitialVersion = 1000,

/// Support stripping names that are not referenced from export data
NamesReferencedFromExportData,

/// Added a payload table of contents to the package summary
PayloadToc,

/// Added data to identify references from and to optional package
OptionalResources,

/// Large world coordinates converts a number of core types to double components by default.
LargeWorldCoordinates,

/// Remove package GUID from FObjectExport
RemoveObjectExportPackageGuid,

/// Add IsInherited to the FObjectExport entry
TrackObjectExportIsInherited,

/// Replace FName asset path in FSoftObjectPath with (package name, asset name) pair FTopLevelAssetPath
FsoftobjectpathRemoveAssetPathFnames,

/// Add a soft object path list to the package summary for fast remap
AddSoftobjectpathList,

/// Added bulk/data resource table
DataResources,

/// Added script property serialization offset to export table entries for saved, versioned packages
ScriptSerializationOffset,

/// Adding property tag extension,
/// Support for overridable serialization on UObject,
/// Support for overridable logic in containers
PropertyTagExtensionAndOverridableSerialization,

/// Added property tag complete type name and serialization type
PropertyTagCompleteTypeName,
}
13 changes: 13 additions & 0 deletions src/savegame_version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use num_enum::IntoPrimitive;

/// Save Game File Version from FSaveGameFileVersion::Type
#[derive(IntoPrimitive)]
#[repr(u32)]
pub enum SaveGameVersion {
/// Initial version.
InitialVersion = 1,
/// serializing custom versions into the savegame data to handle that type of versioning
AddedCustomVersions = 2,
/// added a new UE5 version number to FPackageFileSummary
PackageFileSummaryVersionChange = 3,
}
4 changes: 2 additions & 2 deletions tests/common/vector2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub(crate) fn expected() -> GvasFile {
deserialized_game_version: DeserializedGameVersion::Default,
header: GvasHeader::Version3 {
package_file_version: 522,
unknown: 1009,
package_file_version_ue5: 1009,
engine_version: FEngineVersion {
major: 5,
minor: 3,
Expand Down Expand Up @@ -809,7 +809,7 @@ pub const VECTOR2D_JSON: &str = r#"{
"header": {
"type": "Version3",
"package_file_version": 522,
"unknown": 1009,
"package_file_version_ue5": 1009,
"engine_version": {
"major": 5,
"minor": 3,
Expand Down

0 comments on commit d577451

Please sign in to comment.