| /* |
| * Created on 28.11.2005 |
| */ |
| package org.eclipse.epp.installer.tools.packager; |
| |
| import java.io.BufferedOutputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.FilterOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| |
| /* |
| * Self-Extracting File Structure |
| * |
| * |---------------------------| |
| * | Executable Extractor | |
| * | | |
| * |---------------------------| |
| * | Files Data | |
| * | | |
| * |---------------------------| |
| * | SFX Header | |
| * | | |
| * |---------------------------| |
| * | Table Of Contents | |
| * | (TOC) | |
| * |---------------------------| |
| * | SFX Footer | |
| * | | |
| * |---------------------------| |
| * |
| * |
| * SFX Header: |
| * 12 bytes - SFX signature ( including format version ) |
| * 4 bytes - number of files |
| * |
| * TOC Entry: |
| * 4 bytes - file data offset |
| * 4 bytes - file data size |
| * 4 bytes - file flags |
| * 4 bytes - filename length (N) |
| * N bytes - filename |
| * |
| * SFX Footer: |
| * 4 bytes - SFX header offset |
| * 4 bytes - 32-bit complementary checksum |
| */ |
| |
| /** |
| * <p> |
| * Copyright (c) 2006, Instantiations, Inc.<br> |
| * All Rights Reserved |
| * |
| * @author Max Stepanov (max@xored.com) |
| */ |
| public class NativePackager { |
| |
| public static final String FORMAT_VERSION = "0.1"; |
| private static final String SIGNATURE = "!SFX.PKG-"; |
| |
| private static final int FLAGS_EXTRACT = 0x0001; |
| |
| private class Entry { |
| File file; |
| String name; |
| int size; |
| int offset; |
| int flags; |
| |
| public Entry( File file) { |
| this(file,0); |
| } |
| |
| public Entry( File file, int flags ) { |
| this(file,file.getName(),flags); |
| } |
| |
| public Entry( File file, String name, int flags ) { |
| this.file = file; |
| this.name = name; |
| this.flags = flags; |
| } |
| } |
| |
| private class ChecksumOutputStream extends FilterOutputStream { |
| private int checksum = 0; |
| private int index = 0; |
| public ChecksumOutputStream(OutputStream out) { |
| super(out); |
| } |
| public void write(byte[] b, int off, int len) throws IOException { |
| super.write(b, off, len); |
| calc(b, off, len); |
| } |
| private void calc(byte[] b, int off, int len) { |
| while( index != 0 && len > 0 ) { |
| calc(b[off++]); |
| --len; |
| } |
| for( ; len >= 4 ; len-=4 ) { |
| checksum += ((int)b[off++]) << 24; |
| checksum += (((int)b[off++]) << 16) & 0xFF0000; |
| checksum += (((int)b[off++]) << 8) & 0xFF00; |
| checksum += ((int)b[off++]) & 0xFF; |
| } |
| while( len > 0 ) { |
| calc(b[off++]); |
| --len; |
| } |
| } |
| private void calc(byte b) { |
| switch ( index ) { |
| case 0: |
| checksum += ((int)b) << 24; |
| index = 1; |
| break; |
| case 1: |
| checksum += (((int)b) << 16) & 0xFF0000; |
| index = 2; |
| break; |
| case 2: |
| checksum += (((int)b) << 8) & 0xFF00; |
| index = 3; |
| break; |
| case 3: |
| checksum += ((int)b) & 0xFF; |
| index = 0; |
| break; |
| } |
| } |
| public int getChecksum() { |
| return checksum; |
| } |
| public int getIndex() { |
| return index; |
| } |
| } |
| |
| private ArrayList entries = new ArrayList(); |
| |
| public NativePackager( File container ) { |
| /* entry 0 is a fake entry for container */ |
| entries.add( new Entry(container)); |
| } |
| |
| public void addFile( File file ) { |
| if ( file == null ) |
| throw new IllegalArgumentException(); |
| entries.add( new Entry(file)); |
| } |
| |
| public void addFile( File file, boolean extract ) { |
| if ( file == null ) |
| throw new IllegalArgumentException(); |
| entries.add( new Entry(file,extract ? FLAGS_EXTRACT : 0)); |
| } |
| |
| public void build( File dest ) throws IOException { |
| if ( dest == null ) |
| throw new IllegalArgumentException(); |
| if ( dest.exists() ) |
| dest.delete(); |
| ChecksumOutputStream out = new ChecksumOutputStream(new BufferedOutputStream( new FileOutputStream(dest))); |
| |
| /* write data */ |
| int n; |
| int offset = 0; |
| byte[] buffer = new byte[1024]; |
| for( Iterator i = entries.iterator(); i.hasNext(); ) { |
| Entry entry = (Entry) i.next(); |
| InputStream in = new FileInputStream(entry.file); |
| entry.offset = offset; |
| while( (n = in.read(buffer)) > 0 ) { |
| out.write(buffer,0,n); |
| offset += n; |
| } |
| entry.size = offset - entry.offset; |
| in.close(); |
| } |
| |
| /* remove fake entry 0 */ |
| entries.remove(0); |
| |
| /* write SFX Header */ |
| out.write((SIGNATURE+FORMAT_VERSION).getBytes()); |
| out.write(toArray32(entries.size())); |
| |
| /* write TOC */ |
| for( Iterator i = entries.iterator(); i.hasNext(); ) { |
| Entry entry = (Entry) i.next(); |
| out.write(toArray32(entry.offset)); |
| out.write(toArray32(entry.size)); |
| out.write(toArray32(entry.flags)); |
| byte[] filename = entry.name.getBytes(); |
| out.write(toArray32(filename.length)); |
| out.write(filename); |
| } |
| |
| /* align to 4 bytes */ |
| if ( out.getIndex() != 0 ) |
| out.write( new byte[4-out.getIndex()]); |
| |
| /* write SFX Footer */ |
| out.write(toArray32(offset)); |
| out.write(toArray32(-1-out.getChecksum())); |
| out.close(); |
| } |
| |
| private static byte[] toArray32(int value) { |
| byte[] data = new byte[4]; |
| data[0] = (byte)( value >>> 24 ); |
| data[1] = (byte)( value >>> 16 ); |
| data[2] = (byte)( value >>> 8 ); |
| data[3] = (byte)( value ); |
| return data; |
| } |
| |
| public static void main( String args[] ) { |
| if ( args.length < 3 ) { |
| System.out.println("Usage:\nNativePackager dest src [-x]file1 [-x]file2 ..."); |
| return; |
| } |
| NativePackager p = new NativePackager( new File(args[1]) ); |
| for( int i = 2; i < args.length; ++i ) { |
| String file = args[i]; |
| if ( file.startsWith("-x") ) { |
| p.addFile( new File(file.substring(2)), true ); |
| } else { |
| p.addFile( new File(file) ); |
| } |
| } |
| try { |
| p.build( new File(args[0]) ); |
| System.out.println("Packaging complete."); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| // Exit with non-zero status so that the build will fail |
| System.exit(1); |
| } |
| } |
| } |