Closed Zhaxxy closed 3 weeks ago
Some structs have custom serialization which adds extra data before or after the properties. This patch seems to allow this particular save to parse but probably is not general. The serialized struct sizes in in the save seem to be different after round trip so the object nesting might still be wrong, but would probably have to decompile the game's executable to dig further.
diff --git a/src/lib.rs b/src/lib.rs
index c89ac32..3c9245f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1536,7 +1536,13 @@ pub enum StructValue {
SoftObjectPath(String, String),
GameplayTagContainer(GameplayTagContainer),
/// User defined struct which is simply a list of properties
- Struct(Properties),
+ Struct {
+ properties: Properties,
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
+ pre: Vec<u8>,
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
+ post: Vec<u8>,
+ },
}
/// Vectorized properties to avoid storing the variant with each value
@@ -1668,7 +1674,28 @@ impl StructValue {
StructValue::GameplayTagContainer(GameplayTagContainer::read(reader)?)
}
- StructType::Struct(_) => StructValue::Struct(read_properties_until_none(reader)?),
+ StructType::Struct(name) => {
+ let pre = if name.as_deref() == Some("NN_ServerCharacterID") {
+ let mut buf = vec![0; 8];
+ reader.read_exact(&mut buf)?;
+ buf
+ } else {
+ vec![]
+ };
+ let properties = read_properties_until_none(reader)?;
+ let post = if name.as_deref() == Some("NN_GameData_ForSave") {
+ let mut buf = vec![0; 278];
+ reader.read_exact(&mut buf)?;
+ buf
+ } else {
+ vec![]
+ };
+ StructValue::Struct {
+ properties,
+ pre,
+ post,
+ }
+ }
})
}
fn write<W: Write>(&self, writer: &mut Context<W>) -> TResult<()> {
@@ -1690,7 +1717,15 @@ impl StructValue {
write_string(writer, b)?;
}
StructValue::GameplayTagContainer(v) => v.write(writer)?,
- StructValue::Struct(v) => write_properties_none_terminated(writer, v)?,
+ StructValue::Struct {
+ properties,
+ pre,
+ post,
+ } => {
+ writer.write_all(pre)?;
+ write_properties_none_terminated(writer, properties)?;
+ writer.write_all(post)?;
+ }
}
Ok(())
}
Some structs have custom serialization which adds extra data before or after the properties. This patch seems to allow this particular save to parse but probably is not general. The serialized struct sizes in in the save seem to be different after round trip so the object nesting might still be wrong, but would probably have to decompile the game's executable to dig further.
diff --git a/src/lib.rs b/src/lib.rs index c89ac32..3c9245f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1536,7 +1536,13 @@ pub enum StructValue { SoftObjectPath(String, String), GameplayTagContainer(GameplayTagContainer), /// User defined struct which is simply a list of properties - Struct(Properties), + Struct { + properties: Properties, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pre: Vec<u8>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + post: Vec<u8>, + }, } /// Vectorized properties to avoid storing the variant with each value @@ -1668,7 +1674,28 @@ impl StructValue { StructValue::GameplayTagContainer(GameplayTagContainer::read(reader)?) } - StructType::Struct(_) => StructValue::Struct(read_properties_until_none(reader)?), + StructType::Struct(name) => { + let pre = if name.as_deref() == Some("NN_ServerCharacterID") { + let mut buf = vec![0; 8]; + reader.read_exact(&mut buf)?; + buf + } else { + vec![] + }; + let properties = read_properties_until_none(reader)?; + let post = if name.as_deref() == Some("NN_GameData_ForSave") { + let mut buf = vec![0; 278]; + reader.read_exact(&mut buf)?; + buf + } else { + vec![] + }; + StructValue::Struct { + properties, + pre, + post, + } + } }) } fn write<W: Write>(&self, writer: &mut Context<W>) -> TResult<()> { @@ -1690,7 +1717,15 @@ impl StructValue { write_string(writer, b)?; } StructValue::GameplayTagContainer(v) => v.write(writer)?, - StructValue::Struct(v) => write_properties_none_terminated(writer, v)?, + StructValue::Struct { + properties, + pre, + post, + } => { + writer.write_all(pre)?; + write_properties_none_terminated(writer, properties)?; + writer.write_all(post)?; + } } Ok(()) }
sorry for taking long to reply i forgot about this, but yes youre right, it does work with that save i sent with the changes to the lib.rs folder, but this save, i get similar error even with the new changes
uesave.exe to-json -i ue4savegame.ps4.sav -o ue4savegame.ps4.sav.json
Error: at offset 557939: io error: failed to fill whole buffer
19_05_2024__23_12_56.zip
(theres 2 saves here but i only tried it with the Customizev003
one (PS4_FOLDER_IN_ME_PS4_SAVEDATA_1924e1ae0638ed9b_CUSA08789Customizev003
)
Sorry if im not being very helpful here as im not familar with the game nor unreal saves or rust but i tried to do
to-json
with theue4savegame.ps4.sav
found in the decrypted save file in the zip attached but i get an error./uesave to-json -i ue4savegame.ps4.sav
Error: at offset 569823: io error: failed to fill whole buffer
could you please look at the save and see if you can add support for it? or perhaps i need to use the -t option but i really dont know, i know the save has the
GSAV
header thoughthank you soo much for this tool
the save file is in this zip, its the ue4savegame.ps4.sav, the other stuff is just ps4 savedata metadata thats in all ps4 saves 15_04_2024__01_32_16.zip