using System.Collections.Generic; using System.IO; using System.Linq; namespace OpenVIII.AV { public static partial class Sound { #region Classes /// /// Entry in FMT file /// /// public class Entry { #region Fields /// /// Header Data to be prepended to sound data for ffmpeg or saving to drive. /// public readonly byte[] HeaderData; /// /// Offset of wav file in the dat. /// public readonly uint Offset; /// /// Size of wav file in the dat. /// public readonly uint Size; /// /// Size the header data in the FMT file. Rest has to be appended via Fill Header. /// private const uint OutputHeaderSize = 50; #endregion Fields #region Constructors /// /// Generate Entry /// /// Source of data /// size entry, this is checked for 0 before loading. private Entry(BinaryReader br, uint size) { Size = size; Offset = br.ReadUInt32(); //Unknown. If first byte is 0, the other 11 are always 0. Probably some kind of looping metadata. br.BaseStream.Seek(12, SeekOrigin.Current); HeaderData = FillHeader(br); } #endregion Constructors #region Properties private uint OutputDataSize => Size; /// /// Total size including the header /// private uint OutputTotalSize => Size + 70; #endregion Properties #region Methods public static implicit operator BufferData(Entry value) => new BufferData { DataSeekLoc = value.Offset, DataSize = value.Size, HeaderSize = checked((uint)value.HeaderData.Length), Target = BufferData.TargetFile.SoundDat }; public static IReadOnlyList Read(Stream s) { using (var br = new BinaryReader(s)) { //The FMT is headed by 4 bytes representing the number of sound file headers //in the rest of the FMT file, then by a 36 byte header that isn't interesting. var count = br.ReadInt32(); s.Seek(36, SeekOrigin.Current); return Enumerable.Range(0, count).Select(_ => CreateInstance(br)).Where(x => x != null).ToList() .AsReadOnly(); } } private static Entry CreateInstance(BinaryReader br) { var size = br.ReadUInt32(); if (size != 0) return new Entry(br, size); br.BaseStream.Seek(34, SeekOrigin.Current); return null; } private byte[] FillHeader(BinaryReader br) => FillHeader(br.ReadBytes(checked((int)OutputHeaderSize))); private byte[] FillHeader(byte[] headerBytes) { var headerData = new byte[OutputHeaderSize + 28]; using (var bw = new BinaryWriter(new MemoryStream(headerData))) { bw.Write(System.Text.Encoding.ASCII.GetBytes("RIFF")); bw.Write(OutputTotalSize); // ReSharper disable once StringLiteralTypo bw.Write(System.Text.Encoding.ASCII.GetBytes("WAVEfmt ")); bw.Write(OutputHeaderSize); bw.Write(headerBytes); bw.Write(System.Text.Encoding.ASCII.GetBytes("data")); bw.Write(OutputDataSize); } return headerData; } #endregion Methods } #endregion Classes } }