/* * 11/19/04 1.0 moved to LGPL. * 02/23/99 JavaConversion by E.B * Don Cross, April 1993. * RIFF file format classes. * See Chapter 8 of "Multimedia Programmer's Reference" in * the Microsoft Windows SDK. * *----------------------------------------------------------------------- * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *---------------------------------------------------------------------- */ package audio.javazoom.jl.converter; import java.io.IOException; import java.io.RandomAccessFile; /** * Class to manage RIFF files */ public class RiffFile { class RiffChunkHeader { public int ckID = 0; // Four-character chunk ID public int ckSize = 0; // Length of data in chunk public RiffChunkHeader() {} } // DDCRET public static final int DDC_SUCCESS = 0; // The operation succeded public static final int DDC_FAILURE = 1; // The operation failed for unspecified reasons public static final int DDC_OUT_OF_MEMORY = 2; // Operation failed due to running out of memory public static final int DDC_FILE_ERROR = 3; // Operation encountered file I/O error public static final int DDC_INVALID_CALL = 4; // Operation was called with invalid parameters public static final int DDC_USER_ABORT = 5; // Operation was aborted by the user public static final int DDC_INVALID_FILE = 6; // File format does not match // RiffFileMode public static final int RFM_UNKNOWN = 0; // undefined type (can use to mean "N/A" or "not open") public static final int RFM_WRITE = 1; // open for write public static final int RFM_READ = 2; // open for read private RiffChunkHeader riff_header; // header for whole file protected int fmode; // current file I/O mode protected RandomAccessFile file; // I/O stream to use /** * Dummy Constructor */ public RiffFile() { file = null; fmode = RFM_UNKNOWN; riff_header = new RiffChunkHeader(); riff_header.ckID = FourCC("RIFF"); riff_header.ckSize = 0; } /** * Return File Mode. */ public int CurrentFileMode() {return fmode;} /** * Open a RIFF file. */ public int Open(String Filename, int NewMode) { int retcode = DDC_SUCCESS; if ( fmode != RFM_UNKNOWN ) { retcode = Close(); } if ( retcode == DDC_SUCCESS ) { switch ( NewMode ) { case RFM_WRITE: try { file = new RandomAccessFile(Filename,"rw"); try { // Write the RIFF header... // We will have to come back later and patch it! byte[] br = new byte[8]; br[0] = (byte) ((riff_header.ckID >>> 24) & 0x000000FF); br[1] = (byte) ((riff_header.ckID >>> 16) & 0x000000FF); br[2] = (byte) ((riff_header.ckID >>> 8) & 0x000000FF); br[3] = (byte) (riff_header.ckID & 0x000000FF); byte br4 = (byte) ((riff_header.ckSize >>> 24)& 0x000000FF); byte br5 = (byte) ((riff_header.ckSize >>> 16)& 0x000000FF); byte br6 = (byte) ((riff_header.ckSize >>> 8)& 0x000000FF); byte br7 = (byte) (riff_header.ckSize & 0x000000FF); br[4] = br7; br[5] = br6; br[6] = br5; br[7] = br4; file.write(br,0,8); fmode = RFM_WRITE; } catch (IOException ioe) { file.close(); fmode = RFM_UNKNOWN; } } catch (IOException ioe) { fmode = RFM_UNKNOWN; retcode = DDC_FILE_ERROR; } break; case RFM_READ: try { file = new RandomAccessFile(Filename,"r"); try { // Try to read the RIFF header... byte[] br = new byte[8]; file.read(br,0,8); fmode = RFM_READ; riff_header.ckID = ((br[0]<<24)& 0xFF000000) | ((br[1]<<16)&0x00FF0000) | ((br[2]<<8)&0x0000FF00) | (br[3]&0x000000FF); riff_header.ckSize = ((br[4]<<24)& 0xFF000000) | ((br[5]<<16)&0x00FF0000) | ((br[6]<<8)&0x0000FF00) | (br[7]&0x000000FF); } catch (IOException ioe) { file.close(); fmode = RFM_UNKNOWN; } } catch (IOException ioe) { fmode = RFM_UNKNOWN; retcode = DDC_FILE_ERROR; } break; default: retcode = DDC_INVALID_CALL; } } return retcode; } /** * Write NumBytes data. */ public int Write(byte[] Data, int NumBytes ) { if ( fmode != RFM_WRITE ) { return DDC_INVALID_CALL; } try { file.write(Data,0,NumBytes); fmode = RFM_WRITE; } catch (IOException ioe) { return DDC_FILE_ERROR; } riff_header.ckSize += NumBytes; return DDC_SUCCESS; } /** * Write NumBytes data. */ public int Write(short[] Data, int NumBytes ) { byte[] theData = new byte[NumBytes]; int yc = 0; for (int y = 0;y<NumBytes;y=y+2) { theData[y] = (byte) (Data[yc] & 0x00FF); theData[y+1] =(byte) ((Data[yc++] >>> 8) & 0x00FF); } if ( fmode != RFM_WRITE ) { return DDC_INVALID_CALL; } try { file.write(theData,0,NumBytes); fmode = RFM_WRITE; } catch (IOException ioe) { return DDC_FILE_ERROR; } riff_header.ckSize += NumBytes; return DDC_SUCCESS; } /** * Write NumBytes data. */ public int Write(RiffChunkHeader Triff_header, int NumBytes ) { byte[] br = new byte[8]; br[0] = (byte) ((Triff_header.ckID >>> 24) & 0x000000FF); br[1] = (byte) ((Triff_header.ckID >>> 16) & 0x000000FF); br[2] = (byte) ((Triff_header.ckID >>> 8) & 0x000000FF); br[3] = (byte) (Triff_header.ckID & 0x000000FF); byte br4 = (byte) ((Triff_header.ckSize >>> 24)& 0x000000FF); byte br5 = (byte) ((Triff_header.ckSize >>> 16)& 0x000000FF); byte br6 = (byte) ((Triff_header.ckSize >>> 8)& 0x000000FF); byte br7 = (byte) (Triff_header.ckSize & 0x000000FF); br[4] = br7; br[5] = br6; br[6] = br5; br[7] = br4; if ( fmode != RFM_WRITE ) { return DDC_INVALID_CALL; } try { file.write(br,0,NumBytes); fmode = RFM_WRITE; } catch (IOException ioe) { return DDC_FILE_ERROR; } riff_header.ckSize += NumBytes; return DDC_SUCCESS; } /** * Write NumBytes data. */ public int Write(short Data, int NumBytes ) { short theData = (short) ( ((Data>>>8)&0x00FF) | ((Data<<8)&0xFF00) ); if ( fmode != RFM_WRITE ) { return DDC_INVALID_CALL; } try { file.writeShort(theData); fmode = RFM_WRITE; } catch (IOException ioe) { return DDC_FILE_ERROR; } riff_header.ckSize += NumBytes; return DDC_SUCCESS; } /** * Write NumBytes data. */ public int Write(int Data, int NumBytes ) { short theDataL = (short) ((Data>>>16)&0x0000FFFF); short theDataR = (short) (Data&0x0000FFFF); short theDataLI = (short) ( ((theDataL>>>8)&0x00FF) | ((theDataL<<8)&0xFF00) ); short theDataRI = (short) ( ((theDataR>>>8)&0x00FF) | ((theDataR<<8)&0xFF00) ); int theData = ((theDataRI<<16)&0xFFFF0000) | (theDataLI&0x0000FFFF); if ( fmode != RFM_WRITE ) { return DDC_INVALID_CALL; } try { file.writeInt(theData); fmode = RFM_WRITE; } catch (IOException ioe) { return DDC_FILE_ERROR; } riff_header.ckSize += NumBytes; return DDC_SUCCESS; } /** * Read NumBytes data. */ public int Read (byte[] Data, int NumBytes) { int retcode = DDC_SUCCESS; try { file.read(Data,0,NumBytes); } catch (IOException ioe) { retcode = DDC_FILE_ERROR; } return retcode; } /** * Expect NumBytes data. */ public int Expect(String Data, int NumBytes ) { byte target = 0; int cnt = 0; try { while ((NumBytes--) != 0) { target = file.readByte(); if (target != Data.charAt(cnt++)) return DDC_FILE_ERROR; } } catch (IOException ioe) { return DDC_FILE_ERROR; } return DDC_SUCCESS; } /** * Close Riff File. * Length is written too. */ public int Close() { int retcode = DDC_SUCCESS; switch ( fmode ) { case RFM_WRITE: try { file.seek(0); try { byte[] br = new byte[8]; br[0] = (byte) ((riff_header.ckID >>> 24) & 0x000000FF); br[1] = (byte) ((riff_header.ckID >>> 16) & 0x000000FF); br[2] = (byte) ((riff_header.ckID >>> 8) & 0x000000FF); br[3] = (byte) (riff_header.ckID & 0x000000FF); br[7] = (byte) ((riff_header.ckSize >>> 24)& 0x000000FF); br[6] = (byte) ((riff_header.ckSize >>> 16)& 0x000000FF); br[5] = (byte) ((riff_header.ckSize >>> 8)& 0x000000FF); br[4] = (byte) (riff_header.ckSize & 0x000000FF); file.write(br,0,8); file.close(); } catch (IOException ioe) { retcode = DDC_FILE_ERROR; } } catch (IOException ioe) { retcode = DDC_FILE_ERROR; } break; case RFM_READ: try { file.close(); } catch (IOException ioe) { retcode = DDC_FILE_ERROR; } break; } file = null; fmode = RFM_UNKNOWN; return retcode; } /** * Return File Position. */ public long CurrentFilePosition() { long position; try { position = file.getFilePointer(); } catch (IOException ioe) { position = -1; } return position; } /** * Write Data to specified offset. */ public int Backpatch (long FileOffset, RiffChunkHeader Data, int NumBytes ) { if (file == null) { return DDC_INVALID_CALL; } try { file.seek(FileOffset); } catch (IOException ioe) { return DDC_FILE_ERROR; } return Write ( Data, NumBytes ); } public int Backpatch (long FileOffset, byte[] Data, int NumBytes ) { if (file == null) { return DDC_INVALID_CALL; } try { file.seek(FileOffset); } catch (IOException ioe) { return DDC_FILE_ERROR; } return Write ( Data, NumBytes ); } /** * Seek in the File. */ protected int Seek(long offset) { int rc; try { file.seek(offset); rc = DDC_SUCCESS; } catch (IOException ioe) { rc = DDC_FILE_ERROR; } return rc; } /** * Error Messages. */ private String DDCRET_String(int retcode) { switch ( retcode ) { case DDC_SUCCESS: return "DDC_SUCCESS"; case DDC_FAILURE: return "DDC_FAILURE"; case DDC_OUT_OF_MEMORY: return "DDC_OUT_OF_MEMORY"; case DDC_FILE_ERROR: return "DDC_FILE_ERROR"; case DDC_INVALID_CALL: return "DDC_INVALID_CALL"; case DDC_USER_ABORT: return "DDC_USER_ABORT"; case DDC_INVALID_FILE: return "DDC_INVALID_FILE"; } return "Unknown Error"; } /** * Fill the header. */ public static int FourCC(String ChunkName) { byte[] p = {0x20,0x20,0x20,0x20}; ChunkName.getBytes(0,4,p,0); int ret = (((p[0] << 24)& 0xFF000000) | ((p[1] << 16)&0x00FF0000) | ((p[2] << 8)&0x0000FF00) | (p[3]&0x000000FF)); return ret; } }