Copyright 2011-01-06 Werner Randelshofer

ch.randelshofer.media.riff
Class RIFFParser

java.lang.Object
  extended by ch.randelshofer.media.riff.RIFFParser

public class RIFFParser
extends java.lang.Object

Interprets Resource Interchange File Format (RIFF) streams.

Abstract

RIFF File Format A RIFF file consists of a RIFF header followed by zero or more lists and chunks.

The RIFF header has the following form:

 'RIFF' fileSize fileType (data)
 
where 'RIFF' is the literal FOURCC code 'RIFF', fileSize is a 4-byte value giving the size of the data in the file, and fileType is a FOURCC that identifies the specific file type. The value of fileSize includes the size of the fileType FOURCC plus the size of the data that follows, but does not include the size of the 'RIFF' FOURCC or the size of fileSize. The file data consists of chunks and lists, in any order.

FOURCCs
A FOURCC (four-character code) is a 32-bit unsigned integer created by concatenating four ASCII characters. For example, the FOURCC 'abcd' is represented on a Little-Endian system as 0x64636261. FOURCCs can contain space characters, so ' abc' is a valid FOURCC. The RIFF file format uses FOURCC codes to identify stream types, data chunks, index entries, and other information.

A chunk has the following form:

 ckID ckSize ckData
 
where ckID is a FOURCC that identifies the data contained in the chunk, ckData is a 4-byte value giving the size of the data in ckData, and ckData is zero or more bytes of data. The data is always padded to nearest WORD boundary. ckSize gives the size of the valid data in the chunk; it does not include the padding, the size of ckID, or the size of ckSize.

A list has the following form:

 'LIST' listSize listType listData
 
where 'LIST' is the literal FOURCC code 'LIST', listSize is a 4-byte value giving the size of the list, listType is a FOURCC code, and listData consists of chunks or lists, in any order. The value of listSize includes the size of listType plus the size of listData; it does not include the 'LIST' FOURCC or the size of listSize.

For more information see: http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c/directx/htm/avirifffilereference.asp http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c/directx/htm/aboutriff.asp

Grammar for RIFF streams used by this parser

 RIFFFile    ::= 'RIFF' FormGroup
 
GroupChunk ::= FormGroup | ListGroup FormGroup ::= size GroupType [ ChunkID LocalChunk [pad] | 'LIST ' ListGroup [pad] } ListGroup ::= size GroupType [ ChunkID LocalChunk [pad] | 'LIST ' ListGroup [pad] }
LocalChunk ::= DataChunk | CollectionChunk | PropertyChunk DataChunk ::= size [ struct ] CollectionChunk ::= size [ struct ] PropertyChunk ::= size [ struct ]
size ::= ULONG GroupType ::= FourCC ChunkID ::= FourCC pad ::= (BYTE)0 struct ::= any C language struct built with primitive data types.

Examples

Traversing the raw structure of a RIFF file

To traverse the file structure you must first set up a RIFFVisitor object that does something useful at each call to the visit method. Then create an instance of a RIFFParser and invoke the #interpret method.

 class RIFFRawTraversal
 .      {
 .      static class Visitor
 .      implements RIFFVisitor
 .              {
 .              ...implement the visitor interface here...
 .              }
 .
 .      public static void main(String[] args)
 .              {
 .              try     {
 .                      Visitor visitor = new Visitor();
 .                      FileInputStream stream = new FileInputStream(args[0]);
 .                      RIFFParser p = new RIFFParser();
 .                      p.interpret(stream,visitor);
 .                      stream.close();
 .                      }
 .              catch (IOException e) { System.out.println(e); }
 .              catch (InterpreterException e)  { System.out.println(e); }
 .              catch (AbortedException e)  { System.out.println(e); }
 .              }
 .      }
 

Traversing the RIFF file and interpreting its content.

Since RIFF files are not completely self describing (there is no information that helps differentiate between data chunks, property chunks and collection chunks) a reader must set up the interpreter with some contextual information before starting the interpreter.

Once at least one chunk has been declared, the interpreter will only call the visitor for occurences of the declared group chunks and data chunks. The property chunks and the collection chunks can be obtained from the current group chunk by calling #getProperty or #getCollection.
Note: All information the visitor can obtain during interpretation is only valid during the actual #visit... call. Dont try to get information about properties or collections for chunks that the visitor is not visiting right now.

 class InterpretingAnILBMFile
 .      {
 .      static class Visitor
 .      implements RIFFVisitor
 .              {
 .              ...
 .              }
 .
 .      public static void main(String[] args)
 .              {
 .              try     {
 .                      Visitor visitor = new Visitor();
 .                      FileInputStream stream = new FileInputStream(args[0]);
 .                      RIFFParser p = new RIFFParser();
 .                      p.declareGroupChunk('FORM','ILBM');
 .                      p.declarePropertyChunk('ILBM','BMHD');
 .                      p.declarePropertyChunk('ILBM','CMAP');
 .                      p.declareCollectionChunk('ILBM','CRNG');
 .                      p.declareDataChunk('ILBM','BODY');
 .                      p.interpret(stream,visitor);
 .                      stream.close();
 .                      }
 .              catch (IOException e) { System.out.println(e); }
 .              catch (InterpreterException e)  { System.out.println(e); }
 .              catch (AbortedException e)  { System.out.println(e); }
 .              }
 .      }
 

