Entry.cs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. using System.Collections.Generic;
  2. using System.IO;
  3. using System.Linq;
  4. namespace OpenVIII.AV
  5. {
  6. public static partial class Sound
  7. {
  8. #region Classes
  9. /// <summary>
  10. /// Entry in FMT file
  11. /// </summary>
  12. /// <see cref="http://wiki.ffrtt.ru/index.php?title=FF8/FileFormat_FMT"/>
  13. public class Entry
  14. {
  15. #region Fields
  16. /// <summary>
  17. /// Header Data to be prepended to sound data for ffmpeg or saving to drive.
  18. /// </summary>
  19. public readonly byte[] HeaderData;
  20. /// <summary>
  21. /// Offset of wav file in the dat.
  22. /// </summary>
  23. public readonly uint Offset;
  24. /// <summary>
  25. /// Size of wav file in the dat.
  26. /// </summary>
  27. public readonly uint Size;
  28. /// <summary>
  29. /// Size the header data in the FMT file. Rest has to be appended via Fill Header.
  30. /// </summary>
  31. private const uint OutputHeaderSize = 50;
  32. #endregion Fields
  33. #region Constructors
  34. /// <summary>
  35. /// Generate Entry
  36. /// </summary>
  37. /// <param name="br">Source of data</param>
  38. /// <param name="size">size entry, this is checked for 0 before loading.</param>
  39. private Entry(BinaryReader br, uint size)
  40. {
  41. Size = size;
  42. Offset = br.ReadUInt32();
  43. //Unknown. If first byte is 0, the other 11 are always 0. Probably some kind of looping metadata.
  44. br.BaseStream.Seek(12, SeekOrigin.Current);
  45. HeaderData = FillHeader(br);
  46. }
  47. #endregion Constructors
  48. #region Properties
  49. private uint OutputDataSize => Size;
  50. /// <summary>
  51. /// Total size including the header
  52. /// </summary>
  53. private uint OutputTotalSize => Size + 70;
  54. #endregion Properties
  55. #region Methods
  56. public static implicit operator BufferData(Entry value) => new BufferData
  57. {
  58. DataSeekLoc = value.Offset,
  59. DataSize = value.Size,
  60. HeaderSize = checked((uint)value.HeaderData.Length),
  61. Target = BufferData.TargetFile.SoundDat
  62. };
  63. public static IReadOnlyList<Entry> Read(Stream s)
  64. {
  65. using (var br = new BinaryReader(s))
  66. {
  67. //The FMT is headed by 4 bytes representing the number of sound file headers
  68. //in the rest of the FMT file, then by a 36 byte header that isn't interesting.
  69. var count = br.ReadInt32();
  70. s.Seek(36, SeekOrigin.Current);
  71. return Enumerable.Range(0, count).Select(_ => CreateInstance(br)).Where(x => x != null).ToList()
  72. .AsReadOnly();
  73. }
  74. }
  75. private static Entry CreateInstance(BinaryReader br)
  76. {
  77. var size = br.ReadUInt32();
  78. if (size != 0) return new Entry(br, size);
  79. br.BaseStream.Seek(34, SeekOrigin.Current);
  80. return null;
  81. }
  82. private byte[] FillHeader(BinaryReader br) => FillHeader(br.ReadBytes(checked((int)OutputHeaderSize)));
  83. private byte[] FillHeader(byte[] headerBytes)
  84. {
  85. var headerData = new byte[OutputHeaderSize + 28];
  86. using (var bw = new BinaryWriter(new MemoryStream(headerData)))
  87. {
  88. bw.Write(System.Text.Encoding.ASCII.GetBytes("RIFF"));
  89. bw.Write(OutputTotalSize);
  90. // ReSharper disable once StringLiteralTypo
  91. bw.Write(System.Text.Encoding.ASCII.GetBytes("WAVEfmt "));
  92. bw.Write(OutputHeaderSize);
  93. bw.Write(headerBytes);
  94. bw.Write(System.Text.Encoding.ASCII.GetBytes("data"));
  95. bw.Write(OutputDataSize);
  96. }
  97. return headerData;
  98. }
  99. #endregion Methods
  100. }
  101. #endregion Classes
  102. }
  103. }