/// <summary> /// Simply return true and does nothing else. It depends on the implementation of the game. /// </summary> /// <param name="chunk"></param> /// <returns></returns> public virtual async ValueTask <bool> ParseGameSpecificHeaderChunk(ReplayArchiveAsync binaryReader) { if (await binaryReader.ReadUInt32Async() != DemoHeaderMagic) { return(false); } NetworkVersionHistory version = (NetworkVersionHistory)await binaryReader.ReadUInt32Async(); if (version < NetworkVersionHistory.saveFullEngineVersion) { return(false); } uint networkChecksum = await binaryReader.ReadUInt32Async(); EngineNetworkVersionHistory engineNetworkProtocolVersion = (EngineNetworkVersionHistory)await binaryReader.ReadUInt32Async(); uint gameNetworkProtocolVersion = await binaryReader.ReadUInt32Async(); Guid guid = Guid.Empty; if (version >= NetworkVersionHistory.guidDemoHeader) { guid = new Guid((await binaryReader.ReadBytesAsync(16)).Span); } ushort major = await binaryReader.ReadUInt16Async(); ushort minor = await binaryReader.ReadUInt16Async(); ushort patch = await binaryReader.ReadUInt16Async(); uint changeList = await binaryReader.ReadUInt32Async(); string branch = await binaryReader.ReadStringAsync(); (string, uint)[] levelNamesAndTimes = await binaryReader.ReadArrayAsync(async() => (await binaryReader.ReadStringAsync(), await binaryReader.ReadUInt32Async()));
public virtual async ValueTask <bool> ParseReplayHeader(ReplayArchiveAsync binaryReader) { if (!await ParseMagicNumber(binaryReader)) { return(false); } ReplayHeader.ReplayVersionHistory fileVersion = (ReplayHeader.ReplayVersionHistory) await binaryReader.ReadUInt32Async(); int lengthInMs = await binaryReader.ReadInt32Async(); uint networkVersion = await binaryReader.ReadUInt32Async(); uint changelist = await binaryReader.ReadUInt32Async(); string friendlyName = await binaryReader.ReadStringAsync(); bool isLive = await binaryReader.ReadUInt32Async() != 0; DateTime timestamp = DateTime.MinValue; if (fileVersion >= ReplayHeader.ReplayVersionHistory.recordedTimestamp) { timestamp = DateTime.FromBinary(await binaryReader.ReadInt64Async()); } bool compressed = false; if (fileVersion >= ReplayHeader.ReplayVersionHistory.compression) { compressed = await binaryReader.ReadUInt32Async() != 0; } ReplayHeader = new ReplayHeader(lengthInMs, networkVersion, changelist, friendlyName, timestamp, 0, isLive, compressed, fileVersion); return(true); }
/// <summary> /// Only does routing to the right method /// No operation should be done here. /// </summary> /// <param name="replayInfo"></param> /// <param name="chunk"></param> /// <returns></returns> public virtual async ValueTask <bool> ChooseChunkType(ReplayArchiveAsync binaryReader, ChunkType chunkType) { return((chunkType switch { ChunkType.Header => throw new InvalidOperationException("Replay Header was already read."), ChunkType.Checkpoint => await ParseCheckpointHeader(binaryReader), ChunkType.Event => await ParseEventHeader(binaryReader), ChunkType.ReplayData => await ParseReplayDataChunkHeader(binaryReader), _ => throw new InvalidOperationException("Invalid ChunkType") }) ? true : await ErrorOnChunkContentParsingAsync());
public virtual async ValueTask <bool> ParseEventHeader(ReplayArchiveAsync ar) { string id = await ar.ReadStringAsync(); string group = await ar.ReadStringAsync(); string metadata = await ar.ReadStringAsync(); uint time1 = await ar.ReadUInt32Async(); uint time2 = await ar.ReadUInt32Async(); int eventSizeInBytes = await ar.ReadInt32Async(); return(await ChooseEventChunkType(ar, new EventOrCheckpointInfo( id, group, metadata, time1, time2 ))); }
public virtual async ValueTask <bool> ParseReplayInfo() { using ReplayArchiveAsync ar = new ReplayArchiveAsync(SubStreamFactory.BaseStream, 0, compressed: false, leaveOpen: true); if (!await ParseReplayHeader(ar)) { return(false); } ChunkHeader chunkHeader = await ParseChunkHeader(); if (chunkHeader.ChunkType != ChunkType.Header) { return(false); } await using SubStream stream = SubStreamFactory.CreateSubstream(chunkHeader.ChunkSize); using ReplayArchiveAsync chunkReader = new ReplayArchiveAsync(stream, 0 /*We are parsing the chunk and don't know the version yet.*/, false, true); return(await ParseGameSpecificHeaderChunk(chunkReader)); }
public virtual async ValueTask <bool> ParseReplayDataChunkHeader(ReplayArchiveAsync chunkReader) { uint time1 = uint.MaxValue; uint time2 = uint.MaxValue; if (ReplayHeader !.FileVersion >= ReplayVersionHistory.streamChunkTimes) { time1 = await chunkReader.ReadUInt32Async(); time2 = await chunkReader.ReadUInt32Async(); int replaySizeInBytes = await chunkReader.ReadInt32Async(); } using (IMemoryOwner <byte> uncompressedData = await chunkReader.UncompressData())//TODO: check compress { return(ParseReplayData(new ChunkArchive(uncompressedData.Memory, DemoHeader !, ReplayHeader !))); } }
public virtual async ValueTask <bool> ParseCheckpointHeader(ReplayArchiveAsync binaryReader) { string id = await binaryReader.ReadStringAsync(); string group = await binaryReader.ReadStringAsync(); string metadata = await binaryReader.ReadStringAsync(); uint time1 = await binaryReader.ReadUInt32Async(); uint time2 = await binaryReader.ReadUInt32Async(); int eventSizeInBytes = await binaryReader.ReadInt32Async(); Debug.Assert(eventSizeInBytes == binaryReader.Length - binaryReader.Position); using (IMemoryOwner <byte> uncompressed = await binaryReader.UncompressData()) { return(ParseCheckpointContent(new ChunkArchive(uncompressed.Memory, DemoHeader !, ReplayHeader !), id, group, metadata, time1, time2)); } }
public virtual async ValueTask <bool> ParseCheckpointHeader(ReplayArchiveAsync binaryReader) { string id = await binaryReader.ReadStringAsync(); string group = await binaryReader.ReadStringAsync(); string metadata = await binaryReader.ReadStringAsync(); uint time1 = await binaryReader.ReadUInt32Async(); uint time2 = await binaryReader.ReadUInt32Async(); int eventSizeInBytes = await binaryReader.ReadInt32Async(); using (IMemoryOwner <byte> uncompressed = await binaryReader.UncompressData()) { return(ParseCheckpointContent(new ChunkArchive(uncompressed.Memory, DemoHeader !.EngineNetworkProtocolVersion), id, group, metadata, time1, time2)); return(true); } }
/// <summary> /// Parse the header of a chunk, with that we know the <see cref="ChunkType"/> and the length of the chunk /// Took extra caution because there is no <see cref="SubStream"/> to protect the reading. /// When I discover the length of the replay, I immediatly create a SubStream so i can protect the rest of the replay. /// </summary> /// <param name="replayInfo"></param> /// <returns></returns> public virtual async ValueTask <ChunkHeader> ParseChunkHeader() { if (SubStreamFactory.CanReadLength && SubStreamFactory.CanReadPosition && SubStreamFactory.BaseStream.Position == SubStreamFactory.BaseStream.Length) { return(new ChunkHeader { ChunkType = ChunkType.EndOfStream, ChunkSize = 0 }); } int chunkSize; ChunkType chunkType; await using (SubStream chunkHeader = SubStreamFactory.CreateSubstream(8)) await using (ReplayArchiveAsync customReader = new ReplayArchiveAsync(chunkHeader, 0, false, true)) { try //TODO add case when you can seek. { chunkType = (ChunkType)await customReader.ReadUInt32Async(); } catch (EndOfStreamException) { chunkHeader.CancelSelfRepositioning(); return(new ChunkHeader { ChunkType = ChunkType.EndOfStream, ChunkSize = 0 }); } chunkSize = await customReader.ReadInt32Async(); if ((uint)chunkType > 3) { return(new ChunkHeader { ChunkType = ChunkType.Unknown, ChunkSize = 0 }); } } return(new ChunkHeader { ChunkType = chunkType, ChunkSize = chunkSize }); }
/// <summary> /// We do nothing there /// </summary> /// <param name="eventInfo"></param> /// <returns>always <see langword="true"/></returns> public virtual ValueTask <bool> ChooseEventChunkType(ReplayArchiveAsync ar, EventOrCheckpointInfo eventInfo) { return(new ValueTask <bool>(true)); }
/// <summary> /// I don't know maybe you want to change that ? Why i did this ? I don't know me too. /// </summary> /// <param name="binaryReader"></param> /// <returns></returns> public virtual async ValueTask <bool> ParseMagicNumber(ReplayArchiveAsync binaryReader) { return(await VisitMagicNumber(await binaryReader.ReadUInt32Async())); }
public override ValueTask <bool> ChooseEventChunkType(ReplayArchiveAsync ar, EventOrCheckpointInfo eventInfo) => eventInfo.Group.Trim('\0') switch { "playerElim" => VisitPlayerElimChunk(ar, eventInfo),