diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 99b5e7d07..f8f1e5f13 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,7 +26,7 @@ jlinker = "1.0.7" jphantom = "1.4.4" junit = "5.11.0" jsvg = "1.6.1" -llzip = "2.6.2" +lljzip = "2.7.0" logback-classic = { strictly = "1.4.11" } # newer releases break in jar releases mapping-io = "0.6.1" mockito = "5.14.2" @@ -109,7 +109,7 @@ junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref jsvg = { module = "com.github.weisj:jsvg", version.ref = "jsvg" } -llzip = { module = "software.coley:lljzip", version.ref = "llzip" } +lljzip = { module = "software.coley:lljzip", version.ref = "lljzip" } logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback-classic" } diff --git a/recaf-core/build.gradle b/recaf-core/build.gradle index ad31ba263..eda3177ec 100644 --- a/recaf-core/build.gradle +++ b/recaf-core/build.gradle @@ -28,7 +28,7 @@ dependencies { api(libs.gson) // required by R8 (from dex-translator) api(libs.instrument.server) api(libs.jphantom) - api(libs.llzip) + api(libs.lljzip) api(libs.bundles.logging) api(libs.mapping.io) api(libs.natural.order) diff --git a/recaf-core/src/main/java/software/coley/recaf/services/workspace/io/ResourceImporterConfig.java b/recaf-core/src/main/java/software/coley/recaf/services/workspace/io/ResourceImporterConfig.java index add51a5b0..794968040 100644 --- a/recaf-core/src/main/java/software/coley/recaf/services/workspace/io/ResourceImporterConfig.java +++ b/recaf-core/src/main/java/software/coley/recaf/services/workspace/io/ResourceImporterConfig.java @@ -25,6 +25,7 @@ public class ResourceImporterConfig extends BasicConfigContainer implements ServiceConfig { private final ObservableObject zipStrategy = new ObservableObject<>(ZipStrategy.JVM); private final ObservableBoolean skipRevisitedCenToLocalLinks = new ObservableBoolean(true); + private final ObservableBoolean allowBasicJvmBaseOffsetZeroCheck = new ObservableBoolean(true); @Inject public ResourceImporterConfig() { @@ -32,6 +33,7 @@ public ResourceImporterConfig() { addValue(new BasicConfigValue<>("zip-strategy", ZipStrategy.class, zipStrategy)); addValue(new BasicConfigValue<>("skip-revisited-cen-to-local-links", boolean.class, skipRevisitedCenToLocalLinks)); + addValue(new BasicConfigValue<>("allow-basic-base-offset-zero-check", boolean.class, allowBasicJvmBaseOffsetZeroCheck)); } /** @@ -55,6 +57,30 @@ public ObservableBoolean getSkipRevisitedCenToLocalLinks() { return skipRevisitedCenToLocalLinks; } + /** + * When the {@link #getZipStrategy() ZIP strategy} is {@link ZipStrategy#JVM} this allows toggling + * how the JVM base offset of the zip file is calculated. Normally the start of a ZIP file is calculated + * based off the logic in {@code ZipFile.Source#findEND()} which looks like: + *
{@code
+	 *  // ENDSIG matched, however the size of file comment in it does
+	 *  // not match the real size. One "common" cause for this problem
+	 *  // is some "extra" bytes are padded at the end of the zipfile.
+	 *  // Let's do some extra verification, we don't care about the
+	 *  // performance in this situation.
+	 *  byte[] sbuf = new byte[4];
+	 *  long cenpos = end.endpos - end.cenlen;
+	 *  long locpos = cenpos - end.cenoff;
+	 * }
+ * In some edge cases this results in {@code locpos} being {@code > 0} even when the file has no prefix/padding. + * + * @return {@code true} when defaulting to check for zero being the base JVM zip offset instead of the lookup + * based on the code in {@code ZipFile.Source#findEND()}. + */ + @Nonnull + public ObservableBoolean getAllowBasicJvmBaseOffsetZeroCheck() { + return allowBasicJvmBaseOffsetZeroCheck; + } + /** * @return Mapping of input bytes to a ZIP archive model. */ @@ -62,7 +88,8 @@ public ObservableBoolean getSkipRevisitedCenToLocalLinks() { public UncheckedFunction mapping() { ZipStrategy strategy = zipStrategy.getValue(); if (strategy == ZipStrategy.JVM) - return input -> ZipIO.read(input, new JvmZipReader(skipRevisitedCenToLocalLinks.getValue())); + return input -> ZipIO.read(input, new JvmZipReader(skipRevisitedCenToLocalLinks.getValue(), + allowBasicJvmBaseOffsetZeroCheck.getValue())); if (strategy == ZipStrategy.STANDARD) return ZipIO::readStandard; return ZipIO::readNaive; diff --git a/recaf-ui/src/main/resources/translations/en_US.lang b/recaf-ui/src/main/resources/translations/en_US.lang index 01a7c7358..2593d595f 100644 --- a/recaf-ui/src/main/resources/translations/en_US.lang +++ b/recaf-ui/src/main/resources/translations/en_US.lang @@ -741,6 +741,7 @@ service.io.recent-workspaces-config.recent-workspaces=Recent workspace service.io.recent-workspaces-config.last-class-export-path=Last class export path service.io.resource-importer-config=Archive importing service.io.resource-importer-config.zip-strategy=ZIP parsing strategy +service.io.resource-importer-config.allow-basic-base-offset-zero-check=Default to check 0 as zip beginning with JVM strategy service.io.resource-importer-config.skip-revisited-cen-to-local-links=Skip duplicate CEN-to-LOC entries with JVM strategy service.mapping=Mapping service.mapping.mapping-aggregator-config=Mapping aggregation