- The Resource Interchange File Format (RIFF)
- Exploring the ca.mb.javajeff.riff Package
- The ANI Format
- Inside the Animated Cursor Library, Part 2
- Conclusion
Exploring the ca.mb.javajeff.riff Package
To simplify the tasks of navigating through and extracting data from RIFF-based .ani files, I’ve created a ca.mb.javajeff.riff package containing RIFF, Chunk, and BadRIFFException classes. The RIFF class, which only recognizes RIFF files with RIFF parent chunks, presents four constructors and three non-constructor methods:
- public RIFF(File file) opens an input stream to the RIFF file identified by its File argument, reads the first chunk’s header, verifies that this chunk is RIFF, and reads the RIFF chunk’s form type. This constructor throws IOException for a file I/O problem; it throws BadRIFFException if no valid RIFF chunk is found.
- public RIFF(InputStream is) reads the first chunk’s header from the already open input stream identified by its InputStream argument, verifies that this chunk is RIFF, and reads the RIFF chunk’s form type. This constructor throws the same exceptions as the previous constructor.
- public RIFF(String filename) opens an input stream to the RIFF file identified by its String argument, reads the first chunk’s header, verifies that this chunk is RIFF, and reads the RIFF chunk’s form type. This constructor throws the same exceptions as the previous constructors.
- public RIFF(URL url) opens an input stream to the RIFF file identified by its URL argument, reads the first chunk’s header, verifies that this chunk is RIFF, and reads the RIFF chunk’s form type. This constructor throws the same exceptions as the previous constructors.
- public void close() closes the currently open input stream. You must call this method after you finish reading chunks. You don’t need to call close() when a constructor throws an exception. This method throws no exceptions.
- public Chunk getChunk() returns the next chunk (starting with the first chunk inside the RIFF chunk) from the input stream. If there are no more chunks, getChunk() returns null. This method throws the same exceptions as the constructors.
- public String getFormType() returns the RIFF chunk’s form type as a four-character string.
There is a problem with part 1’s implementation of the RIFF constructors listed above. Because part 1’s constructors assume that RIFF files strictly follow the RIFF specification, the constructors don’t verify that a RIFF file’s RIFF chunk header size field contains a value that’s eight bytes less than the RIFF file’s size—the eight bytes account for the header’s four-byte ID and size fields.
Because this check isn’t performed, the RIFF instance’s starting "bytes left to read" tally isn’t properly initialized for RIFF files where the header’s size field equals the file size. This incorrect tally value results in getChunk() eventually throwing a BadRIFFException rather than returning null when it reaches the end of the file.
The constructors in this article’s RIFF class address this problem by keeping track of the number of bytes read and comparing this value to the header’s size field each time getChunk() is called. This method returns null if the number of bytes read equals the header size value plus eight. It also returns null if the header size value incorrectly equals just the header size.
The getChunk() method returns a chunk as an instance of the Chunk class. This class is described in Listing 2.
Listing 2 Chunk.java.
// Chunk.java package ca.mb.javajeff.riff; public class Chunk { public String name; public long size; public byte [] data; }
Chunk’s name and size fields contain the values that are located in a chunk header. For non-LIST chunks, the data field contains the entire contents of the chunk’s data area—the length of this byte array equals the value in the size field. For LIST chunks, data contains only the four-byte list type.
The final class in the ca.mb.javajeff.riff package is BadRIFFException. Listing 3 describes this class.
Listing 3 BadRIFFException.java.
// BadRIFFException.java package ca.mb.javajeff.riff; public class BadRIFFException extends Exception { public BadRIFFException (String message) { super (message); } }
BadRIFFException is simpler than BadAniException (see part 1 for the source code for this class) because it doesn’t require a constructor to wrap exceptions. BadAniException includes a constructor to wrap exceptions to minimize coupling between an application and the animation library’s support infrastructure—an application only imports a support package to access the wrapped exception.