From 529e28b5d70dd9e37de6c064bb213436bcceacad Mon Sep 17 00:00:00 2001 From: Reinhard Pointner Date: Sat, 9 Mar 2013 15:12:01 +0000 Subject: [PATCH] * enable proper processing of any .001 style raw multi-part archive 7zip can handle --- .../sourceforge/filebot/archive/Archive.java | 27 ++- .../filebot/archive/SevenZipLoader.java | 6 +- .../archive/VolumedArchiveInStream.java | 184 ++++++++++++++++++ 3 files changed, 206 insertions(+), 11 deletions(-) create mode 100644 source/net/sourceforge/filebot/archive/VolumedArchiveInStream.java diff --git a/source/net/sourceforge/filebot/archive/Archive.java b/source/net/sourceforge/filebot/archive/Archive.java index 61c7fe7b..d9b3ac1f 100644 --- a/source/net/sourceforge/filebot/archive/Archive.java +++ b/source/net/sourceforge/filebot/archive/Archive.java @@ -8,7 +8,6 @@ import java.io.FileFilter; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.EnumMap; import java.util.List; @@ -20,8 +19,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import net.sf.sevenzipjbinding.ArchiveFormat; -import net.sf.sevenzipjbinding.IArchiveOpenCallback; -import net.sf.sevenzipjbinding.IInStream; import net.sf.sevenzipjbinding.ISevenZipInArchive; import net.sf.sevenzipjbinding.PropID; import net.sf.sevenzipjbinding.SevenZipException; @@ -41,13 +38,15 @@ public class Archive implements Closeable { throw new FileNotFoundException(file.getAbsolutePath()); } - // 7-Zip-JBinding NATIVE LIBS MUST BE LOADED WITH SYSTEM CLASSLOADER - Class clazz = ClassLoader.getSystemClassLoader().loadClass("net.sourceforge.filebot.archive.SevenZipLoader"); - Method m = clazz.getMethod("open", new Class[] { IInStream.class, IArchiveOpenCallback.class }); - try { openVolume = new ArchiveOpenVolumeCallback(); - inArchive = (ISevenZipInArchive) m.invoke(null, openVolume.getStream(file.getAbsolutePath()), openVolume); + if (!hasMultiPartIndex(file)) { + // single volume archives and multi-volume rar archives + inArchive = SevenZipLoader.open(openVolume.getStream(file.getAbsolutePath()), openVolume); + } else { + // raw multi-volume archives + inArchive = SevenZipLoader.open(new VolumedArchiveInStream(file.getAbsolutePath(), openVolume), null); + } } catch (InvocationTargetException e) { throw (Exception) e.getTargetException(); } @@ -142,16 +141,24 @@ public class Archive implements Closeable { return extensions; } + private static final Pattern multiPartIndex = Pattern.compile("[.][0-9]{3}+$"); + + + public static boolean hasMultiPartIndex(File file) { + return multiPartIndex.matcher(file.getName()).find(); + } + public static final FileFilter VOLUME_ONE_FILTER = new FileFilter() { - private Pattern volume = Pattern.compile("[.]r[0-9]+$|[.]part[0-9]+[.][0-9a-z]+$|[.][0-9]+$", Pattern.CASE_INSENSITIVE); + private Pattern volume = Pattern.compile("[.]r[0-9]+$|[.]part[0-9]+|[.][0-9]+$", Pattern.CASE_INSENSITIVE); private FileFilter archives = new ExtensionFileFilter(getArchiveTypes()); @Override public boolean accept(File path) { - if (!archives.accept(path) && !volume.matcher(path.getName()).find()) + if (!archives.accept(path) && !hasMultiPartIndex(path)) { return false; + } Matcher matcher = volume.matcher(path.getName()); if (matcher.find()) { diff --git a/source/net/sourceforge/filebot/archive/SevenZipLoader.java b/source/net/sourceforge/filebot/archive/SevenZipLoader.java index 8e661bdd..c29027e2 100644 --- a/source/net/sourceforge/filebot/archive/SevenZipLoader.java +++ b/source/net/sourceforge/filebot/archive/SevenZipLoader.java @@ -46,7 +46,11 @@ public class SevenZipLoader { // initialize 7-Zip-JBinding requireNativeLibraries(); - return SevenZip.openInArchive(null, stream, callback); + if (callback == null) { + return SevenZip.openInArchive(null, stream); + } else { + return SevenZip.openInArchive(null, stream, callback); + } } } diff --git a/source/net/sourceforge/filebot/archive/VolumedArchiveInStream.java b/source/net/sourceforge/filebot/archive/VolumedArchiveInStream.java new file mode 100644 index 00000000..fb54364a --- /dev/null +++ b/source/net/sourceforge/filebot/archive/VolumedArchiveInStream.java @@ -0,0 +1,184 @@ + +package net.sourceforge.filebot.archive; + + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +import net.sf.sevenzipjbinding.IArchiveOpenVolumeCallback; +import net.sf.sevenzipjbinding.IInStream; +import net.sf.sevenzipjbinding.PropID; +import net.sf.sevenzipjbinding.SevenZipException; + + +public class VolumedArchiveInStream implements IInStream { + + private static final String FIRST_VOLUME_POSTFIX = ".001"; + + private long absoluteOffset; + private long absoluteLength = -1; + + private int currentIndex = -1; + private IInStream currentInStream; + private long currentVolumeOffset; + private long currentVolumeLength; + private List volumePositions = new ArrayList(); + + private final IArchiveOpenVolumeCallback archiveOpenVolumeCallback; + private String cuttedVolumeFilename; + + + public VolumedArchiveInStream(IArchiveOpenVolumeCallback archiveOpenVolumeCallback) throws SevenZipException { + this((String) archiveOpenVolumeCallback.getProperty(PropID.NAME), archiveOpenVolumeCallback); + } + + + public VolumedArchiveInStream(String firstVolumeFilename, IArchiveOpenVolumeCallback archiveOpenVolumeCallback) throws SevenZipException { + this.archiveOpenVolumeCallback = archiveOpenVolumeCallback; + volumePositions.add(Long.valueOf(0)); + + if (!firstVolumeFilename.endsWith(FIRST_VOLUME_POSTFIX)) { + throw new SevenZipException("The first volume filename '" + firstVolumeFilename + "' don't ends with the postfix: '" + FIRST_VOLUME_POSTFIX + "'. Can't proceed"); + + } + + cuttedVolumeFilename = firstVolumeFilename.substring(0, firstVolumeFilename.length() - 3); + openVolume(1, true); + } + + + private void openVolume(int index, boolean seekToBegin) throws SevenZipException { + if (currentIndex == index) { + return; + } + + for (int i = volumePositions.size(); i < index && absoluteLength == -1; i++) { + openVolume(i, false); + } + + if (absoluteLength != -1 && volumePositions.size() <= index) { + return; + } + + String volumeFilename = cuttedVolumeFilename + MessageFormat.format("{0,number,000}", Integer.valueOf(index)); + + // Get new IInStream + IInStream newInStream = archiveOpenVolumeCallback.getStream(volumeFilename); + + if (newInStream == null) { + absoluteLength = volumePositions.get(volumePositions.size() - 1).longValue(); + return; + } + + currentInStream = newInStream; + + if (volumePositions.size() == index) { + // Determine volume size + currentVolumeLength = currentInStream.seek(0, SEEK_END); + if (currentVolumeLength == 0) { + throw new RuntimeException("Volume " + index + " is empty"); + } + volumePositions.add(Long.valueOf(volumePositions.get(index - 1).longValue() + currentVolumeLength)); + + if (seekToBegin) { + currentInStream.seek(0, SEEK_SET); + } + } else { + currentVolumeLength = volumePositions.get(index).longValue() - volumePositions.get(index - 1).longValue(); + } + + if (seekToBegin) { + currentVolumeOffset = 0; + absoluteOffset = volumePositions.get(index - 1).longValue(); + } + + currentIndex = index; + } + + + private void openVolumeToAbsoluteOffset() throws SevenZipException { + int index = volumePositions.size() - 1; + if (absoluteLength != -1 && absoluteOffset >= absoluteLength) { + return; + } + while (volumePositions.get(index).longValue() > absoluteOffset) { + index--; + } + + if (index < volumePositions.size() - 1) { + openVolume(index + 1, false); + return; + } + + do { + index++; + openVolume(index, false); + } while ((absoluteLength == -1 || absoluteOffset < absoluteLength) && volumePositions.get(index).longValue() <= absoluteOffset); + + } + + + @Override + public long seek(long offset, int seekOrigin) throws SevenZipException { + long newOffset; + boolean proceedWithSeek = false; + switch (seekOrigin) { + case SEEK_SET: + newOffset = offset; + break; + + case SEEK_CUR: + newOffset = absoluteOffset + offset; + break; + + case SEEK_END: + if (absoluteLength == -1) { + openVolume(Integer.MAX_VALUE, false); + proceedWithSeek = true; + } + newOffset = absoluteLength + offset; + break; + + default: + throw new RuntimeException("Seek: unknown origin: " + seekOrigin); + } + + if (newOffset == absoluteOffset && !proceedWithSeek) { + return newOffset; + } + absoluteOffset = newOffset; + + openVolumeToAbsoluteOffset(); + + if (absoluteLength != -1 && absoluteLength <= absoluteOffset) { + absoluteOffset = absoluteLength; + return absoluteLength; + } + + currentVolumeOffset = absoluteOffset - volumePositions.get(currentIndex - 1).longValue(); + currentInStream.seek(currentVolumeOffset, SEEK_SET); + + return newOffset; + } + + + @Override + public int read(byte[] data) throws SevenZipException { + if (absoluteLength != -1 && absoluteOffset >= absoluteLength) { + return 0; + } + + int read = currentInStream.read(data); + + absoluteOffset += read; + currentVolumeOffset += read; + + if (currentVolumeOffset >= currentVolumeLength) { + openVolume(currentIndex + 1, true); + } + + return read; + } + +}