From 124f869faae3a0f75a3825e6a8e195c17f3c626a Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 16 Oct 2014 23:34:20 +0400 Subject: initial for idea --- .../src/main/java/com/github/junrar/Archive.java | 583 +++++++++++++++++++++ 1 file changed, 583 insertions(+) create mode 100644 org.fox.ttcomics/src/main/java/com/github/junrar/Archive.java (limited to 'org.fox.ttcomics/src/main/java/com/github/junrar/Archive.java') diff --git a/org.fox.ttcomics/src/main/java/com/github/junrar/Archive.java b/org.fox.ttcomics/src/main/java/com/github/junrar/Archive.java new file mode 100644 index 0000000..23ca39b --- /dev/null +++ b/org.fox.ttcomics/src/main/java/com/github/junrar/Archive.java @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved. + * Original author: Edmund Wagner + * Creation date: 22.05.2007 + * + * Source: $HeadURL$ + * Last changed: $LastChangedDate$ + * + * the unrar licence applies to all junrar source and binary distributions + * you are not allowed to use this source to re-create the RAR compression + * algorithm + * + * Here some html entities which can be used for escaping javadoc tags: + * "&": "&" or "&" + * "<": "<" or "<" + * ">": ">" or ">" + * "@": "@" + */ +package com.github.junrar; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.github.junrar.exception.RarException; +import com.github.junrar.exception.RarException.RarExceptionType; +import com.github.junrar.impl.FileVolumeManager; +import com.github.junrar.io.IReadOnlyAccess; +import com.github.junrar.rarfile.AVHeader; +import com.github.junrar.rarfile.BaseBlock; +import com.github.junrar.rarfile.BlockHeader; +import com.github.junrar.rarfile.CommentHeader; +import com.github.junrar.rarfile.EAHeader; +import com.github.junrar.rarfile.EndArcHeader; +import com.github.junrar.rarfile.FileHeader; +import com.github.junrar.rarfile.MacInfoHeader; +import com.github.junrar.rarfile.MainHeader; +import com.github.junrar.rarfile.MarkHeader; +import com.github.junrar.rarfile.ProtectHeader; +import com.github.junrar.rarfile.SignHeader; +import com.github.junrar.rarfile.SubBlockHeader; +import com.github.junrar.rarfile.UnixOwnersHeader; +import com.github.junrar.rarfile.UnrarHeadertype; +import com.github.junrar.unpack.ComprDataIO; +import com.github.junrar.unpack.Unpack; + + +/** + * The Main Rar Class; represents a rar Archive + * + * @author $LastChangedBy$ + * @version $LastChangedRevision$ + */ +public class Archive implements Closeable { + private static Logger logger = Logger.getLogger(Archive.class.getName()); + + private IReadOnlyAccess rof; + + private final UnrarCallback unrarCallback; + + private final ComprDataIO dataIO; + + private final List headers = new ArrayList(); + + private MarkHeader markHead = null; + + private MainHeader newMhd = null; + + private Unpack unpack; + + private int currentHeaderIndex; + + /** Size of packed data in current file. */ + private long totalPackedSize = 0L; + + /** Number of bytes of compressed data read from current file. */ + private long totalPackedRead = 0L; + + private VolumeManager volumeManager; + private Volume volume; + + public Archive(VolumeManager volumeManager) throws RarException, + IOException { + this(volumeManager, null); + } + + /** + * create a new archive object using the given {@link VolumeManager} + * + * @param volumeManager + * the the {@link VolumeManager} that will provide volume stream + * data + * @throws RarException + */ + public Archive(VolumeManager volumeManager, UnrarCallback unrarCallback) + throws RarException, IOException { + this.volumeManager = volumeManager; + this.unrarCallback = unrarCallback; + + setVolume(this.volumeManager.nextArchive(this, null)); + dataIO = new ComprDataIO(this); + } + + public Archive(File firstVolume) throws RarException, IOException { + this(new FileVolumeManager(firstVolume), null); + } + + public Archive(File firstVolume, UnrarCallback unrarCallback) + throws RarException, IOException { + this(new FileVolumeManager(firstVolume), unrarCallback); + } + + // public File getFile() { + // return file; + // } + // + // void setFile(File file) throws IOException { + // this.file = file; + // setFile(new ReadOnlyAccessFile(file), file.length()); + // } + + private void setFile(IReadOnlyAccess file, long length) throws IOException { + totalPackedSize = 0L; + totalPackedRead = 0L; + close(); + rof = file; + try { + readHeaders(length); + } catch (Exception e) { + logger.log(Level.WARNING, + "exception in archive constructor maybe file is encrypted " + + "or currupt", e); + // ignore exceptions to allow exraction of working files in + // corrupt archive + } + // Calculate size of packed data + for (BaseBlock block : headers) { + if (block.getHeaderType() == UnrarHeadertype.FileHeader) { + totalPackedSize += ((FileHeader) block).getFullPackSize(); + } + } + if (unrarCallback != null) { + unrarCallback.volumeProgressChanged(totalPackedRead, + totalPackedSize); + } + } + + public void bytesReadRead(int count) { + if (count > 0) { + totalPackedRead += count; + if (unrarCallback != null) { + unrarCallback.volumeProgressChanged(totalPackedRead, + totalPackedSize); + } + } + } + + public IReadOnlyAccess getRof() { + return rof; + } + + /** + * @return returns all file headers of the archive + */ + public List getFileHeaders() { + List list = new ArrayList(); + for (BaseBlock block : headers) { + if (block.getHeaderType().equals(UnrarHeadertype.FileHeader)) { + list.add((FileHeader) block); + } + } + return list; + } + + public FileHeader nextFileHeader() { + int n = headers.size(); + while (currentHeaderIndex < n) { + BaseBlock block = headers.get(currentHeaderIndex++); + if (block.getHeaderType() == UnrarHeadertype.FileHeader) { + return (FileHeader) block; + } + } + return null; + } + + public UnrarCallback getUnrarCallback() { + return unrarCallback; + } + + /** + * + * @return whether the archive is encrypted + */ + public boolean isEncrypted() { + if (newMhd != null) { + return newMhd.isEncrypted(); + } else { + throw new NullPointerException("mainheader is null"); + } + } + + /** + * Read the headers of the archive + * + * @param fileLength + * Length of file. + * @throws RarException + */ + private void readHeaders(long fileLength) throws IOException, RarException { + markHead = null; + newMhd = null; + headers.clear(); + currentHeaderIndex = 0; + int toRead = 0; + + while (true) { + int size = 0; + long newpos = 0; + byte[] baseBlockBuffer = new byte[BaseBlock.BaseBlockSize]; + + long position = rof.getPosition(); + + // Weird, but is trying to read beyond the end of the file + if (position >= fileLength) { + break; + } + + // logger.info("\n--------reading header--------"); + size = rof.readFully(baseBlockBuffer, BaseBlock.BaseBlockSize); + if (size == 0) { + break; + } + BaseBlock block = new BaseBlock(baseBlockBuffer); + + block.setPositionInFile(position); + + switch (block.getHeaderType()) { + + case MarkHeader: + markHead = new MarkHeader(block); + if (!markHead.isSignature()) { + throw new RarException( + RarException.RarExceptionType.badRarArchive); + } + headers.add(markHead); + // markHead.print(); + break; + + case MainHeader: + toRead = block.hasEncryptVersion() ? MainHeader.mainHeaderSizeWithEnc + : MainHeader.mainHeaderSize; + byte[] mainbuff = new byte[toRead]; + rof.readFully(mainbuff, toRead); + MainHeader mainhead = new MainHeader(block, mainbuff); + headers.add(mainhead); + this.newMhd = mainhead; + if (newMhd.isEncrypted()) { + throw new RarException( + RarExceptionType.rarEncryptedException); + } + // mainhead.print(); + break; + + case SignHeader: + toRead = SignHeader.signHeaderSize; + byte[] signBuff = new byte[toRead]; + rof.readFully(signBuff, toRead); + SignHeader signHead = new SignHeader(block, signBuff); + headers.add(signHead); + // logger.info("HeaderType: SignHeader"); + + break; + + case AvHeader: + toRead = AVHeader.avHeaderSize; + byte[] avBuff = new byte[toRead]; + rof.readFully(avBuff, toRead); + AVHeader avHead = new AVHeader(block, avBuff); + headers.add(avHead); + // logger.info("headertype: AVHeader"); + break; + + case CommHeader: + toRead = CommentHeader.commentHeaderSize; + byte[] commBuff = new byte[toRead]; + rof.readFully(commBuff, toRead); + CommentHeader commHead = new CommentHeader(block, commBuff); + headers.add(commHead); + // logger.info("method: "+commHead.getUnpMethod()+"; 0x"+ + // Integer.toHexString(commHead.getUnpMethod())); + newpos = commHead.getPositionInFile() + + commHead.getHeaderSize(); + rof.setPosition(newpos); + + break; + case EndArcHeader: + + toRead = 0; + if (block.hasArchiveDataCRC()) { + toRead += EndArcHeader.endArcArchiveDataCrcSize; + } + if (block.hasVolumeNumber()) { + toRead += EndArcHeader.endArcVolumeNumberSize; + } + EndArcHeader endArcHead; + if (toRead > 0) { + byte[] endArchBuff = new byte[toRead]; + rof.readFully(endArchBuff, toRead); + endArcHead = new EndArcHeader(block, endArchBuff); + // logger.info("HeaderType: endarch\ndatacrc:"+ + // endArcHead.getArchiveDataCRC()); + } else { + // logger.info("HeaderType: endarch - no Data"); + endArcHead = new EndArcHeader(block, null); + } + headers.add(endArcHead); + // logger.info("\n--------end header--------"); + return; + + default: + byte[] blockHeaderBuffer = new byte[BlockHeader.blockHeaderSize]; + rof.readFully(blockHeaderBuffer, BlockHeader.blockHeaderSize); + BlockHeader blockHead = new BlockHeader(block, + blockHeaderBuffer); + + switch (blockHead.getHeaderType()) { + case NewSubHeader: + case FileHeader: + toRead = blockHead.getHeaderSize() + - BlockHeader.BaseBlockSize + - BlockHeader.blockHeaderSize; + byte[] fileHeaderBuffer = new byte[toRead]; + rof.readFully(fileHeaderBuffer, toRead); + + FileHeader fh = new FileHeader(blockHead, fileHeaderBuffer); + headers.add(fh); + newpos = fh.getPositionInFile() + fh.getHeaderSize() + + fh.getFullPackSize(); + rof.setPosition(newpos); + break; + + case ProtectHeader: + toRead = blockHead.getHeaderSize() + - BlockHeader.BaseBlockSize + - BlockHeader.blockHeaderSize; + byte[] protectHeaderBuffer = new byte[toRead]; + rof.readFully(protectHeaderBuffer, toRead); + ProtectHeader ph = new ProtectHeader(blockHead, + protectHeaderBuffer); + + newpos = ph.getPositionInFile() + ph.getHeaderSize() + + ph.getDataSize(); + rof.setPosition(newpos); + break; + + case SubHeader: { + byte[] subHeadbuffer = new byte[SubBlockHeader.SubBlockHeaderSize]; + rof.readFully(subHeadbuffer, + SubBlockHeader.SubBlockHeaderSize); + SubBlockHeader subHead = new SubBlockHeader(blockHead, + subHeadbuffer); + subHead.print(); + switch (subHead.getSubType()) { + case MAC_HEAD: { + byte[] macHeaderbuffer = new byte[MacInfoHeader.MacInfoHeaderSize]; + rof.readFully(macHeaderbuffer, + MacInfoHeader.MacInfoHeaderSize); + MacInfoHeader macHeader = new MacInfoHeader(subHead, + macHeaderbuffer); + macHeader.print(); + headers.add(macHeader); + + break; + } + // TODO implement other subheaders + case BEEA_HEAD: + break; + case EA_HEAD: { + byte[] eaHeaderBuffer = new byte[EAHeader.EAHeaderSize]; + rof.readFully(eaHeaderBuffer, EAHeader.EAHeaderSize); + EAHeader eaHeader = new EAHeader(subHead, + eaHeaderBuffer); + eaHeader.print(); + headers.add(eaHeader); + + break; + } + case NTACL_HEAD: + break; + case STREAM_HEAD: + break; + case UO_HEAD: + toRead = subHead.getHeaderSize(); + toRead -= BaseBlock.BaseBlockSize; + toRead -= BlockHeader.blockHeaderSize; + toRead -= SubBlockHeader.SubBlockHeaderSize; + byte[] uoHeaderBuffer = new byte[toRead]; + rof.readFully(uoHeaderBuffer, toRead); + UnixOwnersHeader uoHeader = new UnixOwnersHeader( + subHead, uoHeaderBuffer); + uoHeader.print(); + headers.add(uoHeader); + break; + default: + break; + } + + break; + } + default: + logger.warning("Unknown Header"); + throw new RarException(RarExceptionType.notRarArchive); + + } + } + // logger.info("\n--------end header--------"); + } + } + + /** + * Extract the file specified by the given header and write it to the + * supplied output stream + * + * @param header + * the header to be extracted + * @param os + * the outputstream + * @throws RarException + */ + public void extractFile(FileHeader hd, OutputStream os) throws RarException { + if (!headers.contains(hd)) { + throw new RarException(RarExceptionType.headerNotInArchive); + } + try { + doExtractFile(hd, os); + } catch (Exception e) { + if (e instanceof RarException) { + throw (RarException) e; + } else { + throw new RarException(e); + } + } + } + + /** + * Returns an {@link InputStream} that will allow to read the file and + * stream it. Please note that this method will create a new Thread and an a + * pair of Pipe streams. + * + * @param header + * the header to be extracted + * @throws RarException + * @throws IOException + * if any IO error occur + */ + public InputStream getInputStream(final FileHeader hd) throws RarException, + IOException { + final PipedInputStream in = new PipedInputStream(32 * 1024); + final PipedOutputStream out = new PipedOutputStream(in); + + // creates a new thread that will write data to the pipe. Data will be + // available in another InputStream, connected to the OutputStream. + new Thread(new Runnable() { + public void run() { + try { + extractFile(hd, out); + } catch (RarException e) { + } finally { + try { + out.close(); + } catch (IOException e) { + } + } + } + }).start(); + + return in; + } + + private void doExtractFile(FileHeader hd, OutputStream os) + throws RarException, IOException { + dataIO.init(os); + dataIO.init(hd); + dataIO.setUnpFileCRC(this.isOldFormat() ? 0 : 0xffFFffFF); + if (unpack == null) { + unpack = new Unpack(dataIO); + } + if (!hd.isSolid()) { + unpack.init(null); + } + unpack.setDestSize(hd.getFullUnpackSize()); + try { + unpack.doUnpack(hd.getUnpVersion(), hd.isSolid()); + // Verify file CRC + hd = dataIO.getSubHeader(); + long actualCRC = hd.isSplitAfter() ? ~dataIO.getPackedCRC() + : ~dataIO.getUnpFileCRC(); + int expectedCRC = hd.getFileCRC(); + if (actualCRC != expectedCRC) { + throw new RarException(RarExceptionType.crcError); + } + // if (!hd.isSplitAfter()) { + // // Verify file CRC + // if(~dataIO.getUnpFileCRC() != hd.getFileCRC()){ + // throw new RarException(RarExceptionType.crcError); + // } + // } + } catch (Exception e) { + unpack.cleanUp(); + if (e instanceof RarException) { + // throw new RarException((RarException)e); + throw (RarException) e; + } else { + throw new RarException(e); + } + } + } + + /** + * @return returns the main header of this archive + */ + public MainHeader getMainHeader() { + return newMhd; + } + + /** + * @return whether the archive is old format + */ + public boolean isOldFormat() { + return markHead.isOldFormat(); + } + + /** Close the underlying compressed file. */ + public void close() throws IOException { + if (rof != null) { + rof.close(); + rof = null; + } + if (unpack != null) { + unpack.cleanUp(); + } + } + + /** + * @return the volumeManager + */ + public VolumeManager getVolumeManager() { + return volumeManager; + } + + /** + * @param volumeManager + * the volumeManager to set + */ + public void setVolumeManager(VolumeManager volumeManager) { + this.volumeManager = volumeManager; + } + + /** + * @return the volume + */ + public Volume getVolume() { + return volume; + } + + /** + * @param volume + * the volume to set + * @throws IOException + */ + public void setVolume(Volume volume) throws IOException { + this.volume = volume; + setFile(volume.getReadOnlyAccess(), volume.getLength()); + } +} -- cgit v1.2.3