diff --git a/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java b/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java index e8bbf075..39f0f282 100644 --- a/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java +++ b/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java @@ -32,6 +32,7 @@ import java.nio.file.StandardCopyOption; import java.util.Calendar; import java.util.Comparator; import java.util.GregorianCalendar; +import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; @@ -122,7 +123,12 @@ public class ZipReprocessorUtil { } newEntry.setMethod(zipEntryCompressionMethod(zipEntryCompression)); - copyZipEntry(zipOutputStream, newEntry, zipFile.getInputStream(entry)); + + if (zipEntryCompression == ZipEntryCompression.STORED) { + copyUncompressedZipEntry(zipOutputStream, newEntry, zipFile.getInputStream(entry)); + } else { + copyZipEntry(zipOutputStream, newEntry, zipFile.getInputStream(entry)); + } } } } @@ -176,6 +182,21 @@ public class ZipReprocessorUtil { zipOutputStream.closeEntry(); } + private static void copyUncompressedZipEntry(ZipOutputStream zipOutputStream, ZipEntry entry, InputStream inputStream) throws IOException { + // We need to read the entire input stream to calculate the CRC32 checksum and the size of the entry. + final byte[] data = inputStream.readAllBytes(); + + var crc = new CRC32(); + crc.update(data); + entry.setCrc(crc.getValue()); + entry.setSize(data.length); + entry.setCompressedSize(data.length); + + zipOutputStream.putNextEntry(entry); + zipOutputStream.write(data, 0, data.length); + zipOutputStream.closeEntry(); + } + private static void setConstantFileTime(ZipEntry entry) { // See https://github.com/openjdk/jdk/blob/master/test/jdk/java/util/zip/ZipFile/ZipEntryTimeBounds.java entry.setTime(new GregorianCalendar(1980, Calendar.JANUARY, 1, 0, 0, 0).getTimeInMillis()); diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/ZipUtilsTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/ZipUtilsTest.groovy index 349bfe91..b2e8edd0 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/ZipUtilsTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/ZipUtilsTest.groovy @@ -29,6 +29,7 @@ import java.nio.file.Files import java.time.ZoneId import com.google.gson.JsonObject +import org.gradle.api.tasks.bundling.ZipEntryCompression import spock.lang.Specification import net.fabricmc.loom.util.Checksum @@ -240,4 +241,21 @@ class ZipUtilsTest extends Specification { then: thrown FileSystemUtil.UnrecoverableZipException } + + def "reprocess uncompressed"() { + given: + // Create a reproducible input zip + def dir = Files.createTempDirectory("loom-zip-test") + def zip = Files.createTempFile("loom-zip-test", ".zip") + def fileInside = dir.resolve("text.txt") + Files.writeString(fileInside, "hello world") + ZipUtils.pack(dir, zip) + + when: + ZipReprocessorUtil.reprocessZip(zip, true, false, ZipEntryCompression.STORED) + + then: + ZipUtils.unpack(zip, "text.txt") == "hello world".bytes + Checksum.sha1Hex(zip) == "e699fa52a520553241aac798f72255ac0a912b05" + } }