using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace AnotherReplayReader.ReplayFile { internal enum ReplayFooterOption { SeekToFooter, CurrentlyAtFooter, } internal sealed class ReplayFooter { public const uint Terminator = 0x7FFFFFFF; public static readonly byte[] FooterString = Encoding.ASCII.GetBytes("RA3 REPLAY FOOTER"); private readonly byte[] _data; public uint FinalTimeCode { get; } public TimeSpan ReplayLength => TimeSpan.FromSeconds(FinalTimeCode / Replay.FrameRate); public ReplayFooter(BinaryReader reader, ReplayFooterOption option) { var currentPosition = reader.BaseStream.Position; reader.BaseStream.Seek(-4, SeekOrigin.End); var footerLength = reader.ReadInt32(); if (option == ReplayFooterOption.SeekToFooter) { currentPosition = reader.BaseStream.Length - footerLength; } reader.BaseStream.Seek(currentPosition, SeekOrigin.Begin); var footer = reader.ReadBytes(footerLength); if (footer.Length != footerLength || reader.BaseStream.Position != reader.BaseStream.Length) { throw new InvalidDataException("Invalid footer"); } using var footerStream = new MemoryStream(footer); using var footerReader = new BinaryReader(footerStream); var footerString = footerReader.ReadBytes(17); if (!footerString.SequenceEqual(FooterString)) { throw new InvalidDataException("Invalid footer, no footer string"); } FinalTimeCode = footerReader.ReadUInt32(); _data = footerReader.ReadBytes(footer.Length - 25); if (footerReader.ReadInt32() != footerLength) { throw new InvalidDataException(); } } public ReplayFooter(uint finalTimeCode) { FinalTimeCode = finalTimeCode; _data = new byte[] { 0x02, 0x1A, 0x00, 0x00, 0x00 }; } List? TryGetKillDeathRatio() { if (_data.Length < 24) { return null; } var ratios = new List(); using var stream = new MemoryStream(_data, _data.Length - 24, 24); using var reader = new BinaryReader(stream); ratios.Add(reader.ReadSingle()); throw new NotImplementedException(); } public void WriteTo(BinaryWriter writer) { writer.Write(Terminator); writer.Write(FooterString); writer.Write(FinalTimeCode); writer.Write(_data); writer.Write(FooterString.Length + _data.Length + 8); } } }