Restore multipart RAR open strategy for JVM extractor
Bring back callback-first RAR multipart opening with explicit RAR5/RAR fallback while keeping 7z.001 on VolumedArchiveInStream to improve 7z-JBinding compatibility on split RAR sets.
This commit is contained in:
parent
02bd4a61fb
commit
8fad61e329
Binary file not shown.
@ -11,6 +11,7 @@ import net.sf.sevenzipjbinding.IInStream;
|
|||||||
import net.sf.sevenzipjbinding.ISequentialOutStream;
|
import net.sf.sevenzipjbinding.ISequentialOutStream;
|
||||||
import net.sf.sevenzipjbinding.ICryptoGetTextPassword;
|
import net.sf.sevenzipjbinding.ICryptoGetTextPassword;
|
||||||
import net.sf.sevenzipjbinding.PropID;
|
import net.sf.sevenzipjbinding.PropID;
|
||||||
|
import net.sf.sevenzipjbinding.ArchiveFormat;
|
||||||
import net.sf.sevenzipjbinding.SevenZip;
|
import net.sf.sevenzipjbinding.SevenZip;
|
||||||
import net.sf.sevenzipjbinding.SevenZipException;
|
import net.sf.sevenzipjbinding.SevenZipException;
|
||||||
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
|
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
|
||||||
@ -42,6 +43,8 @@ public final class JBindExtractorMain {
|
|||||||
private static final Pattern NUMBERED_ZIP_SPLIT_RE = Pattern.compile("(?i).*\\.zip\\.\\d{3}$");
|
private static final Pattern NUMBERED_ZIP_SPLIT_RE = Pattern.compile("(?i).*\\.zip\\.\\d{3}$");
|
||||||
private static final Pattern OLD_ZIP_SPLIT_RE = Pattern.compile("(?i).*\\.z\\d{2,3}$");
|
private static final Pattern OLD_ZIP_SPLIT_RE = Pattern.compile("(?i).*\\.z\\d{2,3}$");
|
||||||
private static final Pattern SEVEN_ZIP_SPLIT_RE = Pattern.compile("(?i).*\\.7z\\.001$");
|
private static final Pattern SEVEN_ZIP_SPLIT_RE = Pattern.compile("(?i).*\\.7z\\.001$");
|
||||||
|
private static final Pattern RAR_MULTIPART_RE = Pattern.compile("(?i).*\\.part\\d+\\.rar$");
|
||||||
|
private static final Pattern RAR_OLDSPLIT_RE = Pattern.compile("(?i).*\\.r\\d{2,3}$");
|
||||||
private static volatile boolean sevenZipInitialized = false;
|
private static volatile boolean sevenZipInitialized = false;
|
||||||
|
|
||||||
private JBindExtractorMain() {
|
private JBindExtractorMain() {
|
||||||
@ -327,21 +330,78 @@ public final class JBindExtractorMain {
|
|||||||
SevenZipVolumeCallback callback = new SevenZipVolumeCallback(archiveFile, effectivePassword);
|
SevenZipVolumeCallback callback = new SevenZipVolumeCallback(archiveFile, effectivePassword);
|
||||||
|
|
||||||
// VolumedArchiveInStream is ONLY for .7z.001 split archives.
|
// VolumedArchiveInStream is ONLY for .7z.001 split archives.
|
||||||
|
// It internally checks for the ".7z.001" suffix and rejects everything else.
|
||||||
if (SEVEN_ZIP_SPLIT_RE.matcher(nameLower).matches()) {
|
if (SEVEN_ZIP_SPLIT_RE.matcher(nameLower).matches()) {
|
||||||
VolumedArchiveInStream volumed = new VolumedArchiveInStream(archiveFile.getName(), callback);
|
VolumedArchiveInStream volumed = new VolumedArchiveInStream(archiveFile.getName(), callback);
|
||||||
IInArchive archive = SevenZip.openInArchive(null, volumed, callback);
|
IInArchive archive = SevenZip.openInArchive(null, volumed, callback);
|
||||||
return new SevenZipArchiveContext(archive, null, volumed, callback);
|
return new SevenZipArchiveContext(archive, null, volumed, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
// All other archives (including multi-part RAR): use RandomAccessFileInStream
|
// Multi-part RAR (.part1.rar, .part2.rar or old-style .rar/.r01/.r02):
|
||||||
// with auto-detection. The IArchiveOpenVolumeCallback handles additional
|
// The first stream MUST be obtained via the callback so the volume name
|
||||||
// volumes when 7z-JBinding requests them.
|
// tracker is properly initialized. 7z-JBinding uses getProperty(NAME)
|
||||||
|
// to compute subsequent volume filenames.
|
||||||
|
boolean isMultiPartRar = RAR_MULTIPART_RE.matcher(nameLower).matches()
|
||||||
|
|| hasOldStyleRarSplits(archiveFile);
|
||||||
|
|
||||||
|
if (isMultiPartRar) {
|
||||||
|
IInStream inStream = callback.getStream(archiveFile.getAbsolutePath());
|
||||||
|
if (inStream == null) {
|
||||||
|
throw new IOException("Archiv konnte nicht geoeffnet werden: " + archiveFile.getAbsolutePath());
|
||||||
|
}
|
||||||
|
// Try RAR5 first (modern), then RAR4, then auto-detect
|
||||||
|
Exception lastError = null;
|
||||||
|
ArchiveFormat[] rarFormats = { ArchiveFormat.RAR5, ArchiveFormat.RAR, null };
|
||||||
|
for (ArchiveFormat fmt : rarFormats) {
|
||||||
|
try {
|
||||||
|
inStream.seek(0L, 0);
|
||||||
|
IInArchive archive = SevenZip.openInArchive(fmt, inStream, callback);
|
||||||
|
return new SevenZipArchiveContext(archive, null, null, callback);
|
||||||
|
} catch (Exception e) {
|
||||||
|
lastError = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback.close();
|
||||||
|
throw lastError != null ? lastError : new IOException("Archiv konnte nicht geoeffnet werden");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single-file archives: open directly with auto-detection
|
||||||
RandomAccessFile raf = new RandomAccessFile(archiveFile, "r");
|
RandomAccessFile raf = new RandomAccessFile(archiveFile, "r");
|
||||||
RandomAccessFileInStream stream = new RandomAccessFileInStream(raf);
|
RandomAccessFileInStream stream = new RandomAccessFileInStream(raf);
|
||||||
IInArchive archive = SevenZip.openInArchive(null, stream, callback);
|
IInArchive archive = SevenZip.openInArchive(null, stream, callback);
|
||||||
return new SevenZipArchiveContext(archive, stream, null, callback);
|
return new SevenZipArchiveContext(archive, stream, null, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean hasOldStyleRarSplits(File archiveFile) {
|
||||||
|
// Old-style RAR splits: main.rar + main.r01, main.r02, ...
|
||||||
|
String name = archiveFile.getName();
|
||||||
|
if (!name.toLowerCase(Locale.ROOT).endsWith(".rar")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
File parent = archiveFile.getParentFile();
|
||||||
|
if (parent == null || !parent.exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
File[] siblings = parent.listFiles();
|
||||||
|
if (siblings == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String stem = name.substring(0, name.length() - 4);
|
||||||
|
for (File sibling : siblings) {
|
||||||
|
if (!sibling.isFile()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String sibName = sibling.getName();
|
||||||
|
if (sibName.length() > stem.length() + 1 && sibName.substring(0, stem.length()).equalsIgnoreCase(stem)) {
|
||||||
|
String suffix = sibName.substring(stem.length());
|
||||||
|
if (RAR_OLDSPLIT_RE.matcher(suffix).matches() || suffix.toLowerCase(Locale.ROOT).matches("\\.r\\d{2,3}")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isWrongPassword(ZipException error, boolean encrypted) {
|
private static boolean isWrongPassword(ZipException error, boolean encrypted) {
|
||||||
if (error == null) {
|
if (error == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user