// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; using System.Collections; using System.Collections.Generic; using System.IO; namespace SharpDX.Multimedia { /// /// Riff chunk enumerator. /// public class RiffParser : IEnumerator, IEnumerable { private readonly Stream input; private readonly long startPosition; private readonly BinaryReader reader; private readonly Stack chunckStack; private bool descendNext; private bool isEndOfRiff; private bool isErrorState; private RiffChunk current; /// /// Initializes a new instance of the class. /// /// The input. public RiffParser(Stream input) { this.input = input; this.startPosition = input.Position; this.reader = new BinaryReader(input); this.chunckStack = new Stack(); } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { // Nothing to dispose. } /// /// Advances the enumerator to the next element of the collection. /// /// /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. /// /// /// The collection was modified after the enumerator was created. /// public bool MoveNext() { CheckState(); if (current != null) { // By default, set the starting position to the data of the chunk long nextOffset = current.DataPosition; // If we descend if (descendNext) { // Next time, proceed chunk sequentially descendNext = false; } else { // Else, go to next chunk nextOffset += current.Size; // Pad DWORD if ((nextOffset & 1) != 0) nextOffset++; } input.Position = nextOffset; // Check that moveNext is not going outside a parent chunk. // If yes, pop the last chunk from the stack var currentChunkContainer = chunckStack.Peek(); long endOfOuterChunk = currentChunkContainer.DataPosition + currentChunkContainer.Size; if (input.Position >= endOfOuterChunk) chunckStack.Pop(); // If there are no more chunk in the if (chunckStack.Count == 0) { isEndOfRiff = true; return false; } } var fourCC = ((FourCC) reader.ReadUInt32()); bool isList = (fourCC == "LIST"); bool isHeader = (fourCC == "RIFF"); uint chunkSize = 0; if (input.Position == (startPosition+4) && !isHeader) { isErrorState = true; throw new InvalidOperationException("Invalid RIFF file format"); } // Read chunk size chunkSize = reader.ReadUInt32(); // If list or header if (isList || isHeader) { // Check file size if (isHeader && chunkSize > (input.Length - 8)) { isErrorState = true; throw new InvalidOperationException("Invalid RIFF file format"); } chunkSize -= 4; fourCC = reader.ReadUInt32(); } // Read RIFF type and create chunk current = new RiffChunk(input, fourCC, chunkSize, (uint)input.Position, isList, isHeader); return true; } private void CheckState() { if (isEndOfRiff) throw new InvalidOperationException("End of Riff. Cannot MoveNext"); if (isErrorState) throw new InvalidOperationException("The enumerator is in an error state"); } /// /// Gets the current stack of chunks. /// public Stack ChunkStack { get { return chunckStack; } } /// /// Sets the enumerator to its initial position, which is before the first element in the collection. /// /// /// The collection was modified after the enumerator was created. /// public void Reset() { CheckState(); current = null; input.Position = startPosition; } /// /// Ascends to the outer chunk. /// public void Ascend() { CheckState(); var outerChunk = chunckStack.Pop(); input.Position = outerChunk.DataPosition + outerChunk.Size; } /// /// Descends to the current chunk. /// public void Descend() { CheckState(); chunckStack.Push(current); descendNext = true; } /// /// Gets all chunks. /// /// public IList GetAllChunks() { var chunks = new List(); foreach (var riffChunk in this) chunks.Add(riffChunk); return chunks; } /// /// Gets the element in the collection at the current position of the enumerator. /// /// /// The element in the collection at the current position of the enumerator. /// public RiffChunk Current { get { CheckState(); return current; } } /// /// Returns an enumerator that iterates through the collection. /// /// /// A that can be used to iterate through the collection. /// public IEnumerator GetEnumerator() { return this; } object IEnumerator.Current { get { CheckState(); return Current; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }