Skip to content

Commit f18c86d

Browse files
committed
Add option to adopt CEN file names in archive importing config
1 parent 729eff2 commit f18c86d

File tree

6 files changed

+77
-30
lines changed

6 files changed

+77
-30
lines changed

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jlinker = "1.0.7"
2626
jphantom = "1.4.4"
2727
junit = "5.11.4"
2828
jsvg = "1.6.1"
29-
lljzip = "2.7.2"
29+
lljzip = "2.8.1"
3030
logback-classic = { strictly = "1.4.11" } # newer releases break in jar releases
3131
mapping-io = "0.7.0"
3232
mockito = "5.15.2"

recaf-core/src/main/java/software/coley/recaf/services/workspace/io/ResourceImporterConfig.java

Lines changed: 70 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,18 @@
55
import jakarta.inject.Inject;
66
import software.coley.collections.func.UncheckedFunction;
77
import software.coley.lljzip.ZipIO;
8+
import software.coley.lljzip.format.ZipPatterns;
89
import software.coley.lljzip.format.model.CentralDirectoryFileHeader;
910
import software.coley.lljzip.format.model.LocalFileHeader;
1011
import software.coley.lljzip.format.model.ZipArchive;
12+
import software.coley.lljzip.format.read.ForwardScanZipReader;
1113
import software.coley.lljzip.format.read.JvmZipReader;
14+
import software.coley.lljzip.format.read.NaiveLocalFileZipReader;
15+
import software.coley.lljzip.format.read.SimpleZipPartAllocator;
16+
import software.coley.lljzip.format.read.ZipPartAllocator;
17+
import software.coley.lljzip.util.MemorySegmentUtil;
18+
import software.coley.lljzip.util.data.MemorySegmentData;
19+
import software.coley.lljzip.util.data.StringData;
1220
import software.coley.observables.ObservableBoolean;
1321
import software.coley.observables.ObservableInteger;
1422
import software.coley.observables.ObservableObject;
@@ -17,7 +25,9 @@
1725
import software.coley.recaf.config.ConfigGroups;
1826
import software.coley.recaf.services.ServiceConfig;
1927

20-
import java.util.List;
28+
import java.lang.foreign.MemorySegment;
29+
30+
import static software.coley.lljzip.util.MemorySegmentUtil.readLongSlice;
2131

