From f5ffa875b82935c2aeb71d43a6f5ccd068799322 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Thu, 13 Dec 2018 00:42:52 +0800 Subject: [PATCH] Auto detect encoding of compressed packs --- .../hmcl/game/HMCLModpackInstallTask.java | 4 +- .../hmcl/game/HMCLModpackManager.java | 12 +- .../jackhuang/hmcl/game/ModpackHelper.java | 24 +-- .../jackhuang/hmcl/ui/LeftPaneController.java | 34 ++-- .../hmcl/ui/download/ModpackPage.java | 46 +++--- .../hmcl/ui/export/ExportWizardProvider.java | 2 + .../jackhuang/hmcl/ui/versions/Versions.java | 31 ++-- .../assets/fxml/download/modpack.fxml | 3 + .../jackhuang/hmcl/mod/CurseInstallTask.java | 8 +- .../org/jackhuang/hmcl/mod/CurseManifest.java | 10 +- .../hmcl/mod/MinecraftInstanceTask.java | 7 +- .../java/org/jackhuang/hmcl/mod/Modpack.java | 20 ++- .../hmcl/mod/ModpackInstallTask.java | 6 +- .../mod/MultiMCInstanceConfiguration.java | 10 +- .../jackhuang/hmcl/mod/MultiMCManifest.java | 7 +- .../hmcl/mod/MultiMCModpackInstallTask.java | 10 +- .../java/org/jackhuang/hmcl/task/Task.java | 8 +- .../jackhuang/hmcl/task/TaskCallable2.java | 9 +- .../hmcl/util/io/CompressingUtils.java | 150 ++++++++++++++++-- .../org/jackhuang/hmcl/util/io/Unzipper.java | 8 +- .../org/jackhuang/hmcl/util/io/Zipper.java | 3 +- 21 files changed, 294 insertions(+), 118 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java index 7b65b12ef..749394c5f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java @@ -73,7 +73,7 @@ public final class HMCLModpackInstallTask extends Task { } } catch (JsonParseException | IOException ignore) { } - dependents.add(new ModpackInstallTask<>(zipFile, run, "/minecraft", it -> !"pack.json".equals(it), config)); + dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), "/minecraft", it -> !"pack.json".equals(it), config)); } @Override @@ -91,7 +91,7 @@ public final class HMCLModpackInstallTask extends Task { String json = CompressingUtils.readTextZipEntry(zipFile, "minecraft/pack.json"); Version version = JsonUtils.GSON.fromJson(json, Version.class).setId(name).setJar(null); dependencies.add(new VersionJsonSaveTask(repository, version)); - dependencies.add(new MinecraftInstanceTask<>(zipFile, "/minecraft", modpack, MODPACK_TYPE, repository.getModpackConfiguration(name))); + dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/minecraft", modpack, MODPACK_TYPE, repository.getModpackConfiguration(name))); } public static final String MODPACK_TYPE = "HMCL"; 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 90aa6a85d..36f9a90b9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackManager.java @@ -24,8 +24,9 @@ import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.gson.JsonUtils; 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.List; /** @@ -75,14 +76,15 @@ public final class HMCLModpackManager { * Read the manifest in a HMCL modpack. * * @param file a HMCL modpack file. + * @param encoding encoding of modpack zip file. * @throws IOException if the file is not a valid zip file. * @throws JsonParseException if the manifest.json is missing or malformed. * @return the manifest of HMCL modpack. */ - public static Modpack readHMCLModpackManifest(File file) throws IOException, JsonParseException { - String manifestJson = CompressingUtils.readTextZipEntry(file, "modpack.json"); - Modpack manifest = JsonUtils.fromNonNullJson(manifestJson, Modpack.class); - String gameJson = CompressingUtils.readTextZipEntry(file, "minecraft/pack.json"); + public static Modpack readHMCLModpackManifest(Path file, Charset encoding) throws IOException, JsonParseException { + String manifestJson = CompressingUtils.readTextZipEntry(file, "modpack.json", encoding); + Modpack manifest = JsonUtils.fromNonNullJson(manifestJson, Modpack.class).setEncoding(encoding); + String gameJson = CompressingUtils.readTextZipEntry(file, "minecraft/pack.json", encoding); 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 3a8863650..b31347e00 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -23,10 +23,8 @@ import org.jackhuang.hmcl.mod.*; import org.jackhuang.hmcl.setting.EnumGameDirectory; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.VersionSetting; -import org.jackhuang.hmcl.task.FinalizedCallback; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; -import org.jackhuang.hmcl.util.AutoTypingMap; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.function.ExceptionalConsumer; import org.jackhuang.hmcl.util.function.ExceptionalRunnable; @@ -36,26 +34,28 @@ import org.jackhuang.hmcl.util.io.FileUtils; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Path; import java.util.Optional; public final class ModpackHelper { private ModpackHelper() {} - public static Modpack readModpackManifest(File file) throws UnsupportedModpackException { + public static Modpack readModpackManifest(Path file, Charset charset) throws UnsupportedModpackException { try { - return CurseManifest.readCurseForgeModpackManifest(file); + return CurseManifest.readCurseForgeModpackManifest(file, charset); } catch (Exception e) { // ignore it, not a valid CurseForge modpack. } try { - return HMCLModpackManager.readHMCLModpackManifest(file); + return HMCLModpackManager.readHMCLModpackManifest(file, charset); } catch (Exception e) { // ignore it, not a valid HMCL modpack. } try { - return MultiMCInstanceConfiguration.readMultiMCModpackManifest(file); + return MultiMCInstanceConfiguration.readMultiMCModpackManifest(file, charset); } catch (Exception e) { // ignore it, not a valid MultiMC modpack. } @@ -109,32 +109,32 @@ public final class ModpackHelper { }; if (modpack.getManifest() instanceof CurseManifest) - return new CurseInstallTask(profile.getDependency(), zipFile, ((CurseManifest) modpack.getManifest()), name) + return new CurseInstallTask(profile.getDependency(), zipFile, modpack, ((CurseManifest) modpack.getManifest()), name) .finalized(Schedulers.defaultScheduler(), ExceptionalConsumer.fromRunnable(success), failure); else if (modpack.getManifest() instanceof HMCLModpackManifest) return new HMCLModpackInstallTask(profile, zipFile, modpack, name) .finalized(Schedulers.defaultScheduler(), ExceptionalConsumer.fromRunnable(success), failure); else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration) - return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name) + return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, ((MultiMCInstanceConfiguration) modpack.getManifest()), name) .finalized(Schedulers.defaultScheduler(), ExceptionalConsumer.fromRunnable(success), failure) .then(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)); else throw new IllegalStateException("Unrecognized modpack: " + modpack); } - public static Task getUpdateTask(Profile profile, File zipFile, String name, ModpackConfiguration configuration) throws UnsupportedModpackException, MismatchedModpackTypeException { - Modpack modpack = ModpackHelper.readModpackManifest(zipFile); + public static Task getUpdateTask(Profile profile, File zipFile, Charset charset, String name, ModpackConfiguration configuration) throws UnsupportedModpackException, MismatchedModpackTypeException { + Modpack modpack = ModpackHelper.readModpackManifest(zipFile.toPath(), charset); switch (configuration.getType()) { case CurseInstallTask.MODPACK_TYPE: if (!(modpack.getManifest() instanceof CurseManifest)) throw new MismatchedModpackTypeException(CurseInstallTask.MODPACK_TYPE, getManifestType(modpack.getManifest())); - return new CurseInstallTask(profile.getDependency(), zipFile, (CurseManifest) modpack.getManifest(), name); + return new CurseInstallTask(profile.getDependency(), zipFile, modpack, (CurseManifest) modpack.getManifest(), name); case MultiMCModpackInstallTask.MODPACK_TYPE: if (!(modpack.getManifest() instanceof MultiMCInstanceConfiguration)) throw new MismatchedModpackTypeException(MultiMCModpackInstallTask.MODPACK_TYPE, getManifestType(modpack.getManifest())); - return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, (MultiMCInstanceConfiguration) modpack.getManifest(), name); + return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, (MultiMCInstanceConfiguration) modpack.getManifest(), name); case HMCLModpackInstallTask.MODPACK_TYPE: if (!(modpack.getManifest() instanceof HMCLModpackManifest)) throw new MismatchedModpackTypeException(HMCLModpackInstallTask.MODPACK_TYPE, getManifestType(modpack.getManifest())); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java index c9c314377..bc248e4d2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java @@ -26,7 +26,6 @@ import org.jackhuang.hmcl.event.RefreshedVersionsEvent; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.ModpackHelper; import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.UnsupportedModpackException; import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; @@ -41,6 +40,7 @@ import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; import org.jackhuang.hmcl.ui.profile.ProfileAdvancedListItem; import org.jackhuang.hmcl.ui.versions.GameAdvancedListItem; import org.jackhuang.hmcl.ui.versions.Versions; +import org.jackhuang.hmcl.util.io.CompressingUtils; import java.io.File; import java.util.concurrent.atomic.AtomicReference; @@ -111,7 +111,6 @@ public final class LeftPaneController extends AdvancedListBox { // ==== private boolean checkedModpack = false; - private static boolean showNewAccount = true; private void onRefreshedVersions(HMCLGameRepository repository) { JFXUtilities.runInFX(() -> { @@ -121,27 +120,24 @@ public final class LeftPaneController extends AdvancedListBox { if (repository.getVersionCount() == 0) { File modpackFile = new File("modpack.zip").getAbsoluteFile(); if (modpackFile.exists()) { - try { - AtomicReference region = new AtomicReference<>(); - Modpack modpack = ModpackHelper.readModpackManifest(modpackFile); - TaskExecutor executor = ModpackHelper.getInstallTask(repository.getProfile(), modpackFile, modpack.getName(), modpack) - .with(Task.of(Schedulers.javafx(), () -> { - region.get().fireEvent(new DialogCloseEvent()); - checkAccount(); - })).executor(); - region.set(Controllers.taskDialog(executor, i18n("modpack.installing"), "")); - executor.start(); - showNewAccount = false; - } catch (UnsupportedModpackException ignore) { - } + Task.ofResult("encoding", () -> CompressingUtils.findSuitableEncoding(modpackFile.toPath())) + .then(Task.ofResult("modpack", var -> ModpackHelper.readModpackManifest(modpackFile.toPath(), var.get("encoding")))) + .then(Task.of(var -> { + AtomicReference region = new AtomicReference<>(); + Modpack modpack = var.get("modpack"); + TaskExecutor executor = ModpackHelper.getInstallTask(repository.getProfile(), modpackFile, modpack.getName(), modpack) + .with(Task.of(Schedulers.javafx(), () -> { + region.get().fireEvent(new DialogCloseEvent()); + checkAccount(); + })).executor(); + region.set(Controllers.taskDialog(executor, i18n("modpack.installing"), "")); + executor.start(); + })).start(); } } } - if (showNewAccount) { - showNewAccount = false; - checkAccount(); - } + checkAccount(); }); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java index bfcb448de..8079c553d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java @@ -27,16 +27,19 @@ import javafx.scene.layout.StackPane; import javafx.stage.FileChooser; import org.jackhuang.hmcl.game.ModpackHelper; import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.UnsupportedModpackException; import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.task.Schedulers; +import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.WebStage; import org.jackhuang.hmcl.ui.construct.MessageBox; +import org.jackhuang.hmcl.ui.construct.SpinnerPane; import org.jackhuang.hmcl.ui.construct.Validator; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.util.StringUtils; +import org.jackhuang.hmcl.util.io.CompressingUtils; import java.io.File; import java.util.Map; @@ -71,6 +74,9 @@ public final class ModpackPage extends StackPane implements WizardPage { @FXML private JFXButton btnInstall; + @FXML + private SpinnerPane spinnerPane; + public ModpackPage(WizardController controller) { this.controller = controller; @@ -96,24 +102,28 @@ public final class ModpackPage extends StackPane implements WizardPage { controller.getSettings().put(MODPACK_FILE, selectedFile); } - try { - manifest = ModpackHelper.readModpackManifest(selectedFile); - controller.getSettings().put(MODPACK_MANIFEST, manifest); - lblName.setText(manifest.getName()); - lblVersion.setText(manifest.getVersion()); - lblAuthor.setText(manifest.getAuthor()); - txtModpackName.setText(manifest.getName() + (StringUtils.isBlank(manifest.getVersion()) ? "" : "-" + manifest.getVersion())); + spinnerPane.showSpinner(); + Task.ofResult("encoding", () -> CompressingUtils.findSuitableEncoding(selectedFile.toPath())) + .then(Task.ofResult("manifest", var -> + manifest = ModpackHelper.readModpackManifest(selectedFile.toPath(), var.get("encoding")))) + .finalized(Schedulers.javafx(), var -> { + spinnerPane.hideSpinner(); + controller.getSettings().put(MODPACK_MANIFEST, manifest); + lblName.setText(manifest.getName()); + lblVersion.setText(manifest.getVersion()); + lblAuthor.setText(manifest.getAuthor()); + txtModpackName.setText(manifest.getName() + (StringUtils.isBlank(manifest.getVersion()) ? "" : "-" + manifest.getVersion())); - lblModpackLocation.setText(selectedFile.getAbsolutePath()); - txtModpackName.getValidators().addAll( - new Validator(i18n("install.new_game.already_exists"), str -> !profile.getRepository().hasVersion(str) && StringUtils.isNotBlank(str)), - new Validator(i18n("version.forbidden_name"), str -> !profile.getRepository().forbidsVersion(str)) - ); - txtModpackName.textProperty().addListener(e -> btnInstall.setDisable(!txtModpackName.validate())); - } catch (UnsupportedModpackException e) { - Controllers.dialog(i18n("modpack.task.install.error"), i18n("message.error"), MessageBox.ERROR_MESSAGE); - Platform.runLater(controller::onEnd); - } + lblModpackLocation.setText(selectedFile.getAbsolutePath()); + txtModpackName.getValidators().addAll( + new Validator(i18n("install.new_game.already_exists"), str -> !profile.getRepository().hasVersion(str) && StringUtils.isNotBlank(str)), + new Validator(i18n("version.forbidden_name"), str -> !profile.getRepository().forbidsVersion(str)) + ); + txtModpackName.textProperty().addListener(e -> btnInstall.setDisable(!txtModpackName.validate())); + }, e -> { + Controllers.dialog(i18n("modpack.task.install.error"), i18n("message.error"), MessageBox.ERROR_MESSAGE); + Platform.runLater(controller::onEnd); + }).start(); } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java index 34e15f28f..ceaa5c853 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java @@ -31,6 +31,7 @@ import org.jackhuang.hmcl.ui.wizard.WizardProvider; import org.jackhuang.hmcl.util.io.Zipper; import java.io.File; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.Collection; import java.util.Collections; @@ -74,6 +75,7 @@ public final class ExportWizardProvider implements WizardProvider { (String) settings.get(ModpackInfoPage.MODPACK_VERSION), null, (String) settings.get(ModpackInfoPage.MODPACK_DESCRIPTION), + StandardCharsets.UTF_8, null ), tempModpack); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index 79fae2182..3095c07e0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -36,6 +36,7 @@ import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; import org.jackhuang.hmcl.ui.construct.MessageBox; import org.jackhuang.hmcl.ui.export.ExportWizardProvider; import org.jackhuang.hmcl.util.Logging; +import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.OperatingSystem; @@ -86,19 +87,23 @@ public class Versions { chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("modpack"), "*.zip")); File selectedFile = chooser.showOpenDialog(Controllers.getStage()); if (selectedFile != null) { - AtomicReference region = new AtomicReference<>(); - try { - TaskExecutor executor = ModpackHelper.getUpdateTask(profile, selectedFile, version, ModpackHelper.readModpackConfiguration(profile.getRepository().getModpackConfiguration(version))) - .then(Task.of(Schedulers.javafx(), () -> region.get().fireEvent(new DialogCloseEvent()))).executor(); - region.set(Controllers.taskDialog(executor, i18n("modpack.update"), "")); - executor.start(); - } catch (UnsupportedModpackException e) { - Controllers.dialog(i18n("modpack.unsupported"), i18n("message.error"), MessageBox.ERROR_MESSAGE); - } catch (MismatchedModpackTypeException e) { - Controllers.dialog(i18n("modpack.mismatched_type"), i18n("message.error"), MessageBox.ERROR_MESSAGE); - } catch (IOException e) { - Controllers.dialog(i18n("modpack.invalid"), i18n("message.error"), MessageBox.ERROR_MESSAGE); - } + Task.ofResult("encoding", () -> CompressingUtils.findSuitableEncoding(selectedFile.toPath())) + .then(Task.of(Schedulers.javafx(), var -> { + AtomicReference region = new AtomicReference<>(); + try { + TaskExecutor executor = ModpackHelper.getUpdateTask(profile, selectedFile, var.get("encoding"), version, ModpackHelper.readModpackConfiguration(profile.getRepository().getModpackConfiguration(version))) + .then(Task.of(Schedulers.javafx(), () -> region.get().fireEvent(new DialogCloseEvent()))).executor(); + region.set(Controllers.taskDialog(executor, i18n("modpack.update"), "")); + executor.start(); + } catch (UnsupportedModpackException e) { + Controllers.dialog(i18n("modpack.unsupported"), i18n("message.error"), MessageBox.ERROR_MESSAGE); + } catch (MismatchedModpackTypeException e) { + Controllers.dialog(i18n("modpack.mismatched_type"), i18n("message.error"), MessageBox.ERROR_MESSAGE); + } catch (IOException e) { + Controllers.dialog(i18n("modpack.invalid"), i18n("message.error"), MessageBox.ERROR_MESSAGE); + } + })).start(); + } } diff --git a/HMCL/src/main/resources/assets/fxml/download/modpack.fxml b/HMCL/src/main/resources/assets/fxml/download/modpack.fxml index 1875f0ab2..0a2d6feb4 100644 --- a/HMCL/src/main/resources/assets/fxml/download/modpack.fxml +++ b/HMCL/src/main/resources/assets/fxml/download/modpack.fxml @@ -7,12 +7,14 @@ + + @@ -27,4 +29,5 @@ + diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseInstallTask.java index 2a06d4129..8d886fe14 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseInstallTask.java @@ -42,6 +42,7 @@ public final class CurseInstallTask extends Task { private final DefaultDependencyManager dependencyManager; private final DefaultGameRepository repository; private final File zipFile; + private final Modpack modpack; private final CurseManifest manifest; private final String name; private final File run; @@ -58,9 +59,10 @@ public final class CurseInstallTask extends Task { * @param name the new version name * @see CurseManifest#readCurseForgeModpackManifest */ - public CurseInstallTask(DefaultDependencyManager dependencyManager, File zipFile, CurseManifest manifest, String name) { + public CurseInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, CurseManifest manifest, String name) { this.dependencyManager = dependencyManager; this.zipFile = zipFile; + this.modpack = modpack; this.manifest = manifest; this.name = name; this.repository = dependencyManager.getGameRepository(); @@ -92,7 +94,7 @@ public final class CurseInstallTask extends Task { } catch (JsonParseException | IOException ignore) { } this.config = config; - dependents.add(new ModpackInstallTask<>(zipFile, run, manifest.getOverrides(), any -> true, config)); + dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), manifest.getOverrides(), any -> true, config)); } @Override @@ -121,7 +123,7 @@ public final class CurseInstallTask extends Task { FileUtils.writeText(new File(root, "manifest.json"), JsonUtils.GSON.toJson(manifest)); dependencies.add(new CurseCompletionTask(dependencyManager, name, manifest)); - dependencies.add(new MinecraftInstanceTask<>(zipFile, manifest.getOverrides(), manifest, MODPACK_TYPE, repository.getModpackConfiguration(name))); + dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), manifest.getOverrides(), manifest, MODPACK_TYPE, repository.getModpackConfiguration(name))); } public static final String MODPACK_TYPE = "Curse"; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseManifest.java index 78d4e2dc7..ce4c5072b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseManifest.java @@ -19,13 +19,13 @@ package org.jackhuang.hmcl.mod; import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; - import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.gson.JsonUtils; 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; @@ -117,11 +117,11 @@ public final class CurseManifest { * @throws JsonParseException if the manifest.json is missing or malformed. * @return the manifest. */ - public static Modpack readCurseForgeModpackManifest(File f) throws IOException, JsonParseException { - String json = CompressingUtils.readTextZipEntry(f, "manifest.json"); + 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(f, "modlist.html").orElse( "No description"), manifest); + CompressingUtils.readTextZipEntryQuietly(zip, "modlist.html", encoding).orElse( "No description"), encoding, manifest); } public static final String MINECRAFT_MODPACK = "minecraftModpack"; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MinecraftInstanceTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MinecraftInstanceTask.java index 1427d861e..9dbce7a0a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MinecraftInstanceTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MinecraftInstanceTask.java @@ -24,6 +24,7 @@ import org.jackhuang.hmcl.util.io.FileUtils; import java.io.File; import java.io.IOException; +import java.nio.charset.Charset; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.LinkedList; @@ -35,13 +36,15 @@ import static org.jackhuang.hmcl.util.Hex.encodeHex; public final class MinecraftInstanceTask extends Task { private final File zipFile; + private final Charset encoding; private final String subDirectory; private final File jsonFile; private final T manifest; private final String type; - public MinecraftInstanceTask(File zipFile, String subDirectory, T manifest, String type, File jsonFile) { + public MinecraftInstanceTask(File zipFile, Charset encoding, String subDirectory, T manifest, String type, File jsonFile) { this.zipFile = zipFile; + this.encoding = encoding; this.subDirectory = FileUtils.normalizePath(subDirectory); this.manifest = manifest; this.jsonFile = jsonFile; @@ -55,7 +58,7 @@ public final class MinecraftInstanceTask extends Task { public void execute() throws Exception { List overrides = new LinkedList<>(); - try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(zipFile.toPath())) { + try (FileSystem fs = CompressingUtils.readonly(zipFile.toPath()).setEncoding(encoding).build()) { Path root = fs.getPath(subDirectory); if (Files.exists(root)) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Modpack.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Modpack.java index 1044fd060..7128e572c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Modpack.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Modpack.java @@ -17,6 +17,8 @@ */ package org.jackhuang.hmcl.mod; +import java.nio.charset.Charset; + /** * * @author huangyuhui @@ -27,18 +29,20 @@ public final class Modpack { private final String version; private final String gameVersion; private final String description; + private final transient Charset encoding; private final Object manifest; public Modpack() { - this("", null, null, null, null, null); + this("", null, null, null, null, null, null); } - public Modpack(String name, String author, String version, String gameVersion, String description, Object manifest) { + public Modpack(String name, String author, String version, String gameVersion, String description, Charset encoding, Object manifest) { this.name = name; this.author = author; this.version = version; this.gameVersion = gameVersion; this.description = description; + this.encoding = encoding; this.manifest = manifest; } @@ -59,18 +63,26 @@ public final class Modpack { } public Modpack setGameVersion(String gameVersion) { - return new Modpack(name, author, version, gameVersion, description, manifest); + return new Modpack(name, author, version, gameVersion, description, encoding, manifest); } public String getDescription() { return description; } + public Charset getEncoding() { + return encoding; + } + + public Modpack setEncoding(Charset encoding) { + return new Modpack(name, author, version, gameVersion, description, encoding, manifest); + } + public Object getManifest() { return manifest; } public Modpack setManifest(Object manifest) { - return new Modpack(name, author, version, gameVersion, description, manifest); + return new Modpack(name, author, version, gameVersion, description, encoding, manifest); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackInstallTask.java index fb0f57e97..9c2ee133c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackInstallTask.java @@ -23,6 +23,7 @@ import org.jackhuang.hmcl.util.io.Unzipper; import java.io.File; import java.io.IOException; +import java.nio.charset.Charset; import java.nio.file.Files; import java.util.*; import java.util.function.Predicate; @@ -34,13 +35,15 @@ public class ModpackInstallTask extends Task { private final File modpackFile; private final File dest; + private final Charset charset; private final String subDirectory; private final List overrides; private final Predicate callback; - public ModpackInstallTask(File modpackFile, File dest, String subDirectory, Predicate callback, ModpackConfiguration oldConfiguration) { + public ModpackInstallTask(File modpackFile, File dest, Charset charset, String subDirectory, Predicate callback, ModpackConfiguration oldConfiguration) { this.modpackFile = modpackFile; this.dest = dest; + this.charset = charset; this.subDirectory = subDirectory; this.callback = callback; @@ -64,6 +67,7 @@ public class ModpackInstallTask extends Task { .setSubDirectory(subDirectory) .setTerminateIfSubDirectoryNotExists() .setReplaceExistentFile(true) + .setEncoding(charset) .setFilter((destPath, isDirectory, zipEntry, entryPath) -> { if (isDirectory) return true; if (!callback.test(entryPath)) return false; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCInstanceConfiguration.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCInstanceConfiguration.java index 069c832d4..faf4fed33 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCInstanceConfiguration.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCInstanceConfiguration.java @@ -21,9 +21,9 @@ import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.CompressingUtils; -import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.Charset; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; @@ -264,9 +264,9 @@ public final class MultiMCInstanceConfiguration { return mmcPack; } - public static Modpack readMultiMCModpackManifest(File modpackFile) throws IOException { - MultiMCManifest manifest = MultiMCManifest.readMultiMCModpackManifest(modpackFile); - try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modpackFile.toPath())) { + public static Modpack readMultiMCModpackManifest(Path modpackFile, Charset encoding) throws IOException { + MultiMCManifest manifest = MultiMCManifest.readMultiMCModpackManifest(modpackFile, encoding); + try (FileSystem fs = CompressingUtils.readonly(modpackFile).setEncoding(encoding).build()) { Path root = Files.list(fs.getPath("/")).filter(Files::isDirectory).findAny() .orElseThrow(() -> new IOException("Not a valid MultiMC modpack")); String name = StringUtils.removeSuffix(root.normalize().getFileName().toString(), "/"); @@ -275,7 +275,7 @@ public final class MultiMCInstanceConfiguration { if (Files.notExists(instancePath)) throw new IOException("`instance.cfg` not found, " + modpackFile + " is not a valid MultiMC modpack."); MultiMCInstanceConfiguration cfg = new MultiMCInstanceConfiguration(name, Files.newInputStream(instancePath), manifest); - return new Modpack(cfg.getName(), "", "", cfg.getGameVersion(), cfg.getNotes(), cfg); + return new Modpack(cfg.getName(), "", "", cfg.getGameVersion(), cfg.getNotes(), encoding, cfg); } } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCManifest.java index 13c11ec6e..1738dbfd4 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCManifest.java @@ -18,14 +18,13 @@ package org.jackhuang.hmcl.mod; import com.google.gson.annotations.SerializedName; - 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.FileSystem; import java.nio.file.Files; import java.nio.file.Path; @@ -53,8 +52,8 @@ public final class MultiMCManifest { return components; } - public static MultiMCManifest readMultiMCModpackManifest(File zipFile) throws IOException { - try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(zipFile.toPath())) { + public static MultiMCManifest readMultiMCModpackManifest(Path zipFile, Charset encoding) throws IOException { + try (FileSystem fs = CompressingUtils.readonly(zipFile).setEncoding(encoding).build()) { Path root = Files.list(fs.getPath("/")).filter(Files::isDirectory).findAny() .orElseThrow(() -> new IOException("Not a valid MultiMC modpack")); Path mmcPack = root.resolve("mmc-pack.json"); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCModpackInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCModpackInstallTask.java index bafba55ca..c7a029fb9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCModpackInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCModpackInstallTask.java @@ -26,7 +26,7 @@ import org.jackhuang.hmcl.game.Arguments; import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.Task; -import org.jackhuang.hmcl.util.*; +import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.io.FileUtils; @@ -49,14 +49,16 @@ import java.util.Optional; public final class MultiMCModpackInstallTask extends Task { private final File zipFile; + private final Modpack modpack; private final MultiMCInstanceConfiguration manifest; private final String name; private final DefaultGameRepository repository; private final List dependencies = new LinkedList<>(); private final List dependents = new LinkedList<>(); - public MultiMCModpackInstallTask(DefaultDependencyManager dependencyManager, File zipFile, MultiMCInstanceConfiguration manifest, String name) { + public MultiMCModpackInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, MultiMCInstanceConfiguration manifest, String name) { this.zipFile = zipFile; + this.modpack = modpack; this.manifest = manifest; this.name = name; this.repository = dependencyManager.getGameRepository(); @@ -100,7 +102,7 @@ public final class MultiMCModpackInstallTask extends Task { } catch (JsonParseException | IOException ignore) { } - dependents.add(new ModpackInstallTask<>(zipFile, run, "/" + manifest.getName() + "/minecraft", any -> true, config)); + dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), "/" + manifest.getName() + "/minecraft", any -> true, config)); } @Override @@ -141,7 +143,7 @@ public final class MultiMCModpackInstallTask extends Task { } dependencies.add(new VersionJsonSaveTask(repository, version)); - dependencies.add(new MinecraftInstanceTask<>(zipFile, "/" + manifest.getName() + "/minecraft", manifest, MODPACK_TYPE, repository.getModpackConfiguration(name))); + dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/" + manifest.getName() + "/minecraft", manifest, MODPACK_TYPE, repository.getModpackConfiguration(name))); } public static final String MODPACK_TYPE = "MultiMC"; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java index b08f790b0..b1e6dee6a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java @@ -23,7 +23,10 @@ import javafx.beans.property.ReadOnlyDoubleWrapper; import javafx.beans.property.ReadOnlyStringProperty; import javafx.beans.property.ReadOnlyStringWrapper; import org.jackhuang.hmcl.event.EventManager; -import org.jackhuang.hmcl.util.*; +import org.jackhuang.hmcl.util.AutoTypingMap; +import org.jackhuang.hmcl.util.InvocationDispatcher; +import org.jackhuang.hmcl.util.Logging; +import org.jackhuang.hmcl.util.ReflectionHelper; import org.jackhuang.hmcl.util.function.ExceptionalConsumer; import org.jackhuang.hmcl.util.function.ExceptionalFunction; import org.jackhuang.hmcl.util.function.ExceptionalRunnable; @@ -31,7 +34,6 @@ import org.jackhuang.hmcl.util.function.ExceptionalRunnable; import java.util.Collection; import java.util.Collections; import java.util.concurrent.Callable; -import java.util.function.Function; import java.util.logging.Level; /** @@ -363,7 +365,7 @@ public abstract class Task { return new TaskCallable<>(id, callable); } - public static TaskResult ofResult(String id, Function, V> closure) { + public static TaskResult ofResult(String id, ExceptionalFunction, V, ?> closure) { return new TaskCallable2<>(id, closure); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskCallable2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskCallable2.java index 26669c5cc..b41801030 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskCallable2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskCallable2.java @@ -18,8 +18,7 @@ package org.jackhuang.hmcl.task; import org.jackhuang.hmcl.util.AutoTypingMap; - -import java.util.function.Function; +import org.jackhuang.hmcl.util.function.ExceptionalFunction; /** * @@ -28,9 +27,9 @@ import java.util.function.Function; class TaskCallable2 extends TaskResult { private final String id; - private final Function, V> callable; + private final ExceptionalFunction, V, ?> callable; - public TaskCallable2(String id, Function, V> callable) { + public TaskCallable2(String id, ExceptionalFunction, V, ?> callable) { this.id = id; this.callable = callable; } @@ -41,7 +40,7 @@ class TaskCallable2 extends TaskResult { } @Override - public void execute() { + public void execute() throws Exception { setResult(callable.apply(getVariables())); } } 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 651eed437..0a9f4faa7 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 @@ -17,14 +17,20 @@ */ package org.jackhuang.hmcl.util.io; +import org.jetbrains.annotations.NotNull; + import java.io.File; import java.io.IOException; -import java.nio.file.FileSystem; -import java.nio.file.Path; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.spi.FileSystemProvider; +import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.ZipError; import java.util.zip.ZipException; @@ -43,28 +49,127 @@ public final class CompressingUtils { private CompressingUtils() { } + @NotNull + private static FileVisitResult testZipPath(Path file, Path root, AtomicBoolean result) { + try { + root.relativize(file).toString(); // throw IllegalArgumentException for wrong encoding. + return FileVisitResult.CONTINUE; + } catch (Exception e) { + result.set(false); + return FileVisitResult.TERMINATE; + } + } + + public static boolean testEncoding(Path zipFile, Charset encoding) throws IOException { + AtomicBoolean result = new AtomicBoolean(true); + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(zipFile, encoding)) { + Path root = fs.getPath("/"); + Files.walkFileTree(root, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, + BasicFileAttributes attrs) { + return testZipPath(file, root, result); + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, + BasicFileAttributes attrs) { + return testZipPath(dir, root, result); + } + }); + } + return result.get(); + } + + public static Charset findSuitableEncoding(Path zipFile) throws IOException { + return findSuitableEncoding(zipFile, Charset.availableCharsets().values()); + } + + public static Charset findSuitableEncoding(Path zipFile, Collection candidates) throws IOException { + if (testEncoding(zipFile, StandardCharsets.UTF_8)) return StandardCharsets.UTF_8; + if (testEncoding(zipFile, Charset.defaultCharset())) return Charset.defaultCharset(); + + for (Charset charset : candidates) + if (charset != null && testEncoding(zipFile, charset)) + return charset; + throw new IOException("Cannot find suitable encoding for the zip."); + } + + public static final class Builder { + private boolean autoDetectEncoding = false; + private Collection charsetCandidates; + private Charset encoding = StandardCharsets.UTF_8; + private boolean useTempFile = false; + private final boolean create; + private final Path zip; + + public Builder(Path zip, boolean create) { + this.zip = zip; + this.create = create; + } + + public Builder setAutoDetectEncoding(boolean autoDetectEncoding) { + this.autoDetectEncoding = autoDetectEncoding; + return this; + } + + public Builder setCharsetCandidates(Collection charsetCandidates) { + this.charsetCandidates = charsetCandidates; + return this; + } + + public Builder setEncoding(Charset encoding) { + this.encoding = encoding; + return this; + } + + public Builder setUseTempFile(boolean useTempFile) { + this.useTempFile = useTempFile; + return this; + } + + public FileSystem build() throws IOException { + if (autoDetectEncoding) { + if (!testEncoding(zip, encoding)) { + if (charsetCandidates == null) + charsetCandidates = Charset.availableCharsets().values(); + encoding = findSuitableEncoding(zip, charsetCandidates); + } + } + return createZipFileSystem(zip, create, useTempFile, encoding); + } + } + + public static Builder readonly(Path zipFile) { + return new Builder(zipFile, false); + } + + public static Builder writable(Path zipFile) { + return new Builder(zipFile, true).setUseTempFile(true); + } + public static FileSystem createReadOnlyZipFileSystem(Path zipFile) throws IOException { return createReadOnlyZipFileSystem(zipFile, null); } - public static FileSystem createReadOnlyZipFileSystem(Path zipFile, String encoding) throws IOException { - return createZipFileSystem(zipFile, false, false, encoding); + public static FileSystem createReadOnlyZipFileSystem(Path zipFile, Charset charset) throws IOException { + return createZipFileSystem(zipFile, false, false, charset); } public static FileSystem createWritableZipFileSystem(Path zipFile) throws IOException { return createWritableZipFileSystem(zipFile, null); } - public static FileSystem createWritableZipFileSystem(Path zipFile, String encoding) throws IOException { - return createZipFileSystem(zipFile, true, true, encoding); + public static FileSystem createWritableZipFileSystem(Path zipFile, Charset charset) throws IOException { + return createZipFileSystem(zipFile, true, true, charset); } - public static FileSystem createZipFileSystem(Path zipFile, boolean create, boolean useTempFile, String encoding) throws IOException { + public static FileSystem createZipFileSystem(Path zipFile, boolean create, boolean useTempFile, Charset encoding) throws IOException { Map env = new HashMap<>(); if (create) env.put("create", "true"); if (encoding != null) - env.put("encoding", encoding); + env.put("encoding", encoding.name()); if (useTempFile) env.put("useTempFile", true); try { @@ -86,7 +191,19 @@ public final class CompressingUtils { * @return the plain text content of given file. */ public static String readTextZipEntry(File zipFile, String name) throws IOException { - try (FileSystem fs = createReadOnlyZipFileSystem(zipFile.toPath())) { + return readTextZipEntry(zipFile.toPath(), name, null); + } + + /** + * 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(Path zipFile, String name, Charset encoding) throws IOException { + try (FileSystem fs = createReadOnlyZipFileSystem(zipFile, encoding)) { return FileUtils.readText(fs.getPath(name)); } } @@ -105,4 +222,19 @@ public final class CompressingUtils { return Optional.empty(); } } + + /** + * Read the text content of a file in zip. + * + * @param file the zip file + * @param name the location of the text in zip file, something like A/B/C/D.txt + * @return the plain text content of given file. + */ + public static Optional readTextZipEntryQuietly(Path file, String name, Charset encoding) { + try { + return Optional.of(readTextZipEntry(file, name, encoding)); + } catch (IOException e) { + return Optional.empty(); + } + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/Unzipper.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/Unzipper.java index 54a9ab61b..be86f54a2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/Unzipper.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/Unzipper.java @@ -19,6 +19,8 @@ package org.jackhuang.hmcl.util.io; import java.io.File; import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; @@ -28,7 +30,7 @@ public class Unzipper { private boolean terminateIfSubDirectoryNotExists = false; private String subDirectory = "/"; private FileFilter filter = null; - private String encoding; + private Charset encoding = StandardCharsets.UTF_8; /** * Decompress the given zip file to a directory. @@ -82,7 +84,7 @@ public class Unzipper { return this; } - public Unzipper setEncoding(String encoding) { + public Unzipper setEncoding(Charset encoding) { this.encoding = encoding; return this; } @@ -99,7 +101,7 @@ public class Unzipper { */ public void unzip() throws IOException { Files.createDirectories(dest); - try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(zipFile, encoding)) { + try (FileSystem fs = CompressingUtils.readonly(zipFile).setEncoding(encoding).setAutoDetectEncoding(true).build()) { Path root = fs.getPath(subDirectory); if (!root.isAbsolute() || (subDirectory.length() > 1 && subDirectory.endsWith("/"))) throw new IllegalArgumentException("Subdirectory for unzipper must be absolute"); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/Zipper.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/Zipper.java index 740284765..3f4b038f8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/Zipper.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/Zipper.java @@ -22,6 +22,7 @@ import java.io.Closeable; 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.function.Predicate; @@ -39,7 +40,7 @@ public final class Zipper implements Closeable { this(zipFile, null); } - public Zipper(Path zipFile, String encoding) throws IOException { + public Zipper(Path zipFile, Charset encoding) throws IOException { Files.deleteIfExists(zipFile); fs = CompressingUtils.createWritableZipFileSystem(zipFile, encoding); }