mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-06 23:16:27 +00:00
trunk contents moved to root
This commit is contained in:
841
libsrc/avi/src/org/monte/media/riff/RIFFParser.java
Normal file
841
libsrc/avi/src/org/monte/media/riff/RIFFParser.java
Normal file
@@ -0,0 +1,841 @@
|
||||
/*
|
||||
* @(#)RIFFParser.java 1.5 2012-08-03
|
||||
*
|
||||
* Copyright (c) 2005-2012 Werner Randelshofer, Goldau, Switzerland.
|
||||
* All rights reserved.
|
||||
*
|
||||
* You may not use, copy or modify this file, except in compliance with the
|
||||
* license agreement you entered into with Werner Randelshofer.
|
||||
* For details see accompanying license terms.
|
||||
*/
|
||||
package org.monte.media.riff;
|
||||
|
||||
import org.monte.media.AbortException;
|
||||
import org.monte.media.ParseException;
|
||||
import org.monte.media.io.ImageInputStreamAdapter;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.WeakHashMap;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
/**
|
||||
* Interprets Resource Interchange File Format (RIFF) streams.
|
||||
*
|
||||
* <p><b>Abstract</b>
|
||||
* <p>
|
||||
* <b>RIFF File Format</b>
|
||||
* A RIFF file consists of a RIFF header followed by zero or more lists and chunks.
|
||||
* <p>
|
||||
* The RIFF header has the following form:
|
||||
* <pre>
|
||||
* 'RIFF' fileSize fileType (data)
|
||||
* </pre>
|
||||
* 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.
|
||||
* <p>
|
||||
* <b>FOURCCs</b><br>
|
||||
* 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.
|
||||
* <p>
|
||||
* A chunk has the following form:
|
||||
* <pre>
|
||||
* ckID ckSize ckData
|
||||
* </pre>
|
||||
* 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.
|
||||
* <p>
|
||||
* A list has the following form:
|
||||
* <pre>
|
||||
* 'LIST' listSize listType listData
|
||||
* </pre>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* <p><b>Grammar for RIFF streams used by this parser</b>
|
||||
* <pre>
|
||||
* RIFFFile ::= 'RIFF' FormGroup
|
||||
* <br>
|
||||
* GroupChunk ::= FormGroup | ListGroup
|
||||
* FormGroup ::= size GroupType [ ChunkID LocalChunk [pad] | 'LIST ' ListGroup [pad] }
|
||||
* ListGroup ::= size GroupType [ ChunkID LocalChunk [pad] | 'LIST ' ListGroup [pad] }
|
||||
* <br>
|
||||
* LocalChunk ::= DataChunk | CollectionChunk | PropertyChunk
|
||||
* DataChunk ::= size [ struct ]
|
||||
* CollectionChunk ::= size [ struct ]
|
||||
* PropertyChunk ::= size [ struct ]
|
||||
* <br>
|
||||
* size ::= ULONG
|
||||
* GroupType ::= FourCC
|
||||
* ChunkID ::= FourCC
|
||||
* pad ::= (BYTE)0
|
||||
* struct ::= any C language struct built with primitive data types.
|
||||
* </pre>
|
||||
*
|
||||
* <p><b>Examples</b>
|
||||
*
|
||||
* <p><b>Traversing the raw structure of a RIFF file</b>
|
||||
* <p>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.
|
||||
*
|
||||
* <pre>
|
||||
* 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); }
|
||||
* . }
|
||||
* . }
|
||||
* </pre>
|
||||
*
|
||||
* <p><b>Traversing the RIFF file and interpreting its content.</b>
|
||||
* <p>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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <br>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.
|
||||
*
|
||||
* <pre>
|
||||
* 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); }
|
||||
* . }
|
||||
* . }
|
||||
* </pre>
|
||||
*
|
||||
* @see RIFFVisitor
|
||||
*
|
||||
* @author Werner Randelshofer, Hausmatt 10, CH-6405 Goldau, Switzerland
|
||||
* @version 1.5 2012-08-03 Adds offset property.
|
||||
* <br>1.4 2011-08-26 Adds HashMap for stop chunks.
|
||||
* <br>1.3 2011-01-23 Use HashMap instead of Hashtable.
|
||||
* <br>1.2 2010-07-06 Use integer IDs for efficiency. Added support for
|
||||
* stop chunks.
|
||||
* <br>1.0 2005-01-16 Created.
|
||||
*/
|
||||
public class RIFFParser extends Object {
|
||||
private final static boolean DEBUG =false;
|
||||
/** ID for FormGroupExpression. */
|
||||
public final static int RIFF_ID = stringToID("RIFF");
|
||||
/** ID for ListGroupExpression. */
|
||||
public final static int LIST_ID = stringToID("LIST");
|
||||
/** ID for NULL chunks. */
|
||||
public final static int NULL_ID = stringToID(" ");
|
||||
/** ID for NULL chunks. */
|
||||
public final static int NULL_NUL_ID = stringToID("\0\0\0\0");
|
||||
/** ID for JUNK chunks. */
|
||||
public final static int JUNK_ID = stringToID("JUNK");
|
||||
/** The visitor traverses the parse tree. */
|
||||
private RIFFVisitor visitor;
|
||||
/** List of data chunks the visitor is interested in. */
|
||||
private HashSet<RIFFChunk> dataChunks;
|
||||
/** List of property chunks the visitor is interested in. */
|
||||
private HashSet<RIFFChunk> propertyChunks;
|
||||
/** List of collection chunks the visitor is interested in. */
|
||||
private HashSet<RIFFChunk> collectionChunks;
|
||||
/** List of stop chunks the visitor is interested in. */
|
||||
private HashSet<Integer> stopChunkTypes;
|
||||
/** List of group chunks the visitor is interested in. */
|
||||
private HashSet<RIFFChunk> groupChunks;
|
||||
/** Reference to the input stream. */
|
||||
private RIFFPrimitivesInputStream in;
|
||||
/** Reference to the image input stream. */
|
||||
private ImageInputStream iin;
|
||||
|
||||
/** Whether we stop at all chunks. */
|
||||
private boolean isStopChunks;
|
||||
|
||||
/** Stream offset. */
|
||||
private long streamOffset;
|
||||
|
||||
/* ---- constructors ---- */
|
||||
/**
|
||||
* Constructs a new RIFF parser.
|
||||
*/
|
||||
public RIFFParser() {
|
||||
}
|
||||
|
||||
public long getStreamOffset() {
|
||||
return streamOffset;
|
||||
}
|
||||
|
||||
public void setStreamOffset(long offset) {
|
||||
this.streamOffset = offset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ---- accessor methods ---- */
|
||||
/* ---- action methods ---- */
|
||||
/**
|
||||
* Interprets the RIFFFile expression located at the
|
||||
* current position of the indicated InputStream.
|
||||
* Lets the visitor traverse the RIFF parse tree during
|
||||
* interpretation.
|
||||
*
|
||||
* <p>Pre condition
|
||||
* <li> Data-, property- and collection chunks must have been
|
||||
* declared prior to this call.
|
||||
* <li> When the client never declared chunks, then all local
|
||||
* chunks will be interpreted as data chunks.
|
||||
* <li> The stream must be positioned at the beginning of the
|
||||
* RIFFFileExpression.
|
||||
*
|
||||
* <p>Post condition
|
||||
* <li> When no exception was thrown then the stream is positioned
|
||||
* after the RIFFFile expression.
|
||||
*
|
||||
* <p>Obligation
|
||||
* The visitor may throw an ParseException or an
|
||||
* AbortException during tree traversal.
|
||||
*
|
||||
* @exception ParseException
|
||||
* Is thrown when an interpretation error occured.
|
||||
* The stream is positioned where the error occured.
|
||||
* @exception AbortException
|
||||
* Is thrown when the visitor decided to abort the
|
||||
* interpretation.
|
||||
*/
|
||||
public long parse(InputStream in, RIFFVisitor v)
|
||||
throws ParseException, AbortException, IOException {
|
||||
this.in = new RIFFPrimitivesInputStream(in);
|
||||
visitor = v;
|
||||
parseFile();
|
||||
return getScan(this.in);
|
||||
}
|
||||
|
||||
public long parse(ImageInputStream in, RIFFVisitor v)
|
||||
throws ParseException, AbortException, IOException {
|
||||
return parse(new ImageInputStreamAdapter(in), v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a RIFF file.
|
||||
*
|
||||
* <pre>
|
||||
* RIFF = 'RIFF' FormGroup
|
||||
* </pre>
|
||||
*/
|
||||
private void parseFile()
|
||||
throws ParseException, AbortException, IOException {
|
||||
int id = in.readFourCC();
|
||||
|
||||
if (id == RIFF_ID) {
|
||||
parseFORM(null);
|
||||
} else if (id == JUNK_ID) {
|
||||
parseLocalChunk(null,id);
|
||||
} else {
|
||||
if (iin!=null) {
|
||||
throw new ParseException("Invalid RIFF File ID: \"" + idToString(id) +" 0x"+Integer.toHexString(id)+" near "+iin.getStreamPosition()+" 0x"+Long.toHexString(iin.getStreamPosition()));
|
||||
} else {
|
||||
throw new ParseException("Invalid RIFF File ID: \"" + idToString(id) +" 0x"+Integer.toHexString(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long getScan(RIFFPrimitivesInputStream in) {
|
||||
return in.getScan()+streamOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a FORM group.
|
||||
* <pre>
|
||||
* FormGroup ::= size GroupType { ChunkID LocalChunk [pad]
|
||||
* | 'FORM' FormGroup [pad] }
|
||||
* | 'LIST' ListGroup [pad] }
|
||||
* </pre>
|
||||
*/
|
||||
private void parseFORM(HashMap props)
|
||||
throws ParseException, AbortException, IOException {
|
||||
long size = in.readULONG();
|
||||
long offset = getScan(in);
|
||||
int type = in.readFourCC();
|
||||
if (DEBUG)System.out.println("RIFFParser.parseForm "+idToString(type));
|
||||
if (!isGroupType(type)) {
|
||||
throw new ParseException("Invalid FORM Type: \"" + idToString(type) + "\"");
|
||||
}
|
||||
|
||||
RIFFChunk propGroup = (props == null) ? null : (RIFFChunk) props.get(type);
|
||||
RIFFChunk chunk = new RIFFChunk(type, RIFF_ID, size, offset, propGroup);
|
||||
|
||||
boolean visitorWantsToEnterGroup = false;
|
||||
if (isGroupChunk(chunk) && (visitorWantsToEnterGroup = visitor.enteringGroup(chunk))) {
|
||||
visitor.enterGroup(chunk);
|
||||
}
|
||||
|
||||
try {
|
||||
long finish = offset + size;
|
||||
while (getScan(in) < finish) {
|
||||
long idscan = getScan(in);
|
||||
int id = in.readFourCC();
|
||||
|
||||
if (id == RIFF_ID) {
|
||||
parseFORM(props);
|
||||
} else if (id == LIST_ID) {
|
||||
parseLIST(props);
|
||||
} else if (isLocalChunkID(id)) {
|
||||
parseLocalChunk(chunk, id);
|
||||
} else {
|
||||
ParseException pex = new ParseException("Invalid Chunk: \"" + id + "\" at offset:" + idscan);
|
||||
chunk.setParserMessage(pex.getMessage());
|
||||
throw pex;
|
||||
}
|
||||
|
||||
in.align();
|
||||
}
|
||||
} catch (EOFException e) {
|
||||
e.printStackTrace();
|
||||
chunk.setParserMessage(
|
||||
"Unexpected EOF after "
|
||||
+ NumberFormat.getInstance().format(getScan(in) - offset)
|
||||
+ " bytes");
|
||||
} finally {
|
||||
if (visitorWantsToEnterGroup) {
|
||||
visitor.leaveGroup(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a LIST group.
|
||||
* <pre>
|
||||
* ListGroup ::= size GroupType { ChunkID LocalChunk [pad] | 'LIST ' ListGroup [pad] }
|
||||
* </pre>
|
||||
*/
|
||||
private void parseLIST(HashMap props)
|
||||
throws ParseException, AbortException, IOException {
|
||||
long size = in.readULONG();
|
||||
long scan = getScan(in);
|
||||
int type = in.readFourCC();
|
||||
if (DEBUG)System.out.println("RIFFParser.parseLIST "+idToString(type));
|
||||
|
||||
if (!isGroupType(type)) {
|
||||
throw new ParseException("Invalid LIST Type: \"" + type + "\"");
|
||||
}
|
||||
|
||||
RIFFChunk propGroup = (props == null) ? null : (RIFFChunk) props.get(type);
|
||||
RIFFChunk chunk = new RIFFChunk(type, LIST_ID, size, scan, propGroup);
|
||||
|
||||
boolean visitorWantsToEnterGroup = false;
|
||||
if (isGroupChunk(chunk) && (visitorWantsToEnterGroup = visitor.enteringGroup(chunk))) {
|
||||
visitor.enterGroup(chunk);
|
||||
}
|
||||
try {
|
||||
if (visitorWantsToEnterGroup) {
|
||||
long finish = scan + size;
|
||||
while (getScan(in) < finish) {
|
||||
long idscan = getScan(in);
|
||||
int id = in.readFourCC();
|
||||
if (id == LIST_ID) {
|
||||
parseLIST(props);
|
||||
} else if (isLocalChunkID(id)) {
|
||||
parseLocalChunk(chunk, id);
|
||||
} else {
|
||||
parseGarbage(chunk, id, finish-getScan(in), getScan(in));
|
||||
ParseException pex = new ParseException("Invalid Chunk: \"" + id + "\" at offset:" + idscan);
|
||||
chunk.setParserMessage(pex.getMessage());
|
||||
//throw pex;
|
||||
}
|
||||
|
||||
in.align();
|
||||
}
|
||||
} else {
|
||||
in.skipFully(size-4);
|
||||
in.align();
|
||||
}
|
||||
} finally {
|
||||
if (visitorWantsToEnterGroup) {
|
||||
visitor.leaveGroup(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a local chunk.
|
||||
* <pre>
|
||||
* LocalChunk ::= size { DataChunk | PropertyChunk | CollectionChunk }
|
||||
* DataChunk = PropertyChunk = CollectionChunk ::= { byte }...*size
|
||||
* </pre>
|
||||
*/
|
||||
private void parseLocalChunk(RIFFChunk parent, int id)
|
||||
throws ParseException, AbortException, IOException {
|
||||
long size = in.readULONG();
|
||||
long scan = getScan(in);
|
||||
if (DEBUG)System.out.println("RIFFParser.parseLocalChunk "+idToString(id));
|
||||
RIFFChunk chunk = new RIFFChunk(parent==null?0:parent.getType(), id, size, scan);
|
||||
|
||||
if (isDataChunk(chunk)) {
|
||||
byte[] data = new byte[(int) size];
|
||||
in.read(data, 0, (int) size);
|
||||
chunk.setData(data);
|
||||
visitor.visitChunk(parent, chunk);
|
||||
} else if (isPropertyChunk(chunk)) {
|
||||
byte[] data = new byte[(int) size];
|
||||
in.read(data, 0, (int) size);
|
||||
chunk.setData(data);
|
||||
parent.putPropertyChunk(chunk);
|
||||
} else if (isCollectionChunk(chunk)) {
|
||||
byte[] data = new byte[(int) size];
|
||||
in.read(data, 0, (int) size);
|
||||
chunk.setData(data);
|
||||
parent.addCollectionChunk(chunk);
|
||||
} else {
|
||||
in.skipFully((int) size);
|
||||
if (isStopChunks) {
|
||||
visitor.visitChunk(parent, chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This method is invoked when we encounter a parsing problem.
|
||||
* <pre>
|
||||
* LocalChunk ::= size { DataChunk | PropertyChunk | CollectionChunk }
|
||||
* DataChunk = PropertyChunk = CollectionChunk ::= { byte }...*size
|
||||
* </pre>
|
||||
*/
|
||||
private void parseGarbage(RIFFChunk parent, int id, long size, long scan)
|
||||
throws ParseException, AbortException, IOException {
|
||||
//long size = in.readULONG();
|
||||
//long scan = getScan(in);
|
||||
|
||||
RIFFChunk chunk = new RIFFChunk(parent.getType(), id, size, scan);
|
||||
|
||||
if (isDataChunk(chunk)) {
|
||||
byte[] data = new byte[(int) size];
|
||||
in.read(data, 0, (int) size);
|
||||
chunk.setData(data);
|
||||
visitor.visitChunk(parent, chunk);
|
||||
} else if (isPropertyChunk(chunk)) {
|
||||
byte[] data = new byte[(int) size];
|
||||
in.read(data, 0, (int) size);
|
||||
chunk.setData(data);
|
||||
parent.putPropertyChunk(chunk);
|
||||
} else if (isCollectionChunk(chunk)) {
|
||||
byte[] data = new byte[(int) size];
|
||||
in.read(data, 0, (int) size);
|
||||
chunk.setData(data);
|
||||
parent.addCollectionChunk(chunk);
|
||||
} else {
|
||||
in.skipFully((int) size);
|
||||
if (isStopChunk(chunk)) {
|
||||
visitor.visitChunk(parent, chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the ID of the chunk has been declared as a
|
||||
* data chunk.
|
||||
*
|
||||
* <p>Pre condition
|
||||
* <li> Data chunks must have been declared before the
|
||||
* interpretation has been started.
|
||||
* <li> This method will always return true when neither
|
||||
* data chunks, property chunks nor collection chunks
|
||||
* have been declared,
|
||||
*
|
||||
* @param chunk Chunk to be verified.
|
||||
* @return True when the parameter is a data chunk.
|
||||
*/
|
||||
protected boolean isDataChunk(RIFFChunk chunk) {
|
||||
if (dataChunks == null) {
|
||||
if (collectionChunks == null && propertyChunks == null && (stopChunkTypes==null||!stopChunkTypes.contains(chunk.getType()))) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return dataChunks.contains(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the ID of the chunk has been declared as
|
||||
* a group chunk.
|
||||
*
|
||||
* <p>Pre condition
|
||||
* <li> Group chunks must have been declared before the
|
||||
* interpretation has been started.
|
||||
* (Otherwise the response is always true).
|
||||
*
|
||||
* @param chunk Chunk to be verified.
|
||||
* @return True when the visitor is interested in this is a group chunk.
|
||||
*/
|
||||
protected boolean isGroupChunk(RIFFChunk chunk) {
|
||||
if (groupChunks == null) {
|
||||
return true;
|
||||
} else {
|
||||
return groupChunks.contains(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks wether the ID of the chunk has been declared as a
|
||||
* property chunk.
|
||||
*
|
||||
* <p>Pre condition
|
||||
* <li> Property chunks must have been declared before the
|
||||
* interpretation has been started.
|
||||
* <li> This method will always return false when neither
|
||||
* data chunks, property chunks nor collection chunks
|
||||
* have been declared,
|
||||
*/
|
||||
protected boolean isPropertyChunk(RIFFChunk chunk) {
|
||||
if (propertyChunks == null) {
|
||||
return false;
|
||||
} else {
|
||||
return propertyChunks.contains(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks wether the ID of the chunk has been declared as a
|
||||
* collection chunk.
|
||||
*
|
||||
* <p>Pre condition
|
||||
* <li> Collection chunks must have been declared before the
|
||||
* interpretation has been started.
|
||||
* <li> This method will always return true when neither
|
||||
* data chunks, property chunks nor collection chunks
|
||||
* have been declared,
|
||||
*
|
||||
* @param chunk Chunk to be verified.
|
||||
* @return True when the parameter is a collection chunk.
|
||||
*/
|
||||
protected boolean isCollectionChunk(RIFFChunk chunk) {
|
||||
if (collectionChunks == null) {
|
||||
return false;
|
||||
} else {
|
||||
return collectionChunks.contains(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares a data chunk.
|
||||
*
|
||||
* <p>Pre condition
|
||||
* <li> The chunk must not have already been declared as of a
|
||||
* different type.
|
||||
* <li> Declarations may not be done during interpretation
|
||||
* of an RIFFFileExpression.
|
||||
*
|
||||
* <p>Post condition
|
||||
* <li> Data chunk declared
|
||||
*
|
||||
* @param type
|
||||
* Type of the chunk. Must be formulated as a TypeID conforming
|
||||
* to the method #isFormType.
|
||||
* @param id
|
||||
* ID of the chunk. Must be formulated as a ChunkID conforming
|
||||
* to the method #isLocalChunkID.
|
||||
*/
|
||||
public void declareDataChunk(int type, int id) {
|
||||
RIFFChunk chunk = new RIFFChunk(type, id);
|
||||
if (dataChunks == null) {
|
||||
dataChunks = new HashSet<RIFFChunk>();
|
||||
}
|
||||
dataChunks.add(chunk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares a FORM group chunk.
|
||||
*
|
||||
* <p>Pre condition
|
||||
* <li> The chunk must not have already been declared as of a
|
||||
* different type.
|
||||
* <li> Declarations may not be done during interpretation
|
||||
* of an RIFFFileExpression.
|
||||
*
|
||||
* <p>Post condition
|
||||
* <li> Group chunk declared
|
||||
*
|
||||
* @param type
|
||||
* Type of the chunk. Must be formulated as a TypeID conforming
|
||||
* to the method #isFormType.
|
||||
* @param id
|
||||
* ID of the chunk. Must be formulated as a ChunkID conforming
|
||||
* to the method #isContentsType.
|
||||
*/
|
||||
public void declareGroupChunk(int type, int id) {
|
||||
RIFFChunk chunk = new RIFFChunk(type, id);
|
||||
if (groupChunks == null) {
|
||||
groupChunks = new HashSet<RIFFChunk>();
|
||||
}
|
||||
groupChunks.add(chunk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares a property chunk.
|
||||
*
|
||||
* <p>Pre condition
|
||||
* <li> The chunk must not have already been declared as of a
|
||||
* different type.
|
||||
* <li> Declarations may not be done during interpretation
|
||||
* of an RIFFFileExpression.
|
||||
*
|
||||
* <p>Post condition
|
||||
* <li> Group chunk declared
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
* Type of the chunk. Must be formulated as a TypeID conforming
|
||||
* to the method #isFormType.
|
||||
* @param id
|
||||
* ID of the chunk. Must be formulated as a ChunkID conforming
|
||||
* to the method #isLocalChunkID.
|
||||
*/
|
||||
public void declarePropertyChunk(int type, int id) {
|
||||
RIFFChunk chunk = new RIFFChunk(type, id);
|
||||
if (propertyChunks == null) {
|
||||
propertyChunks = new HashSet<RIFFChunk>();
|
||||
}
|
||||
propertyChunks.add(chunk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares a collection chunk.
|
||||
*
|
||||
* <p>Pre condition
|
||||
* <li> The chunk must not have already been declared as of a
|
||||
* different type.
|
||||
* <li> Declarations may not be done during interpretation
|
||||
* of an RIFFFileExpression.
|
||||
*
|
||||
* <p>Post condition
|
||||
* <li> Collection chunk declared
|
||||
*
|
||||
* @param type
|
||||
* Type of the chunk. Must be formulated as a TypeID conforming
|
||||
* to the method #isFormType.
|
||||
* @param id
|
||||
* ID of the chunk. Must be formulated as a ChunkID conforming
|
||||
* to the method #isLocalChunkID.
|
||||
*/
|
||||
public void declareCollectionChunk(int type, int id) {
|
||||
RIFFChunk chunk = new RIFFChunk(type, id);
|
||||
if (collectionChunks == null) {
|
||||
collectionChunks = new HashSet<RIFFChunk>();
|
||||
}
|
||||
collectionChunks.add(chunk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares a stop chunk.
|
||||
*
|
||||
* <p>Pre condition
|
||||
* <li> The chunk must not have already been declared as of a
|
||||
* different type.
|
||||
* <li> Declarations may not be done during interpretation
|
||||
* of an RIFFFileExpression.
|
||||
*
|
||||
* <p>Post condition
|
||||
* <li> Stop chunk declared
|
||||
*
|
||||
* @param type
|
||||
* Type of the chunk. Must be formulated as a TypeID conforming
|
||||
* to the method #isFormType.
|
||||
*/
|
||||
public void declareStopChunkType(int type) {
|
||||
if (stopChunkTypes == null) {
|
||||
stopChunkTypes = new HashSet<Integer>();
|
||||
}
|
||||
stopChunkTypes.add(type);
|
||||
}
|
||||
|
||||
/** Whether the parse should stop at all chunks.
|
||||
* <p>
|
||||
* The parser does not read the data body of stop chunks.
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
public void declareStopChunks() {
|
||||
isStopChunks = true;
|
||||
}
|
||||
|
||||
private boolean isStopChunk(RIFFChunk chunk) {
|
||||
return isStopChunks||stopChunkTypes!=null&&stopChunkTypes.contains(chunk.getType());
|
||||
}
|
||||
|
||||
/* ---- Class methods ---- */
|
||||
/**
|
||||
* Checks wether the argument represents a valid RIFF GroupID.
|
||||
*
|
||||
* <p>Validation
|
||||
* <ul>
|
||||
* <li> Group ID must be one of RIFF_ID, LIST_ID.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param id Chunk ID to be checked.
|
||||
* @return True when the chunk ID is a valid Group ID.
|
||||
*/
|
||||
public static boolean isGroupID(int id) {
|
||||
return id == LIST_ID || id == RIFF_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks wether the argument represents a valid RIFF Group Type.
|
||||
*
|
||||
* <p>Validation
|
||||
* <ul>
|
||||
* <li> Must be a valid ID.</li>
|
||||
* <li> Must not be a group ID.</li>
|
||||
* <li> Must not be a NULL_ID.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param id Chunk ID to be checked.
|
||||
* @return True when the chunk ID is a valid Group ID.
|
||||
*/
|
||||
public static boolean isGroupType(int id) {
|
||||
return isID(id) && !isGroupID(id) && id != NULL_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the argument represents a valid RIFF ID.
|
||||
*
|
||||
* <p>Validation
|
||||
* <li> Every byte of an ID must be in the range of 0x20..0x7e
|
||||
* <li> The id may not have leading spaces (unless the id is a NULL_ID).
|
||||
*
|
||||
* @param id Chunk ID to be checked.
|
||||
* @return True when the ID is a valid IFF chunk ID.
|
||||
*/
|
||||
public static boolean isID(int id) {
|
||||
int c0 = id >> 24;
|
||||
int c1 = (id >> 16) & 0xff;
|
||||
int c2 = (id >> 8) & 0xff;
|
||||
int c3 = id & 0xff;
|
||||
|
||||
return id == NULL_NUL_ID
|
||||
|| c0 >= 0x20 && c0 <= 0x7e
|
||||
&& c1 >= 0x20 && c1 <= 0x7e
|
||||
&& c2 >= 0x20 && c2 <= 0x7e
|
||||
&& c3 >= 0x20 && c3 <= 0x7e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the argument is a valid Local Chunk ID.
|
||||
*
|
||||
* <p>Validation
|
||||
* <ud>
|
||||
* <li> Must be valid ID.</li>
|
||||
* <li> Local Chunk IDs may not collide with GroupIDs.</id>
|
||||
* <li> Must not be a NULL_ID.</li>
|
||||
* </ud>
|
||||
*
|
||||
* @param id Chunk ID to be checked.
|
||||
* @return True when the chunk ID is a Local Chunk ID.
|
||||
*/
|
||||
public static boolean isLocalChunkID(int id) {
|
||||
if (isGroupID(id)) {
|
||||
return false;
|
||||
}
|
||||
return id != NULL_ID && isID(id);
|
||||
}
|
||||
private WeakHashMap<String, String> ids;
|
||||
|
||||
/**
|
||||
* Convert an integer IFF identifier to String.
|
||||
*
|
||||
* @param anInt ID to be converted.
|
||||
* @return String representation of the ID.
|
||||
*/
|
||||
public static String idToString(int anInt) {
|
||||
byte[] bytes = new byte[4];
|
||||
|
||||
bytes[0] = (byte) (anInt >>> 24);
|
||||
bytes[1] = (byte) (anInt >>> 16);
|
||||
bytes[2] = (byte) (anInt >>> 8);
|
||||
bytes[3] = (byte) (anInt >>> 0);
|
||||
|
||||
try {
|
||||
return new String(bytes, "ASCII");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new InternalError(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the first four letters of the
|
||||
* String into an IFF Identifier.
|
||||
*
|
||||
* @param aString String to be converted.
|
||||
* @return ID representation of the String.
|
||||
*/
|
||||
public static int stringToID(String aString) {
|
||||
byte[] bytes = aString.getBytes();
|
||||
|
||||
return ((int) bytes[0]) << 24
|
||||
| ((int) bytes[1]) << 16
|
||||
| ((int) bytes[2]) << 8
|
||||
| ((int) bytes[3]) << 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user