From 17ad6362c3da4a3c4c5dbac666fce80d8cb89fbb Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 14 Feb 2022 23:24:45 +0800 Subject: [PATCH] Use Commons Compress to readModpackManifest --- .../hmcl/game/HMCLModpackManager.java | 8 +-- .../jackhuang/hmcl/game/ModpackHelper.java | 53 +++++++++-------- .../hmcl/mod/curse/CurseManifest.java | 20 +++++-- .../hmcl/mod/mcbbs/McbbsModpackManifest.java | 32 ++++------ .../multimc/MultiMCInstanceConfiguration.java | 59 +++++++++++++------ .../hmcl/mod/multimc/MultiMCManifest.java | 17 +++--- .../mod/server/ServerModpackManifest.java | 6 +- .../hmcl/util/io/CompressingUtils.java | 14 ++++- 8 files changed, 124 insertions(+), 85 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackManager.java index 378d98ff2..6c0ee5bdb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackManager.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.game; import com.google.gson.JsonParseException; +import org.apache.commons.compress.archivers.zip.ZipFile; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.mod.Modpack; import org.jackhuang.hmcl.task.Task; @@ -28,7 +29,6 @@ import org.jackhuang.hmcl.util.io.CompressingUtils; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; -import java.nio.file.Path; /** * @author huangyuhui @@ -46,10 +46,10 @@ public final class HMCLModpackManager { * @throws IOException if the file is not a valid zip file. * @throws JsonParseException if the manifest.json is missing or malformed. */ - public static Modpack readHMCLModpackManifest(Path file, Charset encoding) throws IOException, JsonParseException { - String manifestJson = CompressingUtils.readTextZipEntry(file, "modpack.json", encoding); + public static Modpack readHMCLModpackManifest(ZipFile file, Charset encoding) throws IOException, JsonParseException { + String manifestJson = CompressingUtils.readTextZipEntry(file, "modpack.json"); Modpack manifest = JsonUtils.fromNonNullJson(manifestJson, HMCLModpack.class).setEncoding(encoding); - String gameJson = CompressingUtils.readTextZipEntry(file, "minecraft/pack.json", encoding); + String gameJson = CompressingUtils.readTextZipEntry(file, "minecraft/pack.json"); Version game = JsonUtils.fromNonNullJson(gameJson, Version.class); if (game.getJar() == null) if (StringUtils.isBlank(manifest.getVersion())) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index 64a366234..925615d8e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -19,6 +19,7 @@ package org.jackhuang.hmcl.game; import com.google.gson.JsonParseException; import com.google.gson.reflect.TypeToken; +import org.apache.commons.compress.archivers.zip.ZipFile; import org.jackhuang.hmcl.mod.*; import org.jackhuang.hmcl.mod.curse.CurseCompletionException; import org.jackhuang.hmcl.mod.curse.CurseInstallTask; @@ -61,34 +62,38 @@ public final class ModpackHelper { private ModpackHelper() {} public static Modpack readModpackManifest(Path file, Charset charset) throws UnsupportedModpackException, ManuallyCreatedModpackException { - try { - return McbbsModpackManifest.readManifest(file, charset); - } catch (Exception ignored) { - // ignore it, not a valid MCBBS modpack. - } + try (ZipFile zipFile = CompressingUtils.openZipFile(file, charset)) { + try { + return McbbsModpackManifest.readManifest(zipFile, charset); + } catch (Exception ignored) { + // ignore it, not a valid MCBBS modpack. + } - try { - return CurseManifest.readCurseForgeModpackManifest(file, charset); - } catch (Exception e) { - // ignore it, not a valid CurseForge modpack. - } + try { + return CurseManifest.readCurseForgeModpackManifest(zipFile, charset); + } catch (Exception e) { + // ignore it, not a valid CurseForge modpack. + } - try { - return HMCLModpackManager.readHMCLModpackManifest(file, charset); - } catch (Exception e) { - // ignore it, not a valid HMCL modpack. - } + try { + return HMCLModpackManager.readHMCLModpackManifest(zipFile, charset); + } catch (Exception e) { + // ignore it, not a valid HMCL modpack. + } - try { - return MultiMCInstanceConfiguration.readMultiMCModpackManifest(file, charset); - } catch (Exception e) { - // ignore it, not a valid MultiMC modpack. - } + try { + return MultiMCInstanceConfiguration.readMultiMCModpackManifest(zipFile, file, charset); + } catch (Exception e) { + // ignore it, not a valid MultiMC modpack. + } - try { - return ServerModpackManifest.readManifest(file, charset); - } catch (Exception e) { - // ignore it, not a valid Server modpack. + try { + return ServerModpackManifest.readManifest(zipFile, charset); + } catch (Exception e) { + // ignore it, not a valid Server modpack. + } + + } catch (IOException ignored) { } try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(file, charset)) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifest.java index 57d0a230d..1ecaf0845 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifest.java @@ -19,17 +19,19 @@ package org.jackhuang.hmcl.mod.curse; import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.mod.Modpack; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.CompressingUtils; +import org.jackhuang.hmcl.util.io.IOUtils; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; -import java.nio.file.Path; import java.util.Collections; import java.util.List; @@ -121,11 +123,17 @@ public final class CurseManifest { * @throws JsonParseException if the manifest.json is missing or malformed. * @return the manifest. */ - public static Modpack readCurseForgeModpackManifest(Path zip, Charset encoding) throws IOException, JsonParseException { - String json = CompressingUtils.readTextZipEntry(zip, "manifest.json", encoding); - CurseManifest manifest = JsonUtils.fromNonNullJson(json, CurseManifest.class); - return new Modpack(manifest.getName(), manifest.getAuthor(), manifest.getVersion(), manifest.getMinecraft().getGameVersion(), - CompressingUtils.readTextZipEntryQuietly(zip, "modlist.html", encoding).orElse( "No description"), encoding, manifest) { + public static Modpack readCurseForgeModpackManifest(ZipFile zip, Charset encoding) throws IOException, JsonParseException { + CurseManifest manifest = JsonUtils.fromNonNullJson(CompressingUtils.readTextZipEntry(zip, "manifest.json"), CurseManifest.class); + String description = "No description"; + try { + ZipArchiveEntry modlist = zip.getEntry("modlist.html"); + if (modlist != null) + description = IOUtils.readFullyAsString(zip.getInputStream(modlist)); + } catch (Throwable ignored) { + } + + return new Modpack(manifest.getName(), manifest.getAuthor(), manifest.getVersion(), manifest.getMinecraft().getGameVersion(), description, encoding, manifest) { @Override public Task getInstallTask(DefaultDependencyManager dependencyManager, File zipFile, String name) { return new CurseInstallTask(dependencyManager, zipFile, this, manifest, name); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackManifest.java index 9b8e64ef3..51799856a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackManifest.java @@ -19,24 +19,21 @@ package org.jackhuang.hmcl.mod.mcbbs; import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.game.LaunchOptions; import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.mod.Modpack; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.*; -import org.jackhuang.hmcl.util.io.CompressingUtils; -import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.io.IOUtils; import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.net.URL; import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.nio.file.FileSystem; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -434,8 +431,7 @@ public class McbbsModpackManifest implements Validation { launchOptions.getJavaArguments().addAll(launchInfo.getJavaArguments()); } - private static Modpack fromManifestFile(Path manifestFile, Charset encoding) throws IOException, JsonParseException { - String json = FileUtils.readText(manifestFile, StandardCharsets.UTF_8); + private static Modpack fromManifestFile(String json, Charset encoding) throws IOException, JsonParseException { McbbsModpackManifest manifest = JsonUtils.fromNonNullJson(json, McbbsModpackManifest.class); return manifest.toModpack(encoding); } @@ -447,17 +443,15 @@ public class McbbsModpackManifest implements Validation { * @throws JsonParseException if the server-manifest.json is missing or malformed. * @return the manifest. */ - public static Modpack readManifest(Path zip, Charset encoding) throws IOException, JsonParseException { - try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(zip, encoding)) { - Path mcbbsPackMeta = fs.getPath("mcbbs.packmeta"); - if (Files.exists(mcbbsPackMeta)) { - return fromManifestFile(mcbbsPackMeta, encoding); - } - Path manifestJson = fs.getPath("manifest.json"); - if (Files.exists(manifestJson)) { - return fromManifestFile(manifestJson, encoding); - } - throw new IOException("`mcbbs.packmeta` or `manifest.json` cannot be found"); + public static Modpack readManifest(ZipFile zip, Charset encoding) throws IOException, JsonParseException { + ZipArchiveEntry mcbbsPackMeta = zip.getEntry("mcbbs.packmeta"); + if (mcbbsPackMeta != null) { + return fromManifestFile(IOUtils.readFullyAsString(zip.getInputStream(mcbbsPackMeta)), encoding); } + ZipArchiveEntry manifestJson = zip.getEntry("manifest.json"); + if (manifestJson != null) { + return fromManifestFile(IOUtils.readFullyAsString(zip.getInputStream(manifestJson)), encoding); + } + throw new IOException("`mcbbs.packmeta` or `manifest.json` cannot be found"); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCInstanceConfiguration.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCInstanceConfiguration.java index 502ff395d..223d0b2bd 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCInstanceConfiguration.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCInstanceConfiguration.java @@ -17,11 +17,12 @@ */ package org.jackhuang.hmcl.mod.multimc; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.mod.Modpack; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.Lang; -import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.io.FileUtils; import java.io.File; @@ -30,9 +31,9 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Enumeration; import java.util.Optional; import java.util.Properties; import java.util.stream.Stream; @@ -348,24 +349,44 @@ public final class MultiMCInstanceConfiguration { } } - public static Modpack readMultiMCModpackManifest(Path modpackFile, Charset encoding) throws IOException { - try (FileSystem fs = CompressingUtils.readonly(modpackFile).setEncoding(encoding).build()) { - Path root = getRootPath(fs.getPath("/")); - MultiMCManifest manifest = MultiMCManifest.readMultiMCModpackManifest(root); - String name = FileUtils.getName(root, FileUtils.getNameWithoutExtension(modpackFile)); + public static String getRootEntryName(ZipFile file) throws IOException { + final String instanceFileName = "instance.cfg"; - Path instancePath = root.resolve("instance.cfg"); - if (Files.notExists(instancePath)) - throw new IOException("`instance.cfg` not found, " + modpackFile + " is not a valid MultiMC modpack."); - try (InputStream instanceStream = Files.newInputStream(instancePath)) { - MultiMCInstanceConfiguration cfg = new MultiMCInstanceConfiguration(name, instanceStream, manifest); - return new Modpack(cfg.getName(), "", "", cfg.getGameVersion(), cfg.getNotes(), encoding, cfg) { - @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, File zipFile, String name) { - return new MultiMCModpackInstallTask(dependencyManager, zipFile, this, cfg, name); - } - }; - } + if (file.getEntry(instanceFileName) != null) return ""; + + Enumeration entries = file.getEntries(); + while (entries.hasMoreElements()) { + ZipArchiveEntry entry = entries.nextElement(); + String entryName = entry.getName(); + + int idx = entryName.indexOf('/'); + if (idx >= 0 + && entryName.length() == idx + instanceFileName.length() + 1 + && entryName.startsWith(instanceFileName, idx + 1)) + return entryName.substring(0, idx + 1); + } + + throw new IOException("Not a valid MultiMC modpack"); + } + + public static Modpack readMultiMCModpackManifest(ZipFile modpackFile, Path modpackPath, Charset encoding) throws IOException { + String rootEntryName = getRootEntryName(modpackFile); + MultiMCManifest manifest = MultiMCManifest.readMultiMCModpackManifest(modpackFile, rootEntryName); + + String name = rootEntryName.isEmpty() ? FileUtils.getNameWithoutExtension(modpackPath) : rootEntryName.substring(0, rootEntryName.length() - 1); + ZipArchiveEntry instanceEntry = modpackFile.getEntry(rootEntryName + "instance.cfg"); + + if (instanceEntry == null) + throw new IOException("`instance.cfg` not found, " + modpackFile + " is not a valid MultiMC modpack."); + try (InputStream instanceStream = modpackFile.getInputStream(instanceEntry)) { + MultiMCInstanceConfiguration cfg = new MultiMCInstanceConfiguration(name, instanceStream, manifest); + return new Modpack(cfg.getName(), "", "", cfg.getGameVersion(), cfg.getNotes(), encoding, cfg) { + @Override + public Task getInstallTask(DefaultDependencyManager dependencyManager, File zipFile, String name) { + return new MultiMCModpackInstallTask(dependencyManager, zipFile, this, cfg, name); + } + }; } } + } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCManifest.java index cd7a74ced..54fbb414d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCManifest.java @@ -18,13 +18,13 @@ package org.jackhuang.hmcl.mod.multimc; import com.google.gson.annotations.SerializedName; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.gson.JsonUtils; -import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.io.IOUtils; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.*; @Immutable @@ -51,18 +51,17 @@ public final class MultiMCManifest { /** * Read MultiMC modpack manifest from zip file - * @param root root path in zip file (Path root is a path of ZipFileSystem) + * @param zipFile the zip file * @return the MultiMC modpack manifest. * @throws IOException if zip file is malformed * @throws com.google.gson.JsonParseException if manifest is malformed. */ - public static MultiMCManifest readMultiMCModpackManifest(Path root) throws IOException { - Path mmcPack = root.resolve("mmc-pack.json"); - if (Files.notExists(mmcPack)) + public static MultiMCManifest readMultiMCModpackManifest(ZipFile zipFile, String rootEntryName) throws IOException { + ZipArchiveEntry mmcPack = zipFile.getEntry(rootEntryName + "mmc-pack.json"); + if (mmcPack == null) return null; - String json = FileUtils.readText(mmcPack); + String json = IOUtils.readFullyAsString(zipFile.getInputStream(mmcPack)); MultiMCManifest manifest = JsonUtils.fromNonNullJson(json, MultiMCManifest.class); - if (manifest.getComponents() == null) throw new IOException("mmc-pack.json malformed."); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackManifest.java index be9536904..1d48284b9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackManifest.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.mod.server; import com.google.gson.JsonParseException; +import org.apache.commons.compress.archivers.zip.ZipFile; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.mod.Modpack; import org.jackhuang.hmcl.mod.ModpackConfiguration; @@ -30,7 +31,6 @@ import org.jackhuang.hmcl.util.io.CompressingUtils; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; -import java.nio.file.Path; import java.util.Collections; import java.util.List; @@ -134,8 +134,8 @@ public class ServerModpackManifest implements Validation { * @throws JsonParseException if the server-manifest.json is missing or malformed. * @return the manifest. */ - public static Modpack readManifest(Path zip, Charset encoding) throws IOException, JsonParseException { - String json = CompressingUtils.readTextZipEntry(zip, "server-manifest.json", encoding); + public static Modpack readManifest(ZipFile zip, Charset encoding) throws IOException, JsonParseException { + String json = CompressingUtils.readTextZipEntry(zip, "server-manifest.json"); ServerModpackManifest manifest = JsonUtils.fromNonNullJson(json, ServerModpackManifest.class); return manifest.toModpack(encoding); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/CompressingUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/CompressingUtils.java index 0bdfd365d..1c5b42b7c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/CompressingUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/CompressingUtils.java @@ -217,10 +217,22 @@ public final class CompressingUtils { */ public static String readTextZipEntry(File zipFile, String name) throws IOException { try (ZipFile s = new ZipFile(zipFile)) { - return IOUtils.readFullyAsString(s.getInputStream(s.getEntry(name)), StandardCharsets.UTF_8); + return readTextZipEntry(s, name); } } + /** + * Read the text content of a file in zip. + * + * @param zipFile the zip file + * @param name the location of the text in zip file, something like A/B/C/D.txt + * @throws IOException if the file is not a valid zip file. + * @return the plain text content of given file. + */ + public static String readTextZipEntry(ZipFile zipFile, String name) throws IOException { + return IOUtils.readFullyAsString(zipFile.getInputStream(zipFile.getEntry(name)), StandardCharsets.UTF_8); + } + /** * Read the text content of a file in zip. *