Fix multi-part RAR: get first stream via callback, track current volume name
Two bugs in SevenZipVolumeCallback caused multi-part RAR extraction to fail: 1. getProperty(NAME) always returned firstFileName instead of tracking the last opened volume name. 7z-JBinding needs this to compute subsequent volume filenames. 2. The first IInStream was created separately instead of through the callback's getStream() method, so the volume name tracker was not properly initialized. Verified with real multi-part RAR5 test archives (3 parts, WinRAR 7.01). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
26bf675a41
commit
93d54c8f84
Binary file not shown.
Binary file not shown.
@ -337,37 +337,37 @@ public final class JBindExtractorMain {
|
|||||||
return new SevenZipArchiveContext(archive, null, volumed, callback);
|
return new SevenZipArchiveContext(archive, null, volumed, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
RandomAccessFile raf = new RandomAccessFile(archiveFile, "r");
|
|
||||||
RandomAccessFileInStream stream = new RandomAccessFileInStream(raf);
|
|
||||||
|
|
||||||
// Multi-part RAR (.part1.rar, .part2.rar or old-style .rar/.r01/.r02):
|
// Multi-part RAR (.part1.rar, .part2.rar or old-style .rar/.r01/.r02):
|
||||||
// Auto-detection with null format can fail for multi-volume RAR because
|
// The first stream MUST be obtained via the callback so the volume name
|
||||||
// 7z-JBinding may not fully recognize the format from a single part.
|
// tracker is properly initialized. 7z-JBinding uses getProperty(NAME)
|
||||||
// Specify the format explicitly (try RAR5 first, then RAR4).
|
// to compute subsequent volume filenames.
|
||||||
if (RAR_MULTIPART_RE.matcher(nameLower).matches() || hasOldStyleRarSplits(archiveFile)) {
|
boolean isMultiPartRar = RAR_MULTIPART_RE.matcher(nameLower).matches()
|
||||||
ArchiveFormat[] rarFormats = { ArchiveFormat.RAR5, ArchiveFormat.RAR };
|
|| 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;
|
Exception lastError = null;
|
||||||
|
ArchiveFormat[] rarFormats = { ArchiveFormat.RAR5, ArchiveFormat.RAR, null };
|
||||||
for (ArchiveFormat fmt : rarFormats) {
|
for (ArchiveFormat fmt : rarFormats) {
|
||||||
try {
|
try {
|
||||||
stream.seek(0L, 0);
|
inStream.seek(0L, 0);
|
||||||
IInArchive archive = SevenZip.openInArchive(fmt, stream, callback);
|
IInArchive archive = SevenZip.openInArchive(fmt, inStream, callback);
|
||||||
return new SevenZipArchiveContext(archive, stream, null, callback);
|
return new SevenZipArchiveContext(archive, null, null, callback);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
lastError = e;
|
lastError = e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Final attempt with auto-detection
|
callback.close();
|
||||||
try {
|
throw lastError != null ? lastError : new IOException("Archiv konnte nicht geoeffnet werden");
|
||||||
stream.seek(0L, 0);
|
|
||||||
IInArchive archive = SevenZip.openInArchive(null, stream, callback);
|
|
||||||
return new SevenZipArchiveContext(archive, stream, null, callback);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Close the RAF since we're about to throw
|
|
||||||
try { raf.close(); } catch (Throwable ignored) {}
|
|
||||||
throw lastError != null ? lastError : e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Single-file archives: open directly with auto-detection
|
||||||
|
RandomAccessFile raf = new RandomAccessFile(archiveFile, "r");
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
@ -840,20 +840,22 @@ public final class JBindExtractorMain {
|
|||||||
|
|
||||||
private static final class SevenZipVolumeCallback implements IArchiveOpenCallback, IArchiveOpenVolumeCallback, ICryptoGetTextPassword, Closeable {
|
private static final class SevenZipVolumeCallback implements IArchiveOpenCallback, IArchiveOpenVolumeCallback, ICryptoGetTextPassword, Closeable {
|
||||||
private final File archiveDir;
|
private final File archiveDir;
|
||||||
private final String firstFileName;
|
|
||||||
private final String password;
|
private final String password;
|
||||||
private final Map<String, RandomAccessFile> openRafs = new HashMap<String, RandomAccessFile>();
|
private final Map<String, RandomAccessFile> openRafs = new HashMap<String, RandomAccessFile>();
|
||||||
|
// Must track the LAST opened volume name — 7z-JBinding queries this via
|
||||||
|
// getProperty(NAME) to compute the next volume filename.
|
||||||
|
private volatile String currentVolumeName;
|
||||||
|
|
||||||
SevenZipVolumeCallback(File archiveFile, String password) {
|
SevenZipVolumeCallback(File archiveFile, String password) {
|
||||||
this.archiveDir = archiveFile.getAbsoluteFile().getParentFile();
|
this.archiveDir = archiveFile.getAbsoluteFile().getParentFile();
|
||||||
this.firstFileName = archiveFile.getName();
|
this.currentVolumeName = archiveFile.getName();
|
||||||
this.password = password == null ? "" : password;
|
this.password = password == null ? "" : password;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getProperty(PropID propID) {
|
public Object getProperty(PropID propID) {
|
||||||
if (propID == PropID.NAME) {
|
if (propID == PropID.NAME) {
|
||||||
return firstFileName;
|
return currentVolumeName;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -872,6 +874,9 @@ public final class JBindExtractorMain {
|
|||||||
openRafs.put(key, raf);
|
openRafs.put(key, raf);
|
||||||
}
|
}
|
||||||
raf.seek(0L);
|
raf.seek(0L);
|
||||||
|
// Update current volume name so getProperty(NAME) returns the
|
||||||
|
// correct value when 7z-JBinding computes the next volume.
|
||||||
|
currentVolumeName = filename;
|
||||||
return new RandomAccessFileInStream(raf);
|
return new RandomAccessFileInStream(raf);
|
||||||
} catch (IOException error) {
|
} catch (IOException error) {
|
||||||
throw new SevenZipException("Volume konnte nicht geoffnet werden: " + filename, error);
|
throw new SevenZipException("Volume konnte nicht geoffnet werden: " + filename, error);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user