Fix #5067: 在 MinecraftInstanceTask 中使用 kala-compress 解压文件 (#5151)

This commit is contained in:
Glavo
2026-01-06 22:35:33 +08:00
committed by GitHub
parent 243f3e7371
commit 8b8a62ccf1
4 changed files with 70 additions and 44 deletions

View File

@@ -17,19 +17,22 @@
*/
package org.jackhuang.hmcl.mod;
import kala.compress.archivers.zip.ZipArchiveEntry;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.DigestUtils;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.tree.ArchiveFileTree;
import org.jackhuang.hmcl.util.tree.ZipFileTree;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public final class MinecraftInstanceTask<T> extends Task<ModpackConfiguration<T>> {
@@ -53,26 +56,42 @@ public final class MinecraftInstanceTask<T> extends Task<ModpackConfiguration<T>
this.version = version;
}
private static void getOverrides(List<ModpackConfiguration.FileInformation> overrides,
ZipFileTree tree,
ArchiveFileTree.Dir<ZipArchiveEntry> dir,
List<String> names) throws IOException {
String prefix = String.join("/", names);
if (!prefix.isEmpty())
prefix = prefix + "/";
for (Map.Entry<String, ZipArchiveEntry> entry : dir.getFiles().entrySet()) {
String hash;
try (InputStream input = tree.getInputStream(entry.getValue())) {
hash = DigestUtils.digestToString("SHA-1", input);
}
overrides.add(new ModpackConfiguration.FileInformation(prefix + entry.getKey(), hash));
}
for (ArchiveFileTree.Dir<ZipArchiveEntry> subDir : dir.getSubDirs().values()) {
names.add(subDir.getName());
getOverrides(overrides, tree, subDir, names);
names.remove(names.size() - 1);
}
}
@Override
public void execute() throws Exception {
List<ModpackConfiguration.FileInformation> overrides = new ArrayList<>();
try (FileSystem fs = CompressingUtils.readonly(zipFile).setEncoding(encoding).build()) {
try (var tree = new ZipFileTree(CompressingUtils.openZipFileWithPossibleEncoding(zipFile, encoding))) {
for (String subDirectory : subDirectories) {
Path root = fs.getPath(subDirectory);
if (Files.exists(root))
Files.walkFileTree(root, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String relativePath = root.relativize(file).normalize().toString().replace(File.separatorChar, '/');
overrides.add(new ModpackConfiguration.FileInformation(relativePath, DigestUtils.digestToString("SHA-1", file)));
return FileVisitResult.CONTINUE;
}
});
ArchiveFileTree.Dir<ZipArchiveEntry> root = tree.getDirectory(subDirectory);
if (root == null)
continue;
var names = new ArrayList<String>();
getOverrides(overrides, tree, root, names);
}
}
ModpackConfiguration<T> configuration = new ModpackConfiguration<>(manifest, type, name, version, overrides);
Files.createDirectories(jsonFile.getParent());
JsonUtils.writeToJsonFile(jsonFile, configuration);

View File

@@ -133,12 +133,28 @@ public final class CompressingUtils {
}
public static ZipArchiveReader openZipFile(Path zipFile) throws IOException {
return openZipFileWithPossibleEncoding(zipFile, StandardCharsets.UTF_8);
}
public static ZipArchiveReader openZipFile(Path zipFile, Charset charset) throws IOException {
return new ZipArchiveReader(zipFile, charset);
}
public static ZipArchiveReader openZipFileWithPossibleEncoding(Path zipFile, Charset possibleEncoding) throws IOException {
if (possibleEncoding == null)
possibleEncoding = StandardCharsets.UTF_8;
ZipArchiveReader zipReader = new ZipArchiveReader(Files.newByteChannel(zipFile));
Charset suitableEncoding;
try {
suitableEncoding = findSuitableEncoding(zipReader);
if (possibleEncoding != StandardCharsets.UTF_8 && CompressingUtils.testEncoding(zipReader, possibleEncoding)) {
suitableEncoding = possibleEncoding;
} else {
suitableEncoding = CompressingUtils.findSuitableEncoding(zipReader);
if (suitableEncoding == StandardCharsets.UTF_8)
return zipReader;
}
} catch (Throwable e) {
IOUtils.closeQuietly(zipReader, e);
throw e;
@@ -148,10 +164,6 @@ public final class CompressingUtils {
return new ZipArchiveReader(Files.newByteChannel(zipFile), suitableEncoding);
}
public static ZipArchiveReader openZipFile(Path zipFile, Charset charset) throws IOException {
return new ZipArchiveReader(zipFile, charset);
}
public static final class Builder {
private boolean autoDetectEncoding = false;
private Charset encoding = StandardCharsets.UTF_8;

View File

@@ -80,27 +80,6 @@ public final class Unzipper {
return this;
}
private ZipArchiveReader openReader() throws IOException {
ZipArchiveReader zipReader = new ZipArchiveReader(Files.newByteChannel(zipFile));
Charset suitableEncoding;
try {
if (encoding != StandardCharsets.UTF_8 && CompressingUtils.testEncoding(zipReader, encoding)) {
suitableEncoding = encoding;
} else {
suitableEncoding = CompressingUtils.findSuitableEncoding(zipReader);
if (suitableEncoding == StandardCharsets.UTF_8)
return zipReader;
}
} catch (Throwable e) {
IOUtils.closeQuietly(zipReader, e);
throw e;
}
zipReader.close();
return new ZipArchiveReader(Files.newByteChannel(zipFile), suitableEncoding);
}
/// Decompress the given zip file to a directory.
///
/// @throws IOException if zip file is malformed or filesystem error.
@@ -113,7 +92,7 @@ public final class Unzipper {
: new CopyOption[]{};
long entryCount = 0L;
try (ZipArchiveReader reader = openReader()) {
try (ZipArchiveReader reader = CompressingUtils.openZipFileWithPossibleEncoding(zipFile, encoding)) {
String pathPrefix = StringUtils.addSuffix(subDirectory, "/");
for (ZipArchiveEntry entry : reader.getEntries()) {

View File

@@ -90,6 +90,22 @@ public abstract class ArchiveFileTree<R, E extends ArchiveEntry> implements Clos
}
}
public @Nullable Dir<E> getDirectory(@NotNull String dirPath) {
Dir<E> dir = root;
if (dirPath.isEmpty()) {
return dir;
}
String[] path = dirPath.split("/");
for (String item : path) {
if (item.isEmpty())
continue;
dir = dir.getSubDirs().get(item);
if (dir == null)
return null;
}
return dir;
}
protected void addEntry(E entry) throws IOException {
String[] path = entry.getName().split("/");
List<String> pathList = Arrays.asList(path);