public virtual async ValueTask <bool> ParseReplayHeader(CustomBinaryReaderAsync 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> /// 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(CustomBinaryReaderAsync 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()));
/// <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(CustomBinaryReaderAsync 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(CustomBinaryReaderAsync 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(); return(await ChooseEventChunkType(binaryReader, new EventOrCheckpointInfo( id, group, metadata, time1, time2 ))); }
public virtual async ValueTask <bool> ParseCheckpointHeader(CustomBinaryReaderAsync 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 MemoryReader(uncompressed.Memory, Endianness.Native), id, group, metadata, time1, time2)); } }
public virtual async ValueTask <bool> ParseReplayDataChunkHeader(CustomBinaryReaderAsync 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 MemoryReader( uncompressedData.Memory, Endianness.Native )); } return(true); }
public virtual async ValueTask <bool> ParseReplayInfo() { using (CustomBinaryReaderAsync binaryReader = new CustomBinaryReaderAsync(SubStreamFactory.BaseStream, true)) { if (!await ParseReplayHeader(binaryReader)) { return(false); } ChunkHeader chunkHeader = await ParseChunkHeader(); if (chunkHeader.ChunkType != ChunkType.Header) { return(false); } await using (SubStream stream = SubStreamFactory.CreateSubstream(chunkHeader.ChunkSize)) using (CustomBinaryReaderAsync chunkReader = new CustomBinaryReaderAsync(stream, true)) { return(await ParseGameSpecificHeaderChunk(chunkReader)); } } }
/// <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 (CustomBinaryReaderAsync customReader = new CustomBinaryReaderAsync(chunkHeader, 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 }); }
public virtual async ValueTask <bool> VisitChunks() { while (true) { ChunkHeader chunkHeader = await ParseChunkHeader(); await using (SubStream stream = SubStreamFactory.CreateSubstream(chunkHeader.ChunkSize)) using (CustomBinaryReaderAsync binaryReader = new CustomBinaryReaderAsync(stream, true)) { if (chunkHeader.ChunkType == ChunkType.EndOfStream) { if (await VisitEndOfStream()) { continue; } return(true); } if (!await ChooseChunkType(binaryReader, chunkHeader.ChunkType)) { return(false); } } } }
/// <summary> /// We do nothing there /// </summary> /// <param name="eventInfo"></param> /// <returns>always <see langword="true"/></returns> public virtual ValueTask <bool> ChooseEventChunkType(CustomBinaryReaderAsync binaryReader, EventOrCheckpointInfo eventInfo) { return(new ValueTask <bool>(true)); }
public override ValueTask <bool> ChooseEventChunkType(CustomBinaryReaderAsync chunkReader, EventOrCheckpointInfo eventInfo) => eventInfo.Group.Trim('\0') switch { "playerElim" => VisitPlayerElimChunk(chunkReader, eventInfo),
/// <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(CustomBinaryReaderAsync binaryReader) { return(await VisitMagicNumber(await binaryReader.ReadUInt32Async())); }