From 0b60bbd14296ec0f7d4bcee251287f2eb13499e8 Mon Sep 17 00:00:00 2001 From: Glavo Date: Thu, 26 Mar 2026 20:26:49 +0800 Subject: [PATCH 01/10] Convert MojangJavaDownloads to records --- .../hmcl/java/HMCLJavaRepository.java | 2 +- .../java/mojang/MojangJavaDownloadTask.java | 6 +- .../java/mojang/MojangJavaDownloads.java | 92 ++----------------- 3 files changed, 13 insertions(+), 87 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java index 07b10fc038..513bbca322 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java @@ -167,7 +167,7 @@ public Task getDownloadJavaTask(DownloadProvider downloadProvider, if (JavaManager.isCompatible(platform)) info = JavaInfoUtils.fromExecutable(executable, false); else - info = new JavaInfo(platform, result.download.getVersion().getName(), null); + info = new JavaInfo(platform, result.download.version().name(), null); Map update = new LinkedHashMap<>(); update.put("provider", "mojang"); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java index a44a68e811..28d6d423d1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java @@ -58,14 +58,14 @@ public MojangJavaDownloadTask(DownloadProvider downloadProvider, Path target, Ga .thenComposeAsync(javaDownloadsJson -> { MojangJavaDownloads allDownloads = JsonUtils.fromNonNullJson(javaDownloadsJson, MojangJavaDownloads.class); - Map> osDownloads = allDownloads.getDownloads().get(platform); + Map> osDownloads = allDownloads.downloads().get(platform); if (osDownloads == null || !osDownloads.containsKey(javaVersion.component())) throw new UnsupportedPlatformException("Unsupported platform: " + platform); List candidates = osDownloads.get(javaVersion.component()); for (MojangJavaDownloads.JavaDownload download : candidates) { - if (JavaInfo.parseVersion(download.getVersion().getName()) >= javaVersion.majorVersion()) { + if (JavaInfo.parseVersion(download.version().name()) >= javaVersion.majorVersion()) { this.download = download; - return new GetTask(downloadProvider.injectURLWithCandidates(download.getManifest().getUrl())); + return new GetTask(downloadProvider.injectURLWithCandidates(download.manifest().getUrl())); } } throw new UnsupportedPlatformException("Candidates: " + JsonUtils.GSON.toJson(candidates)); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloads.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloads.java index cd1cf741cd..dc1ef15133 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloads.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloads.java @@ -20,7 +20,7 @@ import com.google.gson.*; import com.google.gson.annotations.JsonAdapter; import org.jackhuang.hmcl.game.DownloadInfo; -import org.jackhuang.hmcl.util.Immutable; +import org.jackhuang.hmcl.util.gson.JsonSerializable; import java.lang.reflect.Type; import java.util.List; @@ -29,21 +29,10 @@ import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf; import static org.jackhuang.hmcl.util.gson.JsonUtils.mapTypeOf; +@JsonSerializable @JsonAdapter(MojangJavaDownloads.Adapter.class) -public class MojangJavaDownloads { - - private final Map>> downloads; - - public MojangJavaDownloads(Map>> downloads) { - this.downloads = downloads; - } - - public Map>> getDownloads() { - return downloads; - } - +public record MojangJavaDownloads(Map>> downloads) { public static class Adapter implements JsonSerializer, JsonDeserializer { - @Override public JsonElement serialize(MojangJavaDownloads src, Type typeOfSrc, JsonSerializationContext context) { return context.serialize(src.downloads); @@ -55,78 +44,15 @@ public MojangJavaDownloads deserialize(JsonElement json, Type typeOfT, JsonDeser } } - @Immutable - public static class JavaDownload { - private final Availability availability; - private final DownloadInfo manifest; - private final Version version; - - public JavaDownload() { - this(new Availability(), new DownloadInfo(), new Version()); - } - - public JavaDownload(Availability availability, DownloadInfo manifest, Version version) { - this.availability = availability; - this.manifest = manifest; - this.version = version; - } - - public Availability getAvailability() { - return availability; - } - - public DownloadInfo getManifest() { - return manifest; - } - - public Version getVersion() { - return version; - } + @JsonSerializable + public record JavaDownload(Availability availability, DownloadInfo manifest, Version version) { } - @Immutable - public static class Availability { - private final int group; - private final int progress; - - public Availability() { - this(0, 0); - } - - public Availability(int group, int progress) { - this.group = group; - this.progress = progress; - } - - public int getGroup() { - return group; - } - - public int getProgress() { - return progress; - } + @JsonSerializable + public record Availability(int group, int progress) { } - @Immutable - public static class Version { - private final String name; - private final String released; - - public Version() { - this("", ""); - } - - public Version(String name, String released) { - this.name = name; - this.released = released; - } - - public String getName() { - return name; - } - - public String getReleased() { - return released; - } + @JsonSerializable + public record Version(String name, String released) { } } From 8f6709149603ec99a2a61424fcfcedb83dc67350 Mon Sep 17 00:00:00 2001 From: Glavo Date: Thu, 26 Mar 2026 20:29:37 +0800 Subject: [PATCH 02/10] Use pattern matching in MojangJavaDownloadTask --- .../hmcl/download/java/mojang/MojangJavaDownloadTask.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java index 28d6d423d1..b4a1bcead1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java @@ -82,9 +82,7 @@ public Collection> getDependents() { public void execute() throws Exception { for (Map.Entry entry : javaDownloadsTask.getResult().getFiles().entrySet()) { Path dest = target.resolve(entry.getKey()); - if (entry.getValue() instanceof MojangJavaRemoteFiles.RemoteFile) { - MojangJavaRemoteFiles.RemoteFile file = ((MojangJavaRemoteFiles.RemoteFile) entry.getValue()); - + if (entry.getValue() instanceof MojangJavaRemoteFiles.RemoteFile file) { // Use local file if it already exists try { BasicFileAttributes localFileAttributes = Files.readAttributes(dest, BasicFileAttributes.class); @@ -137,8 +135,7 @@ public void execute() throws Exception { } } else if (entry.getValue() instanceof MojangJavaRemoteFiles.RemoteDirectory) { Files.createDirectories(dest); - } else if (entry.getValue() instanceof MojangJavaRemoteFiles.RemoteLink) { - MojangJavaRemoteFiles.RemoteLink link = ((MojangJavaRemoteFiles.RemoteLink) entry.getValue()); + } else if (entry.getValue() instanceof MojangJavaRemoteFiles.RemoteLink link) { Files.deleteIfExists(dest); Files.createSymbolicLink(dest, Paths.get(link.getTarget())); } From b23bfe5cc39f331a19010b9c67dfe5890aa66169 Mon Sep 17 00:00:00 2001 From: Glavo Date: Thu, 26 Mar 2026 20:30:23 +0800 Subject: [PATCH 03/10] Extract Java list URL constant --- .../java/mojang/MojangJavaDownloadTask.java | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java index b4a1bcead1..320215f896 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java @@ -43,6 +43,8 @@ public final class MojangJavaDownloadTask extends Task { + private static final String JAVA_LIST_URL = "https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"; + private final DownloadProvider downloadProvider; private final Path target; private final Task javaDownloadsTask; @@ -53,24 +55,23 @@ public final class MojangJavaDownloadTask extends Task { - MojangJavaDownloads allDownloads = JsonUtils.fromNonNullJson(javaDownloadsJson, MojangJavaDownloads.class); - - Map> osDownloads = allDownloads.downloads().get(platform); - if (osDownloads == null || !osDownloads.containsKey(javaVersion.component())) - throw new UnsupportedPlatformException("Unsupported platform: " + platform); - List candidates = osDownloads.get(javaVersion.component()); - for (MojangJavaDownloads.JavaDownload download : candidates) { - if (JavaInfo.parseVersion(download.version().name()) >= javaVersion.majorVersion()) { - this.download = download; - return new GetTask(downloadProvider.injectURLWithCandidates(download.manifest().getUrl())); - } - } - throw new UnsupportedPlatformException("Candidates: " + JsonUtils.GSON.toJson(candidates)); - }) - .thenApplyAsync(javaDownloadJson -> JsonUtils.fromNonNullJson(javaDownloadJson, MojangJavaRemoteFiles.class)); + this.javaDownloadsTask = new GetTask(downloadProvider.injectURLWithCandidates(JAVA_LIST_URL)) + .thenComposeAsync(javaDownloadsJson -> { + MojangJavaDownloads allDownloads = JsonUtils.fromNonNullJson(javaDownloadsJson, MojangJavaDownloads.class); + + Map> osDownloads = allDownloads.downloads().get(platform); + if (osDownloads == null || !osDownloads.containsKey(javaVersion.component())) + throw new UnsupportedPlatformException("Unsupported platform: " + platform); + List candidates = osDownloads.get(javaVersion.component()); + for (MojangJavaDownloads.JavaDownload download : candidates) { + if (JavaInfo.parseVersion(download.version().name()) >= javaVersion.majorVersion()) { + this.download = download; + return new GetTask(downloadProvider.injectURLWithCandidates(download.manifest().getUrl())); + } + } + throw new UnsupportedPlatformException("Candidates: " + JsonUtils.GSON.toJson(candidates)); + }) + .thenApplyAsync(javaDownloadJson -> JsonUtils.fromNonNullJson(javaDownloadJson, MojangJavaRemoteFiles.class)); } @Override From 7392a85faaa0bc99905f6425b5df2f9cd1e01321 Mon Sep 17 00:00:00 2001 From: Glavo Date: Thu, 26 Mar 2026 20:31:00 +0800 Subject: [PATCH 04/10] Extract Java list URL constant --- .../java/org/jackhuang/hmcl/java/HMCLJavaRepository.java | 4 ++-- .../download/java/mojang/MojangJavaDownloadTask.java | 9 +-------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java index 513bbca322..9b8b0d4cb5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java @@ -167,14 +167,14 @@ public Task getDownloadJavaTask(DownloadProvider downloadProvider, if (JavaManager.isCompatible(platform)) info = JavaInfoUtils.fromExecutable(executable, false); else - info = new JavaInfo(platform, result.download.version().name(), null); + info = new JavaInfo(platform, result.download().version().name(), null); Map update = new LinkedHashMap<>(); update.put("provider", "mojang"); update.put("component", gameJavaVersion.component()); Map files = new LinkedHashMap<>(); - result.remoteFiles.getFiles().forEach((path, file) -> { + result.remoteFiles().getFiles().forEach((path, file) -> { if (file instanceof MojangJavaRemoteFiles.RemoteFile) { DownloadInfo downloadInfo = ((MojangJavaRemoteFiles.RemoteFile) file).getDownloads().get("raw"); if (downloadInfo != null) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java index 320215f896..31518374f2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java @@ -158,13 +158,6 @@ public void postExecute() throws Exception { setResult(new Result(download, javaDownloadsTask.getResult())); } - public static final class Result { - public final MojangJavaDownloads.JavaDownload download; - public final MojangJavaRemoteFiles remoteFiles; - - public Result(MojangJavaDownloads.JavaDownload download, MojangJavaRemoteFiles remoteFiles) { - this.download = download; - this.remoteFiles = remoteFiles; - } + public record Result(MojangJavaDownloads.JavaDownload download, MojangJavaRemoteFiles remoteFiles) { } } From 4f3b9d69d4688b4eaec9fda1db59a1a6ccc619b6 Mon Sep 17 00:00:00 2001 From: Glavo Date: Thu, 26 Mar 2026 20:57:06 +0800 Subject: [PATCH 05/10] Use temp dir for Java download and verify integrity --- .../hmcl/java/HMCLJavaRepository.java | 3 +- .../java/mojang/MojangJavaDownloadTask.java | 33 ++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java index 9b8b0d4cb5..1d93bee0aa 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java @@ -151,8 +151,9 @@ public Collection getAllJava(Platform platform) { @Override public Task getDownloadJavaTask(DownloadProvider downloadProvider, Platform platform, GameJavaVersion gameJavaVersion) { Path javaDir = getJavaDir(platform, gameJavaVersion); + Path tempDir = getPlatformRoot(platform).resolve(".tmp").resolve(javaDir.getFileName()); - return new MojangJavaDownloadTask(downloadProvider, javaDir, gameJavaVersion, JavaManager.getMojangJavaPlatform(platform)).thenApplyAsync(result -> { + return new MojangJavaDownloadTask(downloadProvider, javaDir, tempDir, gameJavaVersion, JavaManager.getMojangJavaPlatform(platform)).thenApplyAsync(result -> { Path executable; try { executable = JavaManager.getExecutable(javaDir).toRealPath(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java index 31518374f2..9eac2bd925 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java @@ -25,6 +25,7 @@ import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.util.DigestUtils; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.ChecksumMismatchException; import org.jackhuang.hmcl.util.io.FileUtils; @@ -47,13 +48,15 @@ public final class MojangJavaDownloadTask extends Task javaDownloadsTask; private final List> dependencies = new ArrayList<>(); private volatile MojangJavaDownloads.JavaDownload download; - public MojangJavaDownloadTask(DownloadProvider downloadProvider, Path target, GameJavaVersion javaVersion, String platform) { + public MojangJavaDownloadTask(DownloadProvider downloadProvider, Path target, Path tempDir, GameJavaVersion javaVersion, String platform) { this.target = target; + this.tempDir = tempDir; this.downloadProvider = downloadProvider; this.javaDownloadsTask = new GetTask(downloadProvider.injectURLWithCandidates(JAVA_LIST_URL)) .thenComposeAsync(javaDownloadsJson -> { @@ -82,7 +85,7 @@ public Collection> getDependents() { @Override public void execute() throws Exception { for (Map.Entry entry : javaDownloadsTask.getResult().getFiles().entrySet()) { - Path dest = target.resolve(entry.getKey()); + Path dest = tempDir.resolve(entry.getKey()); if (entry.getValue() instanceof MojangJavaRemoteFiles.RemoteFile file) { // Use local file if it already exists try { @@ -100,11 +103,11 @@ public void execute() throws Exception { if (file.getDownloads().containsKey("lzma")) { DownloadInfo download = file.getDownloads().get("lzma"); - Path tempFile = target.resolve(entry.getKey() + ".lzma"); + Path tempFile = tempDir.resolve(entry.getKey() + ".lzma"); var task = new FileDownloadTask(downloadProvider.injectURLWithCandidates(download.getUrl()), tempFile, new FileDownloadTask.IntegrityCheck("SHA-1", download.getSha1())); task.setName(entry.getKey()); dependencies.add(task.thenRunAsync(() -> { - Path decompressed = target.resolve(entry.getKey() + ".tmp"); + Path decompressed = tempDir.resolve(entry.getKey()); try (LZMAInputStream input = new LZMAInputStream(Files.newInputStream(tempFile))) { Files.copy(input, decompressed, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { @@ -155,6 +158,28 @@ public boolean doPostExecute() { @Override public void postExecute() throws Exception { + for (var entry : javaDownloadsTask.getResult().getFiles().entrySet()) { + if (entry.getValue() instanceof MojangJavaRemoteFiles.RemoteFile remoteFile) { + DownloadInfo raw = remoteFile.getDownloads().get("raw"); + if (raw != null && raw.getSha1() != null) { + Path dest = tempDir.resolve(entry.getKey()); + String actual = DigestUtils.digestToString("SHA-1", dest); + + if (!raw.getSha1().equalsIgnoreCase(actual)) { + throw new ArtifactMalformedException("Malformed file " + dest + ": expected SHA-1 " + raw.getSha1() + ", but got " + actual); + } + } + } + } + + FileUtils.cleanDirectory(target); + + if (Files.getFileStore(target).equals(Files.getFileStore(tempDir))) { + Files.move(tempDir, target, StandardCopyOption.REPLACE_EXISTING); + } else { + FileUtils.copyDirectory(tempDir, target); + } + setResult(new Result(download, javaDownloadsTask.getResult())); } From 786f8aecafb7c6dc152c0e47c24b9f5f835b2b59 Mon Sep 17 00:00:00 2001 From: Glavo Date: Thu, 26 Mar 2026 21:14:57 +0800 Subject: [PATCH 06/10] update --- .../hmcl/download/java/mojang/MojangJavaDownloadTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java index 9eac2bd925..ede000d137 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java @@ -107,7 +107,7 @@ public void execute() throws Exception { var task = new FileDownloadTask(downloadProvider.injectURLWithCandidates(download.getUrl()), tempFile, new FileDownloadTask.IntegrityCheck("SHA-1", download.getSha1())); task.setName(entry.getKey()); dependencies.add(task.thenRunAsync(() -> { - Path decompressed = tempDir.resolve(entry.getKey()); + Path decompressed = tempDir.resolve(entry.getKey() + ".tmp"); try (LZMAInputStream input = new LZMAInputStream(Files.newInputStream(tempFile))) { Files.copy(input, decompressed, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { From 1f27e7466cb3a6876ae2c0ad04ec94f7930c5d01 Mon Sep 17 00:00:00 2001 From: Glavo Date: Thu, 26 Mar 2026 21:17:31 +0800 Subject: [PATCH 07/10] Clean up temp dir after Java download --- .../hmcl/download/java/mojang/MojangJavaDownloadTask.java | 1 + 1 file changed, 1 insertion(+) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java index ede000d137..984c01b072 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java @@ -178,6 +178,7 @@ public void postExecute() throws Exception { Files.move(tempDir, target, StandardCopyOption.REPLACE_EXISTING); } else { FileUtils.copyDirectory(tempDir, target); + FileUtils.deleteDirectory(tempDir); } setResult(new Result(download, javaDownloadsTask.getResult())); From 9012570f3c4690bcba306ed91ce531dcfedf0fce Mon Sep 17 00:00:00 2001 From: Glavo Date: Sun, 29 Mar 2026 22:10:38 +0800 Subject: [PATCH 08/10] Add SHA-1 verification for decompressed files in Java download --- .../java/mojang/MojangJavaDownloadTask.java | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java index 984c01b072..fbbd9500c2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java @@ -38,6 +38,8 @@ import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; +import java.security.DigestInputStream; +import java.security.MessageDigest; import java.util.*; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -103,17 +105,34 @@ public void execute() throws Exception { if (file.getDownloads().containsKey("lzma")) { DownloadInfo download = file.getDownloads().get("lzma"); + DownloadInfo raw = file.getDownloads().get("raw"); + + String rawSha1; + if (raw != null && raw.getSha1() != null) { + rawSha1 = raw.getSha1(); + } else { + rawSha1 = null; + } + Path tempFile = tempDir.resolve(entry.getKey() + ".lzma"); - var task = new FileDownloadTask(downloadProvider.injectURLWithCandidates(download.getUrl()), tempFile, new FileDownloadTask.IntegrityCheck("SHA-1", download.getSha1())); + var task = new FileDownloadTask(downloadProvider.injectURLWithCandidates(download.getUrl()), tempFile, + new FileDownloadTask.IntegrityCheck("SHA-1", download.getSha1())); task.setName(entry.getKey()); dependencies.add(task.thenRunAsync(() -> { Path decompressed = tempDir.resolve(entry.getKey() + ".tmp"); - try (LZMAInputStream input = new LZMAInputStream(Files.newInputStream(tempFile))) { + var digest = MessageDigest.getInstance("SHA-1"); + try (var input = new DigestInputStream(new LZMAInputStream(Files.newInputStream(tempFile)), digest)) { Files.copy(input, decompressed, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { throw new ArtifactMalformedException("File " + entry.getKey() + " is malformed", e); } + String actualSha1 = HexFormat.of().formatHex(digest.digest()); + + if (rawSha1 != null && !actualSha1.equals(rawSha1)) { + throw new ArtifactMalformedException("File " + entry.getKey() + " has incorrect SHA-1 hash: expected " + rawSha1 + ", got " + actualSha1); + } + try { Files.deleteIfExists(tempFile); } catch (IOException e) { @@ -158,30 +177,17 @@ public boolean doPostExecute() { @Override public void postExecute() throws Exception { - for (var entry : javaDownloadsTask.getResult().getFiles().entrySet()) { - if (entry.getValue() instanceof MojangJavaRemoteFiles.RemoteFile remoteFile) { - DownloadInfo raw = remoteFile.getDownloads().get("raw"); - if (raw != null && raw.getSha1() != null) { - Path dest = tempDir.resolve(entry.getKey()); - String actual = DigestUtils.digestToString("SHA-1", dest); - - if (!raw.getSha1().equalsIgnoreCase(actual)) { - throw new ArtifactMalformedException("Malformed file " + dest + ": expected SHA-1 " + raw.getSha1() + ", but got " + actual); - } - } + if (isDependenciesSucceeded()) { + FileUtils.cleanDirectory(target); + + if (Files.getFileStore(target).equals(Files.getFileStore(tempDir))) { + Files.move(tempDir, target, StandardCopyOption.REPLACE_EXISTING); + } else { + FileUtils.copyDirectory(tempDir, target); + FileUtils.deleteDirectory(tempDir); } + setResult(new Result(download, javaDownloadsTask.getResult())); } - - FileUtils.cleanDirectory(target); - - if (Files.getFileStore(target).equals(Files.getFileStore(tempDir))) { - Files.move(tempDir, target, StandardCopyOption.REPLACE_EXISTING); - } else { - FileUtils.copyDirectory(tempDir, target); - FileUtils.deleteDirectory(tempDir); - } - - setResult(new Result(download, javaDownloadsTask.getResult())); } public record Result(MojangJavaDownloads.JavaDownload download, MojangJavaRemoteFiles remoteFiles) { From 3f9a5c5903feb5d2b598b91cc7bd18f5f8d88f43 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 30 Mar 2026 21:23:39 +0800 Subject: [PATCH 09/10] update --- .../hmcl/download/java/mojang/MojangJavaDownloadTask.java | 1 - 1 file changed, 1 deletion(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java index fbbd9500c2..7503a96fa1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java @@ -25,7 +25,6 @@ import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.Task; -import org.jackhuang.hmcl.util.DigestUtils; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.ChecksumMismatchException; import org.jackhuang.hmcl.util.io.FileUtils; From fb1229b3e95469f6b3a7e38203a683089092012c Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 30 Mar 2026 21:33:39 +0800 Subject: [PATCH 10/10] update --- .../hmcl/download/java/mojang/MojangJavaDownloadTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java index 7503a96fa1..f790813ecb 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java @@ -128,7 +128,7 @@ public void execute() throws Exception { String actualSha1 = HexFormat.of().formatHex(digest.digest()); - if (rawSha1 != null && !actualSha1.equals(rawSha1)) { + if (rawSha1 != null && !actualSha1.equalsIgnoreCase(rawSha1)) { throw new ArtifactMalformedException("File " + entry.getKey() + " has incorrect SHA-1 hash: expected " + rawSha1 + ", got " + actualSha1); }