blob: 1d17a725d50e14bbedf0ba7e930939e70988d44e [file] [log] [blame]
/*
* Copyright (c) 2005, Joe Desbonnet, (jdesbonnet@gmail.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY <copyright holder> ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/package ie.wombat.jbdiff;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
/**
* Java Binary patcher (based on bspatch by Colin Percival)
*
* @author Joe Desbonnet, joe@galway.net
*/
public class JBPatch {
// JBPatch extensions by Stefan.Liebig@compeople.de:
//
// - uses GZIP compressor to compress ALL of the blocks (ctrl,diff,extra).
// - added an interface that allows using of JBPatch with streams and byte
// arrays
private static final String VERSION = "jbdiff-0.1.0";
/**
* Run JBPatch from the command line. Params: oldfile newfile patchfile.
* newfile will be created.
*
* @param arg
* @throws IOException
*/
public static void main(String[] arg) throws IOException {
if (arg.length != 3) {
System.err
.println("usage example: java -Xmx200m ie.wombat.jbdiff.JBPatch oldfile newfile patchfile");
}
File oldFile = new File(arg[0]);
File newFile = new File(arg[1]);
File diffFile = new File(arg[2]);
bspatch(oldFile, newFile, diffFile);
}
/**
* @param oldFile
* @param newFile
* @param diffFile
* @throws IOException
*/
public static void bspatch(File oldFile, File newFile, File diffFile)
throws IOException {
InputStream oldInputStream = new BufferedInputStream(
new FileInputStream(oldFile));
byte[] diffBytes = new byte[(int) diffFile.length()];
InputStream diffInputStream = new FileInputStream(diffFile);
Util.readFromStream(diffInputStream, diffBytes, 0, diffBytes.length);
byte[] newBytes = bspatch(oldInputStream, (int) oldFile.length(),
diffBytes);
OutputStream newOutputStream = new FileOutputStream(newFile);
newOutputStream.write(newBytes);
newOutputStream.close();
}
/**
* @param oldInputStream
* @param diffInputStream
* @return
*/
public static byte[] bspatch(InputStream oldInputStream, int oldsize,
byte[] diffBytes) throws IOException {
/*
* Read in old file (file to be patched) to oldBuf
*/
// int oldsize = (int) oldFile.length();
// byte[] oldBuf = new byte[oldsize + 1];
byte[] oldBuf = new byte[oldsize];
// InputStream oldIn = new FileInputStream( oldFile );
Util.readFromStream(oldInputStream, oldBuf, 0, oldsize);
oldInputStream.close();
// oldIn.close();
return JBPatch.bspatch(oldBuf, oldsize, diffBytes);
}
/**
* @param oldBuf
* @param oldsize
* @param diffBytes
* @return
* @throws IOException
*/
public static byte[] bspatch(byte[] oldBuf, int oldsize, byte[] diffBytes)
throws IOException {
return bspatch(oldBuf, oldsize, diffBytes, diffBytes.length);
}
/**
* @param oldBuf
* @param oldsize
* @param diffBuf
* @param diffSize
* @return
* @throws IOException
*/
public static byte[] bspatch(byte[] oldBuf, int oldsize, byte[] diffBuf,
int diffSize) throws IOException {
DataInputStream diffIn = new DataInputStream(new ByteArrayInputStream(
diffBuf, 0, diffSize));
// skip headerMagic at header offset 0 (length 8 bytes)
diffIn.skip(8);
// ctrlBlockLen after bzip2 compression at heater offset 8 (length 8
// bytes)
long ctrlBlockLen = diffIn.readLong();
// diffBlockLen after bzip2 compression at header offset 16 (length 8
// bytes)
long diffBlockLen = diffIn.readLong();
// size of new file at header offset 24 (length 8 bytes)
int newsize = (int) diffIn.readLong();
// System.err.println( "newsize=" + newsize );
// System.err.println( "ctrlBlockLen=" + ctrlBlockLen );
// System.err.println( "diffBlockLen=" + diffBlockLen );
// System.err.println( "newsize=" + newsize );
InputStream in;
in = new ByteArrayInputStream(diffBuf, 0, diffSize);
in.skip(Util.HEADER_SIZE);
DataInputStream ctrlBlockIn = new DataInputStream(new GZIPInputStream(
in));
in = new ByteArrayInputStream(diffBuf, 0, diffSize);
in.skip(ctrlBlockLen + Util.HEADER_SIZE);
InputStream diffBlockIn = new GZIPInputStream(in);
in = new ByteArrayInputStream(diffBuf, 0, diffSize);
in.skip(diffBlockLen + ctrlBlockLen + Util.HEADER_SIZE);
InputStream extraBlockIn = new GZIPInputStream(in);
// byte[] newBuf = new byte[newsize + 1];
byte[] newBuf = new byte[newsize];
int oldpos = 0;
int newpos = 0;
int[] ctrl = new int[3];
// int nbytes;
while (newpos < newsize) {
for (int i = 0; i <= 2; i++) {
// ctrl[i] = diffIn.readInt();
ctrl[i] = ctrlBlockIn.readInt();
// System.err.println (" ctrl[" + i + "]=" + ctrl[i]);
}
if (newpos + ctrl[0] > newsize) {
throw new IOException("Corrupt patch.");
}
/*
* Read ctrl[0] bytes from diffBlock stream
*/
Util.readFromStream(diffBlockIn, newBuf, newpos, ctrl[0]);
for (int i = 0; i < ctrl[0]; i++) {
if ((oldpos + i >= 0) && (oldpos + i < oldsize)) {
newBuf[newpos + i] += oldBuf[oldpos + i];
}
}
newpos += ctrl[0];
oldpos += ctrl[0];
if (newpos + ctrl[1] > newsize) {
throw new IOException("Corrupt patch.");
}
Util.readFromStream(extraBlockIn, newBuf, newpos, ctrl[1]);
newpos += ctrl[1];
oldpos += ctrl[2];
}
// TODO: Check if at end of ctrlIn
// TODO: Check if at the end of diffIn
// TODO: Check if at the end of extraIn
// This check is not needed since the byte array has been allocated with
// this constraint!
// if ( newBuf.length - 1 != newsize ) {
// throw new IOException( "Corrupt patch." );
// }
ctrlBlockIn.close();
diffBlockIn.close();
extraBlockIn.close();
diffIn.close();
return newBuf;
// OutputStream out = new FileOutputStream( newFile );
// out.write( newBuf, 0, newBuf.length - 1 );
// out.close();
}
}