diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java index 9942d6f27..5d89682c4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java @@ -21,6 +21,7 @@ import javafx.scene.Node; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; +import org.jackhuang.hmcl.download.VersionMismatchException; import org.jackhuang.hmcl.download.game.LibraryDownloadException; import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask; import org.jackhuang.hmcl.game.Library; @@ -143,6 +144,11 @@ public final class InstallerWizardProvider implements WizardProvider { } } else if (exception instanceof OptiFineInstallTask.UnsupportedOptiFineInstallationException) { Controllers.dialog(i18n("install.failed.optifine_conflict"), i18n("install.failed"), MessageType.ERROR, next); + } else if (exception instanceof UnsupportedOperationException) { + Controllers.dialog(i18n("install.failed.install_online"), i18n("install.failed"), MessageType.ERROR, next); + } else if (exception instanceof VersionMismatchException) { + VersionMismatchException e = ((VersionMismatchException) exception); + Controllers.dialog(i18n("install.failed.version_mismatch", e.getExpect(), e.getActual()), i18n("install.failed"), MessageType.ERROR, next); } else { Controllers.dialog(StringUtils.getStackTrace(exception), i18n("install.failed"), MessageType.ERROR, next); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java index f402d0e21..7951344ed 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java @@ -17,6 +17,9 @@ */ package org.jackhuang.hmcl.ui.versions; +import javafx.scene.Node; +import javafx.scene.control.Skin; +import javafx.stage.FileChooser; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.MaintainTask; import org.jackhuang.hmcl.download.game.VersionJsonSaveTask; @@ -26,25 +29,42 @@ import org.jackhuang.hmcl.game.Version; 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.InstallerItem; -import org.jackhuang.hmcl.ui.ListPage; +import org.jackhuang.hmcl.task.TaskExecutor; +import org.jackhuang.hmcl.task.TaskListener; +import org.jackhuang.hmcl.ui.*; import org.jackhuang.hmcl.ui.download.InstallerWizardProvider; import org.jackhuang.hmcl.ui.download.UpdateInstallerWizardProvider; +import org.jackhuang.hmcl.util.io.FileUtils; +import java.io.File; +import java.util.Arrays; import java.util.LinkedList; +import java.util.List; import java.util.function.Consumer; import java.util.function.Function; import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*; +import static org.jackhuang.hmcl.ui.FXUtils.runInFX; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public class InstallerListPage extends ListPage { +public class InstallerListPage extends ListPageBase { private Profile profile; private String versionId; private Version version; private String gameVersion; + { + FXUtils.applyDragListener(this, it -> Arrays.asList("jar", "exe").contains(FileUtils.getExtension(it)), mods -> { + if (!mods.isEmpty()) + doInstallOffline(mods.get(0)); + }); + } + + @Override + protected Skin createDefaultSkin() { + return new InstallerListPageSkin(); + } + public void loadVersion(Profile profile, String versionId) { this.profile = profile; this.versionId = versionId; @@ -83,11 +103,54 @@ public class InstallerListPage extends ListPage { }).start(); } - @Override - public void add() { + public void installOnline() { if (gameVersion == null) Controllers.dialog(i18n("version.cannot_read")); else Controllers.getDecorator().startWizard(new InstallerWizardProvider(profile, gameVersion, version)); } + + public void installOffline() { + FileChooser chooser = new FileChooser(); + chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("install.installer.install_offline.extension"), "*.jar", "*.exe")); + File file = chooser.showOpenDialog(Controllers.getStage()); + if (file != null) doInstallOffline(file); + } + + private void doInstallOffline(File file) { + Task task = profile.getDependency().installLibraryAsync(version, file.toPath()) + .then(profile.getRepository().refreshVersionsAsync()); + task.setName(i18n("install.installer.install_offline")); + TaskExecutor executor = task.executor(new TaskListener() { + @Override + public void onStop(boolean success, TaskExecutor executor) { + runInFX(() -> { + if (success) { + loadVersion(profile, versionId); + Controllers.dialog(i18n("install.success")); + } else { + if (executor.getLastException() == null) + return; + InstallerWizardProvider.alertFailureMessage(executor.getLastException(), null); + } + }); + } + }); + Controllers.taskDialog(executor, i18n("install.installer.install_offline")); + executor.start(); + } + + private class InstallerListPageSkin extends ToolbarListPageSkin { + + InstallerListPageSkin() { + super(InstallerListPage.this); + } + + @Override + protected List initializeToolbar(InstallerListPage skinnable) { + return Arrays.asList( + createToolbarButton(i18n("install.installer.install_online"), SVG::plus, skinnable::installOnline), + createToolbarButton(i18n("install.installer.install_offline"), SVG::plus, skinnable::installOffline)); + } + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/TaskExecutorDialogWizardDisplayer.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/TaskExecutorDialogWizardDisplayer.java index db46ed19e..5951e4d16 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/TaskExecutorDialogWizardDisplayer.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/TaskExecutorDialogWizardDisplayer.java @@ -64,7 +64,6 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay @Override public void onStop(boolean success, TaskExecutor executor) { runInFX(() -> { - pane.fireEvent(new DialogCloseEvent()); if (success) { if (settings.containsKey("success_message") && settings.get("success_message") instanceof String) Controllers.dialog((String) settings.get("success_message"), null, MessageType.FINE, () -> onEnd()); diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 4b27c5192..a2c621b1b 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -131,11 +131,18 @@ install.failed=Failed to install install.failed.downloading=Failed to install due to some files not downloaded successfully install.failed.downloading.detail=Failed to download file: %s install.failed.downloading.timeout=Download timed out: %s +install.failed.install_online=Unable to recognize what you provided installer file is install.failed.optifine_conflict=OptiFine and Forge are both installed simultaneously on Minecraft 1.13 +install.failed.version_mismatch=The library requires game version %s, but actual version is %s. install.installer.choose=Choose a %s version install.installer.forge=Forge install.installer.game=Game -install.installer.install= +install.installer.install=Install %s +install.installer.install_offline=Install/Upgrade from local file +install.installer.install_offline.extension=Forge/OptiFine installer +install.installer.install_offline.tooltip=Support importing Forge/OptiFine installer jar file +install.installer.install_online=Install Online +install.installer.install_online.tooltip=Support Forge, OptiFine, LiteLoader installation. install.installer.liteloader=LiteLoader install.installer.not_installed=%s not Installed install.installer.optifine=OptiFine diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 5e0bbd84c..6d64466f0 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -130,11 +130,18 @@ install.failed=安裝失敗 install.failed.downloading=安裝失敗,部分文件未能完成下載 install.failed.downloading.detail=未能下載檔案:%s install.failed.downloading.timeout=下載超時:%s -install.failed.optifine_conflict=暫不支持 OptiFine 與 Forge 同時安裝於 Minecraft 1.13 +install.failed.install_online=無法識別要安裝的軟體 +install.failed.optifine_conflict=暫不支持 OptiFine 與 Forge 同時安裝在 Minecraft 1.13 上 +install.failed.version_mismatch=該軟體需要的遊戲版本為 %s,但實際的遊戲版本為 %s。 install.installer.choose=選擇 %s 版本 install.installer.forge=Forge install.installer.game=遊戲 install.installer.install=安裝 %s +install.installer.install_offline=從本地檔案安裝/升級 +install.installer.install_offline.extension=Forge/OptiFine 安裝器 +install.installer.install_offline.tooltip=支持導入已經下載好的 Forge/OptiFine 安裝器 +install.installer.install_online=在線安裝 +install.installer.install_online.tooltip=支持安裝 Forge、OptiFine、LiteLoader install.installer.liteloader=LiteLoader install.installer.not_installed=暫時不安裝 %s,可以點擊此處安裝 install.installer.optifine=OptiFine diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index c9ad476cb..7dd85b32c 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -130,11 +130,18 @@ install.failed=安装失败 install.failed.downloading=安装失败,部分文件未能完成下载 install.failed.downloading.detail=未能下载文件:%s install.failed.downloading.timeout=下载超时:%s -install.failed.optifine_conflict=暂不支持 OptiFine 与 Forge 同时安装于 Minecraft 1.13 +install.failed.install_online=无法识别要安装的软件 +install.failed.optifine_conflict=暂不支持 OptiFine 与 Forge 同时安装在 Minecraft 1.13 上 +install.failed.version_mismatch=该软件需要的游戏版本为 %s,但实际的游戏版本为 %s。 install.installer.choose=选择 %s 版本 install.installer.forge=Forge install.installer.game=游戏 install.installer.install=安装 %s +install.installer.install_offline=从本地文件安装/升级 +install.installer.install_offline.extension=Forge/OptiFine 安装器 +install.installer.install_offline.tooltip=支持导入已经下载好的 Forge/OptiFine 安装器 +install.installer.install_online=在线安装 +install.installer.install_online.tooltip=支持安装 Forge、OptiFine、LiteLoader install.installer.liteloader=LiteLoader install.installer.not_installed=暂不安装 %s,可以点击此处安装 install.installer.optifine=OptiFine diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java index ac656d9fc..8bb6179c0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java @@ -31,6 +31,9 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskResult; import org.jackhuang.hmcl.util.function.ExceptionalFunction; +import java.io.IOException; +import java.nio.file.Path; + /** * Note: This class has no state. * @@ -112,8 +115,29 @@ public class DefaultDependencyManager extends AbstractDependencyManager { .thenCompose(newVersion -> new VersionJsonSaveTask(repository, newVersion)); } - public ExceptionalFunction, ?> installLibraryAsync(RemoteVersion libraryVersion) { return version -> installLibraryAsync(version, libraryVersion); } + + public Task installLibraryAsync(Version oldVersion, Path installer) { + return Task + .of(() -> { + }) + .thenCompose(() -> { + try { + return ForgeInstallTask.install(this, oldVersion, installer); + } catch (IOException ignore) { + } + + try { + return OptiFineInstallTask.install(this, oldVersion, installer); + } catch (IOException ignore) { + } + + throw new UnsupportedOperationException("Library cannot be recognized"); + }) + .thenCompose(LibrariesUniqueTask::new) + .thenCompose(MaintainTask::new) + .thenCompose(newVersion -> new VersionJsonSaveTask(repository, newVersion)); + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/VersionMismatchException.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/VersionMismatchException.java new file mode 100644 index 000000000..22e071825 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/VersionMismatchException.java @@ -0,0 +1,37 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2019 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.download; + +public class VersionMismatchException extends Exception { + + private final String expect, actual; + + public VersionMismatchException(String expect, String actual) { + super("Mismatched game version requirement, library requires game to be " + expect + ", but actual is " + actual); + this.expect = expect; + this.actual = actual; + } + + public String getExpect() { + return expect; + } + + public String getActual() { + return actual; + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java index 7a3201f2f..183858a3b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java @@ -18,17 +18,26 @@ package org.jackhuang.hmcl.download.forge; import org.jackhuang.hmcl.download.DefaultDependencyManager; +import org.jackhuang.hmcl.download.VersionMismatchException; +import org.jackhuang.hmcl.game.GameVersion; import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskResult; +import org.jackhuang.hmcl.util.gson.JsonUtils; +import org.jackhuang.hmcl.util.io.CompressingUtils; +import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.versioning.VersionNumber; +import java.io.IOException; +import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; import java.util.Collections; +import java.util.Map; +import java.util.Optional; /** * @@ -90,4 +99,36 @@ public final class ForgeInstallTask extends TaskResult { else dependency = new ForgeOldInstallTask(dependencyManager, version, installer); } + + /** + * Install Forge library from existing local file. + * + * @param dependencyManager game repository + * @param version version.json + * @param installer the Forge installer, either the new or old one. + * @return the task to install library + * @throws IOException if unable to read compressed content of installer file, or installer file is corrupted, or the installer is not the one we want. + * @throws VersionMismatchException if required game version of installer does not match the actual one. + */ + public static TaskResult install(DefaultDependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException { + Optional gameVersion = GameVersion.minecraftVersion(dependencyManager.getGameRepository().getVersionJar(version)); + if (!gameVersion.isPresent()) throw new IOException(); + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { + String installProfileText = FileUtils.readText(fs.getPath("install_profile.json")); + Map installProfile = JsonUtils.fromNonNullJson(installProfileText, Map.class); + if (installProfile.containsKey("spec")) { + ForgeNewInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeNewInstallProfile.class); + if (!gameVersion.get().equals(profile.getMinecraft())) + throw new VersionMismatchException(profile.getMinecraft(), gameVersion.get()); + return new ForgeNewInstallTask(dependencyManager, version, installer); + } else if (installProfile.containsKey("install") && installProfile.containsKey("versionInfo")) { + ForgeInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeInstallProfile.class); + if (!gameVersion.get().equals(profile.getInstall().getMinecraft())) + throw new VersionMismatchException(profile.getInstall().getMinecraft(), gameVersion.get()); + return new ForgeOldInstallTask(dependencyManager, version, installer); + } else { + throw new IOException(); + } + } + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallProfile.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallProfile.java index 7ede71444..2b60b8262 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallProfile.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallProfile.java @@ -17,10 +17,13 @@ */ package org.jackhuang.hmcl.download.forge; +import com.google.gson.JsonParseException; import org.jackhuang.hmcl.game.Artifact; import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.function.ExceptionalFunction; +import org.jackhuang.hmcl.util.gson.TolerableValidationException; +import org.jackhuang.hmcl.util.gson.Validation; import java.util.Collections; import java.util.HashMap; @@ -29,7 +32,7 @@ import java.util.Map; import java.util.stream.Collectors; @Immutable -public class ForgeNewInstallProfile { +public class ForgeNewInstallProfile implements Validation { private final int spec; private final String minecraft; @@ -97,6 +100,8 @@ public class ForgeNewInstallProfile { /** * Data for processors. + * + * @return a mutable data map for processors. */ public Map getData() { if (data == null) @@ -105,7 +110,13 @@ public class ForgeNewInstallProfile { return data.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getClient())); } - public static class Processor { + @Override + public void validate() throws JsonParseException, TolerableValidationException { + if (minecraft == null || json == null || path == null) + throw new JsonParseException("ForgeNewInstallProfile is malformed"); + } + + public static class Processor implements Validation { private final List sides; private final Artifact jar; private final List classpath; @@ -170,6 +181,12 @@ public class ForgeNewInstallProfile { public Map getOutputs() { return outputs == null ? Collections.emptyMap() : outputs; } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + if (jar == null) + throw new JsonParseException("Processor::jar cannot be null"); + } } public static class Datum { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java index 456a70cb0..4d47575a6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java @@ -18,19 +18,26 @@ package org.jackhuang.hmcl.download.optifine; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.game.LibrariesDownloadInfo; -import org.jackhuang.hmcl.game.Library; -import org.jackhuang.hmcl.game.LibraryDownloadInfo; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.download.VersionMismatchException; +import org.jackhuang.hmcl.game.*; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskResult; import org.jackhuang.hmcl.util.Lang; -import org.jackhuang.hmcl.util.StringUtils; +import org.jackhuang.hmcl.util.io.CompressingUtils; +import org.jackhuang.hmcl.util.io.FileUtils; +import org.jenkinsci.constant_pool_scanner.ConstantPool; +import org.jenkinsci.constant_pool_scanner.ConstantPoolScanner; +import org.jenkinsci.constant_pool_scanner.ConstantType; +import org.jenkinsci.constant_pool_scanner.Utf8Constant; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +import static org.jackhuang.hmcl.util.Lang.getOrDefault; /** * Note: OptiFine should be installed in the end. @@ -42,13 +49,19 @@ public final class OptiFineInstallTask extends TaskResult { private final DefaultDependencyManager dependencyManager; private final Version version; private final OptiFineRemoteVersion remote; + private final Path installer; private final List dependents = new LinkedList<>(); private final List dependencies = new LinkedList<>(); public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion) { + this(dependencyManager, version, remoteVersion, null); + } + + public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion, Path installer) { this.dependencyManager = dependencyManager; this.version = version; this.remote = remoteVersion; + this.installer = installer; } @Override @@ -67,7 +80,7 @@ public final class OptiFineInstallTask extends TaskResult { } @Override - public void execute() { + public void execute() throws IOException { if (!Arrays.asList("net.minecraft.client.main.Main", "net.minecraft.launchwrapper.Launch") .contains(version.getMainClass())) @@ -82,6 +95,10 @@ public final class OptiFineInstallTask extends TaskResult { remote.getUrl())) ); + if (installer != null) { + FileUtils.copyFile(installer, dependencyManager.getGameRepository().getLibraryFile(version, library).toPath()); + } + List libraries = new LinkedList<>(); libraries.add(library); @@ -99,4 +116,37 @@ public final class OptiFineInstallTask extends TaskResult { public static class UnsupportedOptiFineInstallationException extends UnsupportedOperationException { } + + /** + * Install OptiFine library from existing local file. + * + * @param dependencyManager game repository + * @param version version.json + * @param installer the OptiFine installer + * @return the task to install library + * @throws IOException if unable to read compressed content of installer file, or installer file is corrupted, or the installer is not the one we want. + * @throws VersionMismatchException if required game version of installer does not match the actual one. + */ + public static TaskResult install(DefaultDependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException { + File jar = dependencyManager.getGameRepository().getVersionJar(version); + Optional gameVersion = GameVersion.minecraftVersion(jar); + if (!gameVersion.isPresent()) throw new IOException(); + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { + ConstantPool pool = ConstantPoolScanner.parse(Files.readAllBytes(fs.getPath("Config.class")), ConstantType.UTF8); + List constants = new ArrayList<>(); + pool.list(Utf8Constant.class).forEach(utf8 -> constants.add(utf8.get())); + String mcVersion = getOrDefault(constants, constants.indexOf("MC_VERSION") + 1, null); + String ofEdition = getOrDefault(constants, constants.indexOf("OF_EDITION") + 1, null); + String ofRelease = getOrDefault(constants, constants.indexOf("OF_RELEASE") + 1, null); + + if (mcVersion == null || ofEdition == null || ofRelease == null) + throw new IOException("Unrecognized OptiFine installer"); + + if (!mcVersion.equals(gameVersion.get())) + throw new VersionMismatchException(mcVersion, gameVersion.get()); + + return new OptiFineInstallTask(dependencyManager, version, + new OptiFineRemoteVersion(mcVersion, ofEdition + "_" + ofRelease, () -> null, false), installer); + } + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java index 6dc07855d..a8a206555 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Lang.java @@ -91,6 +91,10 @@ public final class Lang { } } + public static T getOrDefault(List a, int index, T defaultValue) { + return index < 0 || index >= a.size() ? defaultValue : a.get(index); + } + /** * Join two collections into one list. *