// 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();
}
}
}