diff --git a/pom.xml b/pom.xml
index a4c258a..ffb7bd6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
software.coley
lljzip
- 2.4.1
+ 2.5.0
LL Java ZIP
Lower level ZIP support for Java
diff --git a/src/main/java/software/coley/lljzip/format/model/ZipArchive.java b/src/main/java/software/coley/lljzip/format/model/ZipArchive.java
index 4ea84e5..5ec7daf 100644
--- a/src/main/java/software/coley/lljzip/format/model/ZipArchive.java
+++ b/src/main/java/software/coley/lljzip/format/model/ZipArchive.java
@@ -1,5 +1,6 @@
package software.coley.lljzip.format.model;
+import software.coley.lljzip.format.read.ZipReader;
import software.coley.lljzip.format.transform.ZipPartMapper;
import software.coley.lljzip.util.OffsetComparator;
@@ -7,6 +8,7 @@
import javax.annotation.Nullable;
import java.io.Closeable;
import java.io.IOException;
+import java.lang.foreign.MemorySegment;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -19,6 +21,7 @@
public class ZipArchive implements AutoCloseable, Iterable {
private final List parts = new ArrayList<>();
private final Closeable closableBackingResource;
+ private MemorySegment prefixData;
/**
* New zip archive without any backing resource.
@@ -37,6 +40,28 @@ public ZipArchive(@Nonnull Closeable closableBackingResource) {
this.closableBackingResource = closableBackingResource;
}
+ /**
+ * If the {@link ZipReader} used to read this archive supports tracking such information this will return
+ * any data not contained in the archive that was found at the front of the input content.
+ *
+ * Some applications like Jar2Exe will prepend a loader executable in front of the jar, in which case this content
+ * would be that executable. It can be any kind of arbitrary data though.
+ *
+ * @return Data at the front of the archive.
+ */
+ @Nullable
+ public MemorySegment getPrefixData() {
+ return prefixData;
+ }
+
+ /**
+ * @param prefixData
+ * Data at the front of the archive.
+ */
+ public void setPrefixData(@Nullable MemorySegment prefixData) {
+ this.prefixData = prefixData;
+ }
+
/**
* @param mapper
* Part mapper to manipulate contained zip parts.
diff --git a/src/main/java/software/coley/lljzip/format/read/ForwardScanZipReader.java b/src/main/java/software/coley/lljzip/format/read/ForwardScanZipReader.java
index 43d59dc..8ef34fe 100644
--- a/src/main/java/software/coley/lljzip/format/read/ForwardScanZipReader.java
+++ b/src/main/java/software/coley/lljzip/format/read/ForwardScanZipReader.java
@@ -13,8 +13,7 @@
import javax.annotation.Nonnull;
import java.io.IOException;
import java.lang.foreign.MemorySegment;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.*;
/**
* The standard read strategy that should work with standard zip archives.
@@ -67,7 +66,7 @@ public void read(@Nonnull ZipArchive zip, @Nonnull MemorySegment data) throws IO
// Read local files
// - Set to prevent duplicate file header entries for the same offset
- Set offsets = new HashSet<>();
+ NavigableSet offsets = new TreeSet<>();
for (CentralDirectoryFileHeader directory : zip.getCentralDirectories()) {
long offset = zipStart + directory.getRelativeOffsetOfLocalHeader();
if (!offsets.contains(offset) && MemorySegmentUtil.readQuad(data, offset) == ZipPatterns.LOCAL_FILE_HEADER_QUAD) {
@@ -83,6 +82,12 @@ public void read(@Nonnull ZipArchive zip, @Nonnull MemorySegment data) throws IO
}
}
+ // Record any data appearing at the front of the file not associated with the ZIP file contents.
+ if (!offsets.isEmpty()) {
+ long firstOffset = offsets.first();
+ zip.setPrefixData(data.asSlice(0, firstOffset));
+ }
+
// Sort based on order
zip.sortParts(new OffsetComparator());
}
diff --git a/src/main/java/software/coley/lljzip/format/read/JvmZipReader.java b/src/main/java/software/coley/lljzip/format/read/JvmZipReader.java
index dcfad74..59d1fb5 100644
--- a/src/main/java/software/coley/lljzip/format/read/JvmZipReader.java
+++ b/src/main/java/software/coley/lljzip/format/read/JvmZipReader.java
@@ -40,7 +40,7 @@ public JvmZipReader() {
*
* @param skipRevisitedCenToLocalLinks
* Flag to skip creating duplicate {@link LocalFileHeader} entries if multiple
- * {@link CentralDirectoryFileHeader} point to the same location.
+ * {@link CentralDirectoryFileHeader} point to the same location.
*/
public JvmZipReader(boolean skipRevisitedCenToLocalLinks) {
this(new JvmZipPartAllocator(), skipRevisitedCenToLocalLinks);
@@ -53,7 +53,7 @@ public JvmZipReader(boolean skipRevisitedCenToLocalLinks) {
* Allocator to use.
* @param skipRevisitedCenToLocalLinks
* Flag to skip creating duplicate {@link LocalFileHeader} entries if multiple
- * {@link CentralDirectoryFileHeader} point to the same location.
+ * {@link CentralDirectoryFileHeader} point to the same location.
*/
public JvmZipReader(@Nonnull ZipPartAllocator allocator, boolean skipRevisitedCenToLocalLinks) {
super(allocator);
@@ -203,6 +203,12 @@ else if (MemorySegmentUtil.readQuad(data, jvmBaseFileOffset) == ZipPatterns.CENT
}
}
+ // Record any data appearing at the front of the file not associated with the ZIP file contents.
+ if (!entryOffsets.isEmpty()) {
+ long firstOffset = entryOffsets.first();
+ zip.setPrefixData(data.asSlice(0, firstOffset));
+ }
+
// Sort based on order
zip.sortParts(new OffsetComparator());
}
diff --git a/src/main/java/software/coley/lljzip/format/read/NaiveLocalFileZipReader.java b/src/main/java/software/coley/lljzip/format/read/NaiveLocalFileZipReader.java
index 966dc11..c422618 100644
--- a/src/main/java/software/coley/lljzip/format/read/NaiveLocalFileZipReader.java
+++ b/src/main/java/software/coley/lljzip/format/read/NaiveLocalFileZipReader.java
@@ -34,12 +34,18 @@ public NaiveLocalFileZipReader(@Nonnull ZipPartAllocator allocator) {
@Override
public void read(@Nonnull ZipArchive zip, @Nonnull MemorySegment data) throws IOException {
- long localFileOffset = -1;
- while ((localFileOffset = MemorySegmentUtil.indexOfQuad(data, localFileOffset + 1, ZipPatterns.LOCAL_FILE_HEADER_QUAD)) >= 0) {
+ long localFileOffset = MemorySegmentUtil.indexOfQuad(data, 0, ZipPatterns.LOCAL_FILE_HEADER_QUAD);
+ if (localFileOffset < 0) return;
+ if (localFileOffset > 0) {
+ // The first offset containing archive data is not at the first byte.
+ // Record whatever content is at the front.
+ zip.setPrefixData(data.asSlice(0, localFileOffset));
+ }
+ do {
LocalFileHeader file = newLocalFileHeader();
file.read(data, localFileOffset);
zip.addPart(file);
postProcessLocalFileHeader(file);
- }
+ } while ((localFileOffset = MemorySegmentUtil.indexOfQuad(data, localFileOffset + 1, ZipPatterns.LOCAL_FILE_HEADER_QUAD)) >= 0);
}
}