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 dd05bea8ec..07876ebf81 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Modpack.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Modpack.java @@ -23,12 +23,17 @@ import java.nio.charset.Charset; import java.nio.file.Path; import java.util.List; +import java.util.Set; /** * * @author huangyuhui */ public abstract class Modpack { + + public static final Set SUPPORTED_ICON_EXTS = Set.of("png", "jpg", "jpeg", "bmp", "gif", "webp", "apng"); + public static final Set SUPPORTED_ICON_NAMES = Set.of("icon.png", "icon.jpg", "icon.jpeg", "icon.bmp", "icon.gif", "icon.webp", "icon.apng"); + private String name; private String author; private String version; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseInstallTask.java index 9b859c4bbb..f74b9a45bf 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseInstallTask.java @@ -22,17 +22,20 @@ import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.mod.*; +import org.jackhuang.hmcl.task.CacheFileTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.gson.JsonUtils; +import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.io.NetworkUtils; import java.io.IOException; +import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; + +import static org.jackhuang.hmcl.util.logging.Logger.LOG; /** * Install a downloaded CurseForge modpack. @@ -47,8 +50,11 @@ public final class CurseInstallTask extends Task { private final Modpack modpack; private final CurseManifest manifest; private final String name; + private final String iconUrl; private final Path run; private final ModpackConfiguration config; + private String iconExt; + private Task downloadIconTask; private final List> dependents = new ArrayList<>(4); private final List> dependencies = new ArrayList<>(1); @@ -60,12 +66,13 @@ public final class CurseInstallTask extends Task { * @param manifest The manifest content of given CurseForge modpack. * @param name the new version name */ - public CurseInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, Modpack modpack, CurseManifest manifest, String name) { + public CurseInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, Modpack modpack, CurseManifest manifest, String name, String iconUrl) { this.dependencyManager = dependencyManager; this.zipFile = zipFile; this.modpack = modpack; this.manifest = manifest; this.name = name; + this.iconUrl = iconUrl; this.repository = dependencyManager.getGameRepository(); this.run = repository.getRunDirectory(name); @@ -108,6 +115,14 @@ public CurseInstallTask(DefaultDependencyManager dependencyManager, Path zipFile dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList(manifest.overrides()), any -> true, config).withStage("hmcl.modpack")); dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList(manifest.overrides()), manifest, CurseModpackProvider.INSTANCE, manifest.name(), manifest.version(), repository.getModpackConfiguration(name)).withStage("hmcl.modpack")); + URI iconUri = NetworkUtils.toURIOrNull(iconUrl); + if (iconUri != null) { + String ext = FileUtils.getExtension(StringUtils.substringAfter(iconUri.getPath(), '/')).toLowerCase(Locale.ROOT); + if (Modpack.SUPPORTED_ICON_EXTS.contains(ext)) { + iconExt = ext; + dependents.add(downloadIconTask = new CacheFileTask(iconUrl)); + } + } dependencies.add(new CurseCompletionTask(dependencyManager, name, manifest)); } @@ -137,5 +152,13 @@ public void execute() throws Exception { Path root = repository.getVersionRoot(name); Files.createDirectories(root); JsonUtils.writeToJsonFile(root.resolve("manifest.json"), manifest); + + if (iconExt != null && Modpack.SUPPORTED_ICON_NAMES.stream().map(root::resolve).allMatch(Files::notExists)) { + try { + Files.copy(downloadIconTask.getResult(), root.resolve("icon." + iconExt)); + } catch (Exception e) { + LOG.warning("Failed to copy modpack icon", e); + } + } } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseModpackProvider.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseModpackProvider.java index 8d51df4352..aae1b7ec83 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseModpackProvider.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseModpackProvider.java @@ -52,7 +52,7 @@ public Task createUpdateTask(DefaultDependencyManager dependencyManager, Stri if (!(modpack.getManifest() instanceof CurseManifest curseManifest)) throw new MismatchedModpackTypeException(getName(), modpack.getManifest().getProvider().getName()); - return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new CurseInstallTask(dependencyManager, zipFile, modpack, curseManifest, name)); + return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new CurseInstallTask(dependencyManager, zipFile, modpack, curseManifest, name, null)); } @Override @@ -69,7 +69,7 @@ public Modpack readManifest(ZipArchiveReader zip, Path file, Charset encoding) t return new Modpack(manifest.name(), manifest.author(), manifest.version(), manifest.minecraft().gameVersion(), description, encoding, manifest) { @Override public Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, String name, String iconUrl) { - return new CurseInstallTask(dependencyManager, zipFile, this, manifest, name); + return new CurseInstallTask(dependencyManager, zipFile, this, manifest, name, iconUrl); } }; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthInstallTask.java index 5a557008e6..b85a0df87b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthInstallTask.java @@ -38,7 +38,6 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public class ModrinthInstallTask extends Task { - private static final Set SUPPORTED_ICON_EXTS = Set.of("png", "jpg", "jpeg", "bmp", "gif", "webp", "apng"); private final DefaultDependencyManager dependencyManager; private final DefaultGameRepository repository; @@ -121,7 +120,7 @@ public ModrinthInstallTask(DefaultDependencyManager dependencyManager, Path zipF URI iconUri = NetworkUtils.toURIOrNull(iconUrl); if (iconUri != null) { String ext = FileUtils.getExtension(StringUtils.substringAfter(iconUri.getPath(), '/')).toLowerCase(Locale.ROOT); - if (SUPPORTED_ICON_EXTS.contains(ext)) { + if (Modpack.SUPPORTED_ICON_EXTS.contains(ext)) { iconExt = ext; dependents.add(downloadIconTask = new CacheFileTask(iconUrl)); } @@ -156,7 +155,7 @@ public void execute() throws Exception { Files.createDirectories(root); JsonUtils.writeToJsonFile(root.resolve("modrinth.index.json"), manifest); - if (iconExt != null) { + if (iconExt != null && Modpack.SUPPORTED_ICON_NAMES.stream().map(root::resolve).allMatch(Files::notExists)) { try { Files.copy(downloadIconTask.getResult(), root.resolve("icon." + iconExt)); } catch (Exception e) {