Use Commons Compress to readModpackManifest

This commit is contained in:
Glavo 2022-02-14 23:24:45 +08:00 committed by Yuhui Huang
parent 0f22c9e31a
commit 17ad6362c3
8 changed files with 124 additions and 85 deletions

View File

@ -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()))

View File

@ -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)) {

View File

@ -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);

View File

@ -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");
}
}

View File

@ -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<ZipArchiveEntry> 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);
}
};
}
}
}

View File

@ -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.");

View File

@ -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);
}

View File

@ -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.
*