Auto detect encoding of compressed packs
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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> 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> 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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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> 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> 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();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,14 @@
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import org.jackhuang.hmcl.ui.construct.ComponentList?>
|
||||
<?import org.jackhuang.hmcl.ui.FXUtils?>
|
||||
<?import org.jackhuang.hmcl.ui.construct.SpinnerPane?>
|
||||
<fx:root xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
type="StackPane">
|
||||
<fx:define>
|
||||
<Insets fx:id="insets" bottom="12" />
|
||||
</fx:define>
|
||||
<SpinnerPane fx:id="spinnerPane">
|
||||
<VBox fx:id="borderPane" alignment="CENTER" FXUtils.limitWidth="500">
|
||||
<HBox style="-fx-padding: 0 0 16 5;"><Label text="%modpack.task.install" /></HBox>
|
||||
<ComponentList>
|
||||
@@ -27,4 +29,5 @@
|
||||
</BorderPane>
|
||||
</ComponentList>
|
||||
</VBox>
|
||||
</SpinnerPane>
|
||||
</fx:root>
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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<T> 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<T> extends Task {
|
||||
public void execute() throws Exception {
|
||||
List<ModpackConfiguration.FileInformation> 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))
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T> extends Task {
|
||||
|
||||
private final File modpackFile;
|
||||
private final File dest;
|
||||
private final Charset charset;
|
||||
private final String subDirectory;
|
||||
private final List<ModpackConfiguration.FileInformation> overrides;
|
||||
private final Predicate<String> callback;
|
||||
|
||||
public ModpackInstallTask(File modpackFile, File dest, String subDirectory, Predicate<String> callback, ModpackConfiguration<T> oldConfiguration) {
|
||||
public ModpackInstallTask(File modpackFile, File dest, Charset charset, String subDirectory, Predicate<String> callback, ModpackConfiguration<T> oldConfiguration) {
|
||||
this.modpackFile = modpackFile;
|
||||
this.dest = dest;
|
||||
this.charset = charset;
|
||||
this.subDirectory = subDirectory;
|
||||
this.callback = callback;
|
||||
|
||||
@@ -64,6 +67,7 @@ public class ModpackInstallTask<T> extends Task {
|
||||
.setSubDirectory(subDirectory)
|
||||
.setTerminateIfSubDirectoryNotExists()
|
||||
.setReplaceExistentFile(true)
|
||||
.setEncoding(charset)
|
||||
.setFilter((destPath, isDirectory, zipEntry, entryPath) -> {
|
||||
if (isDirectory) return true;
|
||||
if (!callback.test(entryPath)) return false;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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<Task> dependencies = new LinkedList<>();
|
||||
private final List<Task> 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";
|
||||
|
||||
@@ -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 <V> TaskResult<V> ofResult(String id, Function<AutoTypingMap<String>, V> closure) {
|
||||
public static <V> TaskResult<V> ofResult(String id, ExceptionalFunction<AutoTypingMap<String>, V, ?> closure) {
|
||||
return new TaskCallable2<>(id, closure);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<V> extends TaskResult<V> {
|
||||
|
||||
private final String id;
|
||||
private final Function<AutoTypingMap<String>, V> callable;
|
||||
private final ExceptionalFunction<AutoTypingMap<String>, V, ?> callable;
|
||||
|
||||
public TaskCallable2(String id, Function<AutoTypingMap<String>, V> callable) {
|
||||
public TaskCallable2(String id, ExceptionalFunction<AutoTypingMap<String>, V, ?> callable) {
|
||||
this.id = id;
|
||||
this.callable = callable;
|
||||
}
|
||||
@@ -41,7 +40,7 @@ class TaskCallable2<V> extends TaskResult<V> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
public void execute() throws Exception {
|
||||
setResult(callable.apply(getVariables()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Path>() {
|
||||
@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<Charset> 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<Charset> 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<Charset> 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<String, Object> 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<String> readTextZipEntryQuietly(Path file, String name, Charset encoding) {
|
||||
try {
|
||||
return Optional.of(readTextZipEntry(file, name, encoding));
|
||||
} catch (IOException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user