Version:
1.2 2010-07-06 Use integer IDs for efficiency. Added support for stop chunks.
1.0 2005-01-16 Created.
Author:
Werner Randelshofer, Hausmatt 10, CH-6405 Immensee, Switzerland
See Also:
RIFFVisitor

Field Summary
static int LIST_ID
          ID for ListGroupExpression.
static int NULL_ID
          ID for NULL chunks.
static int NULL_NUL_ID
          ID for NULL chunks.
static int RIFF_ID
          ID for FormGroupExpression.
 
Constructor Summary
RIFFParser()
          Constructs a new RIFF parser.
 
Method Summary
 void declareCollectionChunk(int type, int id)
          Declares a collection chunk.
 void declareDataChunk(int type, int id)
          Declares a data chunk.
 void declareGroupChunk(int type, int id)
          Declares a FORM group chunk.
 void declarePropertyChunk(int type, int id)
          Declares a property chunk.
 void declareStopChunks()
          Whether the parse should stop at all chunks.
static java.lang.String idToString(int anInt)
          Convert an integer IFF identifier to String.
protected  boolean isCollectionChunk(RIFFChunk chunk)
          Checks wether the ID of the chunk has been declared as a collection chunk.
protected  boolean isDataChunk(RIFFChunk chunk)
          Checks whether the ID of the chunk has been declared as a data chunk.
protected  boolean isGroupChunk(RIFFChunk chunk)
          Checks wether the ID of the chunk has been declared as a group chunk.
static boolean isGroupID(int id)
          Checks wether the argument represents a valid RIFF GroupID.
static boolean isGroupType(int id)
          Checks wether the argument represents a valid RIFF Group Type.
static boolean isID(int id)
          Checks if the argument represents a valid RIFF ID.
static boolean isLocalChunkID(int id)
          Returns whether the argument is a valid Local Chunk ID.
protected  boolean isPropertyChunk(RIFFChunk chunk)
          Checks wether the ID of the chunk has been declared as a property chunk.
 void parse(java.io.InputStream in, RIFFVisitor v)
          Interprets the RIFFFile expression located at the current position of the indicated InputStream.
static int stringToID(java.lang.String aString)
          Converts the first four letters of the String into an IFF Identifier.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

RIFF_ID

public static final int RIFF_ID
ID for FormGroupExpression.


LIST_ID

public static final int LIST_ID
ID for ListGroupExpression.


NULL_ID

public static final int NULL_ID
ID for NULL chunks.


NULL_NUL_ID

public static final int NULL_NUL_ID
ID for NULL chunks.

Constructor Detail

RIFFParser

public RIFFParser()
Constructs a new RIFF parser.

Method Detail

parse

public void parse(java.io.InputStream in,
                  RIFFVisitor v)
           throws ParseException,
                  AbortException,
                  java.io.IOException
Interprets the RIFFFile expression located at the current position of the indicated InputStream. Lets the visitor traverse the RIFF parse tree during interpretation.