2232
/**
2333
* Config for {@link ResourceImporter}.
@@ -29,7 +39,8 @@ public class ResourceImporterConfig extends BasicConfigContainer implements Serv
2939
private final ObservableObject<ZipStrategy> zipStrategy = new ObservableObject<>(ZipStrategy.JVM);
3040
private final ObservableBoolean skipRevisitedCenToLocalLinks = new ObservableBoolean(true);
3141
private final ObservableBoolean allowBasicJvmBaseOffsetZeroCheck = new ObservableBoolean(true);
32-
private final ObservableBoolean ignoreNaiveFileLengths = new ObservableBoolean(false);
42+
private final ObservableBoolean ignoreFileLengths = new ObservableBoolean(false);
43+
private final ObservableBoolean adoptStandardCenFileNames = new ObservableBoolean(false);
3344
private final ObservableInteger maxEmbeddedZipDepth = new ObservableInteger(3);
3445

3546
@Inject
@@ -39,7 +50,8 @@ public ResourceImporterConfig() {
3950
addValue(new BasicConfigValue<>("zip-strategy", ZipStrategy.class, zipStrategy));
4051
addValue(new BasicConfigValue<>("skip-revisited-cen-to-local-links", boolean.class, skipRevisitedCenToLocalLinks));
4152
addValue(new BasicConfigValue<>("allow-basic-base-offset-zero-check", boolean.class, allowBasicJvmBaseOffsetZeroCheck));
42-
addValue(new BasicConfigValue<>("ignore-naive-file-lengths", boolean.class, ignoreNaiveFileLengths));
53+
addValue(new BasicConfigValue<>("ignore-file-lengths", boolean.class, ignoreFileLengths));
54+
addValue(new BasicConfigValue<>("adapt-standard-cen-file-names", boolean.class, adoptStandardCenFileNames));
4355
addValue(new BasicConfigValue<>("max-embedded-zip-depth", int.class, maxEmbeddedZipDepth));
4456
}
4557

@@ -96,8 +108,8 @@ public ObservableBoolean getAllowBasicJvmBaseOffsetZeroCheck() {
96108
* @return {@code true} to ignore the reported file lengths when using {@link ZipStrategy#NAIVE}.
97109
*/
98110
@Nonnull
99-
public ObservableBoolean getIgnoreNaiveFileLengths() {
100-
return ignoreNaiveFileLengths;
111+
public ObservableBoolean getIgnoreFileLengths() {
112+
return ignoreFileLengths;
101113
}
102114

103115
/**
@@ -116,28 +128,62 @@ public ObservableInteger getMaxEmbeddedZipDepth() {
116128
public UncheckedFunction<byte[], ZipArchive> mapping() {
117129
ZipStrategy strategy = zipStrategy.getValue();
118130
if (strategy == ZipStrategy.JVM)
119-
return input -> ZipIO.read(input, new JvmZipReader(skipRevisitedCenToLocalLinks.getValue(),
120-
allowBasicJvmBaseOffsetZeroCheck.getValue()));
131+
return newJvmMapping();
121132
if (strategy == ZipStrategy.STANDARD)
122-
return ZipIO::readStandard;
123-
return input -> {
124-
ZipArchive archive = ZipIO.readNaive(input);
125-
if (ignoreNaiveFileLengths.getValue()) {
126-
List<LocalFileHeader> items = archive.getLocalFiles();
127-
for (int i = 0; i < items.size(); i++) {
128-
LocalFileHeader lfh = items.get(i);
129-
long nextStart = i < items.size() - 1 ?
130-
items.get(i + 1).offset() :
131-
input.length;
132-
long expectedEnd = lfh.offset() + lfh.length();
133-
if (expectedEnd < nextStart) {
134-
// TODO: Subtract nextStart here if data-descriptor is present
135-
lfh.setFileDataEndOffset(nextStart);
136-
}
133+
return newStandardMapping();
134+
return newNaiveMapping();
135+
}
136+
137+
@Nonnull
138+
private UncheckedFunction<byte[], ZipArchive> newNaiveMapping() {
139+
return input -> ZipIO.read(input, new NaiveLocalFileZipReader(newPartAllocator()));
140+
}
141+
142+
@Nonnull
143+
private UncheckedFunction<byte[], ZipArchive> newStandardMapping() {
144+
return input -> ZipIO.read(input, new ForwardScanZipReader(newPartAllocator()) {
145+
@Override
146+
public void postProcessLocalFileHeader(@Nonnull LocalFileHeader file) {
147+
if (adoptStandardCenFileNames.getValue()) {
148+
CentralDirectoryFileHeader directoryFileHeader = file.getLinkedDirectoryFileHeader();
149+
if (directoryFileHeader != null)
150+
file.setFileName(StringData.of(directoryFileHeader.getFileNameAsString()));
137151
}
138152
}
139-
return archive;
140-
};
153+
});
154+
}
155+
156+
@Nonnull
157+
private UncheckedFunction<byte[], ZipArchive> newJvmMapping() {
158+
return input -> ZipIO.read(input, new JvmZipReader(skipRevisitedCenToLocalLinks.getValue(), allowBasicJvmBaseOffsetZeroCheck.getValue()));
159+
}
160+
161+
/**
162+
* @return Part allocator for Naive/Standard strategies.
163+
*/
164+
@Nonnull
165+
private ZipPartAllocator newPartAllocator() {
166+
if (ignoreFileLengths.getValue()) {
167+
return new SimpleZipPartAllocator() {
168+
@Nonnull
169+
@Override
170+
public LocalFileHeader newLocalFileHeader() {
171+
return new LocalFileHeader() {
172+
@Nonnull
173+
@Override
174+
protected MemorySegmentData readFileData(@Nonnull MemorySegment data, long headerOffset) {
175+
long localOffset = MIN_FIXED_SIZE + getFileNameLength() + getExtraFieldLength();
176+
long nextStart = MemorySegmentUtil.indexOfQuad(data, headerOffset + localOffset, ZipPatterns.LOCAL_FILE_HEADER_QUAD);
177+
long fileDataLength = nextStart > headerOffset ?
178+
nextStart - (headerOffset + localOffset) :
179+
data.byteSize() - (headerOffset + localOffset);
180+
return MemorySegmentData.of(readLongSlice(data, headerOffset, localOffset, fileDataLength));
181+
}
182+
};
183+
}
184+
};
185+
}
186+
return new SimpleZipPartAllocator();
141187
}
142188

143189
/**

recaf-ui/src/main/resources/translations/en_US.lang

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -786,8 +786,9 @@ service.io.recent-workspaces-config.last-class-export-path=Last class export pat
786786
service.io.resource-importer-config=Archive importing
787787
service.io.resource-importer-config.zip-strategy=ZIP parsing strategy
788788
service.io.resource-importer-config.allow-basic-base-offset-zero-check=Default to check 0 as zip beginning with JVM strategy
789-
service.io.resource-importer-config.skip-revisited-cen-to-local-links=Skip duplicate CEN-to-LOC entries with JVM strategy
790-
service.io.resource-importer-config.ignore-naive-file-lengths=Ignore reported file lengths with Naive strategy
789+
service.io.resource-importer-config.skip-revisited-cen-to-local-links=Skip duplicate CEN-to-LFH entries with JVM strategy
790+
service.io.resource-importer-config.ignore-file-lengths=Ignore reported file lengths with Naive/Standard strategy
791+
service.io.resource-importer-config.adapt-standard-cen-file-names=Adopt CEN file names with Standard strategy
791792
service.io.resource-importer-config.max-embedded-zip-depth=Max embedded zip traversal depth
792793
service.mapping=Mapping
793794
service.mapping.mapping-aggregator-config=Mapping aggregation

recaf-ui/src/main/resources/translations/ja_JP.lang

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,7 @@ service.io.resource-importer-config=アーカイブ形式のインポート
771771
service.io.resource-importer-config.zip-strategy=ZIP の読み込み方法
772772
service.io.resource-importer-config.allow-basic-base-offset-zero-check=JVM 方式での読み込み時に基本オフセットが 0 のチェックを許可する
773773
service.io.resource-importer-config.skip-revisited-cen-to-local-links=JVM 方式での読み込み時に,CEN からローカルへのリンクをスキップする
774-
service.io.resource-importer-config.ignore-naive-file-lengths=Naive 方式での読み込み時に,ファイル長のチェックを無視する
774+
service.io.resource-importer-config.ignore-file-lengths=Naive 方式での読み込み時に,ファイル長のチェックを無視する
775775
service.mapping=マッピング
776776
service.mapping.mapping-aggregator-config=マッピングの集約
777777
service.mapping.mapping-formats-config=マッピングの形式

recaf-ui/src/main/resources/translations/pl_PL.lang

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,7 @@ service.io.recent-workspaces-config.recent-workspaces=Ostatni obszar roboczy
677677
service.io.recent-workspaces-config.last-class-export-path=Ostatnia ścieżka eksportu klasy
678678
service.io.resource-importer-config=Importowanie archiwum
679679
service.io.resource-importer-config.zip-strategy=Strategia parsowania ZIP
680-
service.io.resource-importer-config.skip-revisited-cen-to-local-links=Pomiń duplikaty wpisów CEN-to-LOC ze strategią JVM
680+
service.io.resource-importer-config.skip-revisited-cen-to-local-links=Pomiń duplikaty wpisów CEN-to-LFH ze strategią JVM
681681
service.mapping=Mapowanie
682682
service.mapping.mapping-aggregator-config=Agregacja mapowania
683683
service.mapping.mapping-formats-config=Formaty mapowania

recaf-ui/src/main/resources/translations/zh_CN.lang

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,7 @@ service.io.recent-workspaces-config.recent-workspaces=最近工作空间
695695
service.io.recent-workspaces-config.last-class-export-path=最后一个类的导出路径
696696
service.io.resource-importer-config=归档导入
697697
service.io.resource-importer-config.zip-strategy=ZIP解析策略
698-
service.io.resource-importer-config.skip-revisited-cen-to-local-links=使用JVM策略跳过重复的CEN-to-LOC
698+
service.io.resource-importer-config.skip-revisited-cen-to-local-links=使用JVM策略跳过重复的CEN-to-LFH
699699
service.mapping=映射
700700
service.mapping.mapping-aggregator-config=映射聚合
701701
service.mapping.mapping-formats-config=映射格式

0 commit comments

Comments
 (0)