Skip to content

Commit

Permalink
improve level data types
Browse files Browse the repository at this point in the history
  • Loading branch information
moritz-h committed Nov 25, 2024
1 parent 3df917b commit c6cd952
Show file tree
Hide file tree
Showing 12 changed files with 183 additions and 148 deletions.
27 changes: 15 additions & 12 deletions docs/SATISFACTORY_SAVE.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ The binary data layout follows the following format:
```
+-------------------------------------------+------------------------------------------------------+
| int64 | total size of binary data (not including this value) |
| FWorldPartitionValidationData | ValidationData |
| FWorldPartitionValidationData | SaveGameValidationData |
| TMap<FString, FPerStreamingLevelSaveData> | mPerLevelDataMap |
| FPersistentAndRuntimeSaveData | mPersistentAndRuntimeData |
| FUnresolvedWorldSaveData | mUnresolvedWorldSaveData |
Expand Down Expand Up @@ -239,17 +239,20 @@ The number of objects in the TOCBlob and DataBlob are the same (first object in
### TOCBlob

```
+-----------------------------------+-----------------+
| int32 | numObjects |
| for i = 1 to numObjects: | |
| bool | isActor |
| if isActor: | |
| FActorSaveHeader | objectHeader |
| else: | |
| FObjectSaveHeader | objectHeader |
| if remaining data: | |
| TArray<FObjectReferenceDisc> | DestroyedActors |
+-----------------------------------+-----------------+
+-----------------------------------------------------+---------------------------+
| int32 | numObjects |
| for i = 1 to numObjects: | |
| bool | isActor |
| if isActor: | |
| FActorSaveHeader | objectHeader |
| else: | |
| FObjectSaveHeader | objectHeader |
| if remaining data: | |
| if within FPerStreamingLevelSaveData: | |
| TArray<FObjectReferenceDisc> | DestroyedActors |
| if within FPersistentAndRuntimeSaveData: | |
| TMap<FString, TArray<FObjectReferenceDisc>> | LevelToDestroyedActorsMap |
+-----------------------------------------------------+---------------------------+
```

`DestroyedActors` is not always present.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once

#include <optional>
#include <vector>

#include "../../IO/Archive/Archive.h"
#include "../UE/Core/Containers/Map.h"
#include "SatisfactorySave/GameTypes/FactoryGame/FGObjectReference.h"
#include "SatisfactorySave/GameTypes/Save/SaveObject.h"
#include "satisfactorysave_export.h"

namespace SatisfactorySave {

struct SATISFACTORYSAVE_API FPerStreamingLevelSaveData {
// The original struct in the game files actually uses TOCBlob64 and DataBlob64 buffers here, but instead we
// are directly serializing the data types within these buffers.
SaveObjectList SaveObjects;
std::optional<std::vector<FObjectReferenceDisc>> TOC_DestroyedActors;

std::vector<FObjectReferenceDisc> DestroyedActors;

void serialize(Archive& ar);
};

struct SATISFACTORYSAVE_API FPersistentAndRuntimeSaveData {
// The original struct in the game files actually uses TOCBlob64 and DataBlob64 buffers here, but instead we
// are directly serializing the data types within these buffers.
SaveObjectList SaveObjects;
std::optional<TMap<std::string, std::vector<FObjectReferenceDisc>>> TOC_LevelToDestroyedActorsMap;

TMap<std::string, std::vector<FObjectReferenceDisc>> LevelToDestroyedActorsMap;

void serialize(Archive& ar);
};

struct SATISFACTORYSAVE_API FUnresolvedWorldSaveData {
std::vector<FObjectReferenceDisc> DestroyedActors;

inline void serialize(Archive& ar) {
ar << DestroyedActors;
}
};

} // namespace SatisfactorySave
51 changes: 7 additions & 44 deletions libsave/include/SatisfactorySave/GameTypes/Save/SaveGame.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
#include <cstdint>
#include <filesystem>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>

#include "../../IO/Archive/IStreamArchive.h"
#include "../../IO/Archive/OStreamArchive.h"
#include "../FactoryGame/FGSaveManagerInterface.h"
#include "../FactoryGame/FGSaveSession.h"
#include "../FactoryGame/FWPSaveDataMigrationContext.h"
#include "SaveObject.h"
#include "satisfactorysave_export.h"
Expand All @@ -20,18 +18,12 @@ namespace SatisfactorySave {

class SATISFACTORYSAVE_API SaveGame {
public:
struct PerLevelData {
std::string level_name;
SaveObjectList save_objects;
std::optional<std::vector<FObjectReferenceDisc>> destroyed_actors_TOC;
std::vector<FObjectReferenceDisc> destroyed_actors;
};

struct PersistentAndRuntimeData {
SaveObjectList save_objects;
std::optional<std::vector<FObjectReferenceDisc>> destroyed_actors_TOC;
// TMap<FString, TArray<FObjectReferenceDisc>> LevelToDestroyedActorsMap; // is always zero.
};
// Save data
FSaveHeader mSaveHeader;
FWorldPartitionValidationData SaveGameValidationData;
TMap<std::string, FPerStreamingLevelSaveData> mPerLevelDataMap;
FPersistentAndRuntimeSaveData mPersistentAndRuntimeData;
FUnresolvedWorldSaveData mUnresolvedWorldSaveData;

struct SaveNode {
std::map<std::string, SaveNode> childNodes;
Expand All @@ -44,26 +36,6 @@ namespace SatisfactorySave {

void save(const std::filesystem::path& filepath);

[[nodiscard]] const FSaveHeader& header() const {
return header_;
}

[[nodiscard]] const FWorldPartitionValidationData& validationData() const {
return ValidationData;
}

[[nodiscard]] const std::vector<PerLevelData>& perLevelData() const {
return per_level_data_;
}

[[nodiscard]] const PersistentAndRuntimeData& persistentAndRuntimeData() const {
return persistent_and_runtime_data_;
}

[[nodiscard]] const std::vector<FObjectReferenceDisc>& unresolvedWorldSaveData() const {
return unresolved_world_save_data_;
}

[[nodiscard]] const SaveObjectList& allSaveObjects() const {
return all_save_objects_;
}
Expand Down Expand Up @@ -116,15 +88,6 @@ namespace SatisfactorySave {

void initAccessStructures(const SaveObjectList& saveObjects, SaveNode& rootNode);

// Save data
FSaveHeader header_;

FWorldPartitionValidationData ValidationData;

std::vector<PerLevelData> per_level_data_;
PersistentAndRuntimeData persistent_and_runtime_data_;
std::vector<FObjectReferenceDisc> unresolved_world_save_data_;

// Redundant structures for object access
SaveObjectList all_save_objects_;
std::unordered_map<SaveObjectPtr, SaveObjectInfo> info_map_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ namespace SatisfactorySave {
}
}

[[nodiscard]] std::size_t size() const {
if (Keys.size() != Values.size()) {
throw std::runtime_error("Invalid map state!");
}
return Keys.size();
}

ValueType& operator[](const KeyType& key) {
const auto it = std::find(Keys.begin(), Keys.end(), key);
if (it != Keys.end()) {
Expand Down
33 changes: 33 additions & 0 deletions libsave/src/GameTypes/FactoryGame/FGSaveSession.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "GameTypes/FactoryGame/FGSaveSession.h"

#include "../Save/SerializationUtils.h"
#include "IO/Archive/IStreamArchive.h"
#include "IO/Archive/OStreamArchive.h"

void SatisfactorySave::FPerStreamingLevelSaveData::serialize(Archive& ar) {
if (ar.isIArchive()) {
auto& inAr = dynamic_cast<IStreamArchive&>(ar);
parseTOCBlob(inAr, SaveObjects, TOC_DestroyedActors);
parseDataBlob(inAr, SaveObjects);
} else {
auto& outAr = dynamic_cast<OStreamArchive&>(ar);
saveTOCBlob(outAr, SaveObjects, TOC_DestroyedActors);
saveDataBlob(outAr, SaveObjects);
}

ar << DestroyedActors;
}

void SatisfactorySave::FPersistentAndRuntimeSaveData::serialize(Archive& ar) {
if (ar.isIArchive()) {
auto& inAr = dynamic_cast<IStreamArchive&>(ar);
parseTOCBlob(inAr, SaveObjects, TOC_LevelToDestroyedActorsMap);
parseDataBlob(inAr, SaveObjects);
} else {
auto& outAr = dynamic_cast<OStreamArchive&>(ar);
saveTOCBlob(outAr, SaveObjects, TOC_LevelToDestroyedActorsMap);
saveDataBlob(outAr, SaveObjects);
}

ar << LevelToDestroyedActorsMap;
}
Loading

0 comments on commit c6cd952

Please sign in to comment.