Pre condition

  • Data-, property- and collection chunks must have been declared prior to this call.
  • When the client never declared chunks, then all local chunks will be interpreted as data chunks.
  • The stream must be positioned at the beginning of the RIFFFileExpression.

    Post condition

  • When no exception was thrown then the stream is positioned after the RIFFFile expression.

    Obligation The visitor may throw an ParseException or an AbortException during tree traversal.

    Throws:
    ParseException - Is thrown when an interpretation error occured. The stream is positioned where the error occured.
    AbortException - Is thrown when the visitor decided to abort the interpretation.
    java.io.IOException

  • isDataChunk

    protected boolean isDataChunk(RIFFChunk chunk)
    Checks whether the ID of the chunk has been declared as a data chunk.

    Pre condition

  • Data chunks must have been declared before the interpretation has been started.
  • This method will always return true when neither data chunks, property chunks nor collection chunks have been declared,

    Parameters:
    chunk - Chunk to be verified.
    Returns:
    True when the parameter is a data chunk.

  • isGroupChunk

    protected boolean isGroupChunk(RIFFChunk chunk)
    Checks wether the ID of the chunk has been declared as a group chunk.

    Pre condition

  • Group chunks must have been declared before the interpretation has been started. (Otherwise the response is always true).

    Parameters:
    chunk - Chunk to be verified.
    Returns:
    True when the visitor is interested in this is a group chunk.

  • isPropertyChunk

    protected boolean isPropertyChunk(RIFFChunk chunk)
    Checks wether the ID of the chunk has been declared as a property chunk.

    Pre condition

  • Property chunks must have been declared before the interpretation has been started.
  • This method will always return false when neither data chunks, property chunks nor collection chunks have been declared,


  • isCollectionChunk

    protected boolean isCollectionChunk(RIFFChunk chunk)
    Checks wether the ID of the chunk has been declared as a collection chunk.

    Pre condition

  • Collection chunks must have been declared before the interpretation has been started.
  • This method will always return true when neither data chunks, property chunks nor collection chunks have been declared,

    Parameters:
    chunk - Chunk to be verified.
    Returns:
    True when the parameter is a collection chunk.

  • declareDataChunk

    public void declareDataChunk(int type,
                                 int id)
    Declares a data chunk.

    Pre condition

  • The chunk must not have already been declared as of a different type.
  • Declarations may not be done during interpretation of an RIFFFileExpression.

    Post condition

  • Data chunk declared

    Parameters:
    type - Type of the chunk. Must be formulated as a TypeID conforming to the method #isFormType.
    id - ID of the chunk. Must be formulated as a ChunkID conforming to the method #isLocalChunkID.

  • declareGroupChunk

    public void declareGroupChunk(int type,
                                  int id)
    Declares a FORM group chunk.

    Pre condition

  • The chunk must not have already been declared as of a different type.
  • Declarations may not be done during interpretation of an RIFFFileExpression.

    Post condition

  • Group chunk declared

    Parameters:
    type - Type of the chunk. Must be formulated as a TypeID conforming to the method #isFormType.
    id - ID of the chunk. Must be formulated as a ChunkID conforming to the method #isContentsType.

  • declarePropertyChunk

    public void declarePropertyChunk(int type,
                                     int id)
    Declares a property chunk.

    Pre condition

  • The chunk must not have already been declared as of a different type.
  • Declarations may not be done during interpretation of an RIFFFileExpression.

    Post condition

  • Group chunk declared

    Parameters:
    type - Type of the chunk. Must be formulated as a TypeID conforming to the method #isFormType.
    id - ID of the chunk. Must be formulated as a ChunkID conforming to the method #isLocalChunkID.

  • declareCollectionChunk

    public void declareCollectionChunk(int type,
                                       int id)
    Declares a collection chunk.

    Pre condition

  • The chunk must not have already been declared as of a different type.
  • Declarations may not be done during interpretation of an RIFFFileExpression.

    Post condition

  • Group chunk declared

    Parameters:
    type - Type of the chunk. Must be formulated as a TypeID conforming to the method #isFormType.
    id - ID of the chunk. Must be formulated as a ChunkID conforming to the method #isLocalChunkID.

  • declareStopChunks

    public void declareStopChunks()
    Whether the parse should stop at all chunks.

    The parser does not read the data body of stop chunks.

    By declaring stop chunks, and not declaring any data, group or property chunks, the file structure of a RIFF file can be quickly scanned through.


    isGroupID

    public static boolean isGroupID(int id)
    Checks wether the argument represents a valid RIFF GroupID.

    Validation

    Parameters:
    id - Chunk ID to be checked.
    Returns:
    True when the chunk ID is a valid Group ID.

    isGroupType

    public static boolean isGroupType(int id)
    Checks wether the argument represents a valid RIFF Group Type.

    Validation

    Parameters:
    id - Chunk ID to be checked.
    Returns:
    True when the chunk ID is a valid Group ID.

    isID

    public static boolean isID(int id)
    Checks if the argument represents a valid RIFF ID.

    Validation

  • Every byte of an ID must be in the range of 0x20..0x7e
  • The id may not have leading spaces (unless the id is a NULL_ID).

    Parameters:
    id - Chunk ID to be checked.
    Returns:
    True when the ID is a valid IFF chunk ID.

  • isLocalChunkID

    public static boolean isLocalChunkID(int id)
    Returns whether the argument is a valid Local Chunk ID.

    Validation

  • Must be valid ID.
  • Local Chunk IDs may not collide with GroupIDs.
  • Must not be a NULL_ID.
  • Parameters:
    id - Chunk ID to be checked.
    Returns:
    True when the chunk ID is a Local Chunk ID.

    idToString

    public static java.lang.String idToString(int anInt)
    Convert an integer IFF identifier to String.

    Parameters:
    anInt - ID to be converted.
    Returns:
    String representation of the ID.

    stringToID

    public static int stringToID(java.lang.String aString)
    Converts the first four letters of the String into an IFF Identifier.

    Parameters:
    aString - String to be converted.
    Returns:
    ID representation of the String.

    Copyright 2011-01-06 Werner Randelshofer