/*
* 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 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;
}
}