public bool DeserializeObjectType(Type type, out object objectInstance, object existingObjectInstance = null) { if (!type.IsClass && !type.IsValueType) { objectInstance = null; return(false); } objectInstance = existingObjectInstance ?? RTTI.CreateObjectInstance(type); if (objectInstance is RTTI.ISaveSerializable asSerializable) { // Custom deserialization function implemented. Let the interface do the work. asSerializable.DeserializeStateObject(this); } else { DeserializeObjectClassMembers(type, objectInstance); } return(true); }
public SaveGameSystem(string savePath, FileMode mode = FileMode.Open) { if (mode == FileMode.Open) { var fileHandle = File.Open(savePath, mode, FileAccess.Read, FileShare.Read); using (var reader = new BinaryReader(fileHandle, Encoding.UTF8, true)) { // Offset 0x0 string gameVersionString = Encoding.ASCII.GetString(reader.ReadBytesStrict(32)); uint gameVersion = reader.ReadUInt32(); byte saveVersion = reader.ReadByte(); byte saveFlags = reader.ReadByte(); // { 0x80 = unknown, 0x1 = NG+, 0x2 = DLC entitlements } ushort worldIdHash = reader.ReadUInt16(); // CRC32-C xor'd - "World" bool isCoopGameMode = reader.ReadBooleanStrict(); _ = reader.ReadBytesStrict(3); // Offset 0x2C uint gameStateBlockLength = reader.ReadUInt32(); var gameStateBlock = reader.ReadBytesStrict(256); if (gameStateBlockLength != 84) { throw new Exception(); } // Offset 0x130 int unknown1 = reader.ReadInt32(); // Sign extended int saveType = reader.ReadInt32(); // Sign extended { 1 = manual, 2 = quick, 4 = auto, 8 = NG+ start point } var gameModuleGUID = new GGUUID().FromData(reader); // Field from `class GameModule` var uniqueSaveGUID = new GGUUID().FromData(reader); // CoCreateGuid() on save var gameLoadGUID = new GGUUID().FromData(reader); // CoCreateGuid() on game start var systemTypeGUID = new GGUUID().FromData(reader); // Possibly GUID for Win32System or physics double playTimeInSeconds = reader.ReadDouble(); _ = reader.ReadBytesStrict(108); // Offset 0x1EC var dataBlockMD5 = reader.ReadBytesStrict(16); uint dataBlockLength = reader.ReadUInt32(); // Parse actual save data State = new SaveState(reader, saveVersion, (uint)reader.BaseStream.Position, dataBlockLength); var unknownData1 = State.Reader.ReadBytesStrict(24); var unknownObject1 = State.ReadObjectHandle(); var unknownString1 = State.ReadIndexedString();// Likely entity RTTI name for the player's current mount. Instanced by AIManager. var unknownData2 = State.Reader.ReadBytesStrict(24); var unknownObject2 = State.ReadObjectHandle(); GlobalGameModule = RTTI.CreateObjectInstance <GameModule>(); GlobalStreamingStrategyManagerGame = RTTI.CreateObjectInstance <StreamingStrategyManagerGame>(); GlobalSceneManagerGame = RTTI.CreateObjectInstance <SceneManagerGame>(); // GameModule info { byte unknownByte = State.Reader.ReadByte(); if (unknownByte != 0) { var unknownData = State.Reader.ReadBytesStrict(24); } } GlobalStreamingStrategyManagerGame.ReadSave(State); GlobalSceneManagerGame.ReadSave(State); GlobalFactDatabase = State.DeserializeType <FactDatabase>(); GlobalGameModule.ReadSaveSystem(this); } } else if (mode == FileMode.Create || mode == FileMode.CreateNew) { throw new NotImplementedException("Writing archives is not supported at the moment"); } else { throw new NotImplementedException("Archive file mode must be Open, Create, or CreateNew"); } }
private void ReadHeaderData() { Reader.BaseStream.Position = SaveDataOffset + SaveDataLength - 0x8; uint typeDataChunkOffset = Reader.ReadUInt32(); uint rawDataChunkOffset = Reader.ReadUInt32(); // String/GUID tables Reader.BaseStream.Position = SaveDataOffset + rawDataChunkOffset; { // UTF-8 strings StringPool = StringTableContainer.FromData(this); // UTF-16 strings WideStringPool = WideStringTableContainer.FromData(this); // GUIDs if (SaveVersion >= 26) { GUIDPool = GUIDTableContainer.FromData(this); } } // Serialized type information and object instances Reader.BaseStream.Position = SaveDataOffset + typeDataChunkOffset; { // Create basic objects that are immediately registered with the engine int objectInstanceTypeCount = ReadVariableLengthOffset(); LocalSaveObjects = new Dictionary <int, object>(); for (int i = 0; i < objectInstanceTypeCount; i++) { Type objectType = RTTI.GetTypeByName(ReadIndexedString()); int instanceCount = ReadVariableLengthOffset(); for (int j = 0; j < instanceCount; j++) { int objectId = ReadVariableLengthOffset(); LocalSaveObjects.Add(objectId, RTTI.CreateObjectInstance(objectType)); } } // Handles to game data objects int gameDataObjectCount = ReadVariableLengthOffset(); GameDataObjects = new Dictionary <int, BaseGGUUID>(); for (int i = 0; i < gameDataObjectCount; i++) { int objectId = ReadVariableLengthOffset(); var guid = new BaseGGUUID().FromData(Reader); GameDataObjects.Add(objectId, guid); } // RTTI/class member layouts RTTIContainer = RTTIMapContainer.FromData(this); // File prefetch int prefetchFileCount = ReadVariableLengthOffset(); PrefetchFilePaths = new List <string>(prefetchFileCount); for (int i = 0; i < prefetchFileCount; i++) { PrefetchFilePaths.Add(ReadIndexedString()); } } Reader.BaseStream.Position = SaveDataOffset; }