Issue parsing State of Decay 2 save files
Hello,
Firstly, I would like to thank you for sharing such an interesting project.
A generic UE4 save file converter seems to be a fun starting point for save game editing and other modding applications.
I am trying to parse save data from State of Decay 2. Looks like the windows version of the game is stored in wgs folder structure which seems to be a replacement of a single binary .sav according to https://github.com/Fr33dan/GPSaveConverter which is another helpful tool.
By digging around hex editors I was able to confirm that the files indeed are GVAS UE4 which seem to be related to .sav format. That's how I ended up here.
This is the output from the GPSSaveConverter tool
Game Name:State of Decay 2 Game Package ID:Microsoft.Dayton_8wekyb3d8bbwe
Xbox Files:
| Container Name 1 | Container Name 2 | Blob ID |
|---|---|---|
| UE4DefaultSaveGameContainer | StateOfDecay2 | Release/v2/SaveGame_Heartland_0 |
| UE4DefaultSaveGameContainer | StateOfDecay2 | Release/v2/SaveGame_Vanilla_0 |
| UE4DefaultSaveGameContainer | StateOfDecay2 | Release/v2/SaveGame_Vanilla_1 |
| UE4DefaultSaveGameContainer | StateOfDecay2 | Release/v2/SaveUser |
My understanding is that each container here is a valid GVAS file and should be parseable by the tool. There could be custom datatypes or something related to compression as mentioned in https://github.com/trumank/uesave-rs/issues/22 which could be failing the parse.
This is the error that I see, but is probably too generic to be of help but here it is:
uesave to-json -i 053844BE4B114756BA7306387F0AE811-o t.json
Error: at offset 159327: io error: failed to fill whole buffer
I haven't ever dived into hex editing and never worked with rust, so apologies if I am missing something trivial.
Since the author and participants in this repo seem to be very active and helpful I have a few questions:
- Is my assumption that these files should be convertible to json with this tool, correct?
- If not, how could I get these files into a state that I can parse them?
- Could you clue me into how can I decipher the format and zlip compression details if there is a different compression tool that I should be using?
- I would be happy to contribute if this is different for this game and can be generalized to be read and decompressed / deserialized as needed for other games too
Please find below the wgs folder structure below for reference.
sample.zip
Thanks in advance!
In https://github.com/trumank/uesave-rs/commit/4a58133c3dbec16132f494240774996897a0c392 I managed to get the save fully parsed and converted to JSON. Serialization the other direction has yet to be implemented but should be far easier.
There were a surprising number of changes necessary:
- incremented header version (conflicting with recent Unreal Engine versions)
- save body is compressed with LZ4 (info found here https://github.com/IncredulousMonk/sod2-tools/blob/main/sod2gvas.md)
- there are two custom
FTextHistoryvariants with unknown purpose - map and set properties don't have any type information at all (usually they have the property type(s))
- custom
AssetObjectPropertythat appears to be identical toStrProperty
@trumank wow that is cool! Sweet find on https://github.com/IncredulousMonk/sod2-tools/blob/main/sod2gvas.md Are the file headers and properties still mostly relevant? I am curious on how @IncredulousMonk or you are able to decipher the allocations and meanings of each byte. I tried using a hex editor but feel a bit lost with that tool with no prior experience. I assume these headers might be useful to grok for any future updates.
incremented header version (conflicting with recent Unreal Engine versions)
This game is a UE4 game, could this be related to that? The UE version is a part of the GVAS file from my hex exploration.
I will try out https://github.com/trumank/uesave-rs/commit/4a58133c3dbec16132f494240774996897a0c392 and see if I can contribute some more context on FTextHistory with game knowledge or otherwise.
The actual significance of version numbers is minimal as long as they don't interfere with parsing and survive round trip so the game can read the save still. This also applies to FTextHistory as I doubt they hold anything interesting to edit.
Hey, I was able to add new set and map properties to parse different save files (from a newer version). 44fc78882cfdfd392bf44175c99d24fd2cd78eec
I had a few follow-up questions:
- How do I know if the structure type I added is correct?
I did look up the lz4 decompressed file, but I could only read that it is a set or map type. I couldn't understand
trace_inner.jsonso that wasn't too helpful to me. - There are a few bytes left unparsed, how can I figure out their data types and what they hold?
- Is there a way to just ignore new unknown types, parse other known types to json and then convert it back to a save file?
- What changes should I be considering to support
from-json - Would these changes be general enough to contribute to your repo or should sod2 be its own forked repo?
Once again, thanks for all the help 👍
- How do I know if the structure type I added is correct? I did look up the lz4 decompressed file, but I could only read that it is a set or map type. I couldn't understand
trace_inner.jsonso that wasn't too helpful to me.
It's a lot of guess work based on what "looks right". If you make a guess and it allows parsing to continue, then it's likely correct. The type information can in theory be exported from game's binary via header dumps but would still require a lot of manual entry. The trace files are to be opened using a parser tracing tool I've been working on but it's still quite unpolished and documentation is non-existent.
- There are a few bytes left unparsed, how can I figure out their data types and what they hold?
These bytes were mentioned in the document I posted earlier, but so far it doesn't look like the purpose is known. For now I would just ignore them and maybe revisit later if the game fails to load the modified save file. uesave should handle saving and restoring them itself, it just gives a warning.
- Is there a way to just ignore new unknown types, parse other known types to json and then convert it back to a save file?
Properties have length information so it might be possible to skip parsing of unknown types and store them as binary blobs in the JSON, but that's not something I've explored.
- What changes should I be considering to support
from-json
For any read functions that have been added or modified, the corresponding write functions will need to be updated as well. In the interest of just getting the whole save to parse, I skipped over persisting all the necessary information to the JSON in a few places, so those will need to be fixed so that information is available to write back out to the save file. The test-resave command can be used to test if the JSON round-trip produces and identical save file, and the --debug flag can be added to output the rewritten save for debugging purposes (I recommend using 010editor for its binary diffing tool).
- Would these changes be general enough to contribute to your repo or should sod2 be its own forked repo?
Most of these changes are game specific and maintaining a bunch of game specific parsers in the same tool just isn't feasible (especially the really invasive ones such as this), so it should be its own version.
I'm glad that you found that document to be useful. I wrote it because that's the sort of information that I wish I had found in one place when I was looking at the save files.