From 79de24880216d1557200aaf38cdfa31b6622b57c Mon Sep 17 00:00:00 2001 From: ToobLac Date: Thu, 9 Jul 2026 16:56:31 +0800 Subject: [PATCH 1/5] Fixes #6348 --- .../hmcl/addon/resourcepack/ResourcePackManager.java | 12 ++++++++---- .../java/org/jackhuang/hmcl/util/StringUtils.java | 11 +++++++++++ .../java/org/jackhuang/hmcl/util/io/FileUtils.java | 7 ++++++- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java index d571f6422cf..fd5a84b5106 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java @@ -36,7 +36,7 @@ import org.jetbrains.annotations.Unmodifiable; import java.io.IOException; -import java.nio.charset.StandardCharsets; +import java.nio.charset.Charset; import java.nio.file.*; import java.util.*; import java.util.stream.Stream; @@ -224,13 +224,17 @@ public ResourcePackManager(GameRepository repository, String id) { this.optionsFile = repository.getRunDirectory(id).resolve("options.txt"); } + private Charset optionsFileCharset; + @NotNull private Map loadOptions() { getMinecraftVersion(); Map options = new LinkedHashMap<>(); if (!Files.isRegularFile(optionsFile)) return options; - try (var stream = Files.lines(optionsFile, StandardCharsets.UTF_8)) { - stream.forEach(s -> { + try { + byte[] bytes = Files.readAllBytes(optionsFile); + optionsFileCharset = StringUtils.maybeDetectTextEncoding(bytes); + new String(bytes, optionsFileCharset).lines().forEach(s -> { if (StringUtils.isNotBlank(s)) { var entry = s.split(":", 2); if (entry.length == 2) { @@ -250,7 +254,7 @@ private void saveOptions(@NotNull Map options) { for (var entry : options.entrySet()) { sb.append(entry.getKey()).append(":").append(entry.getValue()).append(System.lineSeparator()); } - FileUtils.saveSafely(optionsFile, sb.toString()); + FileUtils.saveSafely(optionsFile, sb.toString(), optionsFileCharset); } catch (IOException e) { LOG.warning("Failed to save instance options file", e); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java index 533aaaab189..eccffe22326 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java @@ -17,12 +17,15 @@ */ package org.jackhuang.hmcl.util; +import org.glavo.chardet.UniversalDetector; import org.jackhuang.hmcl.util.gson.JsonUtils; +import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Nullable; import java.io.PrintWriter; import java.io.StringWriter; +import java.nio.charset.Charset; import java.util.*; import java.util.function.Predicate; import java.util.regex.Matcher; @@ -630,6 +633,14 @@ public static List deserializeStringList(String json) { return JsonUtils.fromNonNullJson(json, JsonUtils.listTypeOf(String.class)); } + public static Charset maybeDetectTextEncoding(byte[] data) { + var detector = new UniversalDetector(); + detector.handleData(data); + detector.dataEnd(); + var detected = detector.getDetectedCharset(); + return detected != null ? detected.getCharset(OperatingSystem.NATIVE_CHARSET) : OperatingSystem.NATIVE_CHARSET; + } + public static class LevCalculator { private int[][] lev; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java index c583860bcc8..d41bba90758 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java @@ -27,6 +27,7 @@ import org.jetbrains.annotations.Nullable; import java.io.*; +import java.nio.charset.Charset; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.PosixFileAttributeView; @@ -507,13 +508,17 @@ public static Path tmpSaveFile(Path file) { } public static void saveSafely(Path file, String content) throws IOException { + saveSafely(file, content, UTF_8); + } + + public static void saveSafely(Path file, String content, Charset charset) throws IOException { Path parent = file.toAbsolutePath().getParent(); if (parent != null) { Files.createDirectories(parent); } Path tmpFile = tmpSaveFile(file); - try (BufferedWriter writer = Files.newBufferedWriter(tmpFile, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) { + try (BufferedWriter writer = Files.newBufferedWriter(tmpFile, charset, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) { writer.write(content); } From 9420f708434925438c470a0b4b3efd4e4e3dca87 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Thu, 9 Jul 2026 16:57:56 +0800 Subject: [PATCH 2/5] update --- .../hmcl/addon/resourcepack/ResourcePackManager.java | 1 + .../src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java index fd5a84b5106..f8fc5f55f00 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java @@ -224,6 +224,7 @@ public ResourcePackManager(GameRepository repository, String id) { this.optionsFile = repository.getRunDirectory(id).resolve("options.txt"); } + @Nullable private Charset optionsFileCharset; @NotNull diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java index d41bba90758..1fb1a0efc08 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java @@ -511,14 +511,14 @@ public static void saveSafely(Path file, String content) throws IOException { saveSafely(file, content, UTF_8); } - public static void saveSafely(Path file, String content, Charset charset) throws IOException { + public static void saveSafely(Path file, String content, @Nullable Charset charset) throws IOException { Path parent = file.toAbsolutePath().getParent(); if (parent != null) { Files.createDirectories(parent); } Path tmpFile = tmpSaveFile(file); - try (BufferedWriter writer = Files.newBufferedWriter(tmpFile, charset, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) { + try (BufferedWriter writer = Files.newBufferedWriter(tmpFile, charset != null ? charset : UTF_8, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) { writer.write(content); } From 34d122b4b88c8b825c22d4316c784251eda57ab1 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Thu, 9 Jul 2026 17:06:48 +0800 Subject: [PATCH 3/5] update --- .../resourcepack/ResourcePackManager.java | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java index f8fc5f55f00..149bb45af62 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java @@ -36,6 +36,7 @@ import org.jetbrains.annotations.Unmodifiable; import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.charset.Charset; import java.nio.file.*; import java.util.*; @@ -232,29 +233,31 @@ private Map loadOptions() { getMinecraftVersion(); Map options = new LinkedHashMap<>(); if (!Files.isRegularFile(optionsFile)) return options; + byte[] bytes; try { - byte[] bytes = Files.readAllBytes(optionsFile); - optionsFileCharset = StringUtils.maybeDetectTextEncoding(bytes); - new String(bytes, optionsFileCharset).lines().forEach(s -> { - if (StringUtils.isNotBlank(s)) { - var entry = s.split(":", 2); - if (entry.length == 2) { - options.put(entry[0], entry[1]); - } - } - }); - } catch (IOException e) { + bytes = Files.readAllBytes(optionsFile); + } catch (IOException | UncheckedIOException e) { LOG.warning("Failed to read instance options file", e); + return options; } + optionsFileCharset = StringUtils.maybeDetectTextEncoding(bytes); + new String(bytes, optionsFileCharset).lines().forEach(s -> { + if (StringUtils.isNotBlank(s)) { + var entry = s.split(":", 2); + if (entry.length == 2) { + options.put(entry[0], entry[1]); + } + } + }); return options; } private void saveOptions(@NotNull Map options) { + StringBuilder sb = new StringBuilder(); + for (var entry : options.entrySet()) { + sb.append(entry.getKey()).append(":").append(entry.getValue()).append(System.lineSeparator()); + } try { - StringBuilder sb = new StringBuilder(); - for (var entry : options.entrySet()) { - sb.append(entry.getKey()).append(":").append(entry.getValue()).append(System.lineSeparator()); - } FileUtils.saveSafely(optionsFile, sb.toString(), optionsFileCharset); } catch (IOException e) { LOG.warning("Failed to save instance options file", e); From c1c167216f76949932500a946e4738eb5cff9a99 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Fri, 10 Jul 2026 14:31:20 +0800 Subject: [PATCH 4/5] update --- .../src/main/java/org/jackhuang/hmcl/util/StringUtils.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java index eccffe22326..a9974a043d5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java @@ -26,6 +26,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.function.Predicate; import java.util.regex.Matcher; @@ -638,7 +639,10 @@ public static Charset maybeDetectTextEncoding(byte[] data) { detector.handleData(data); detector.dataEnd(); var detected = detector.getDetectedCharset(); - return detected != null ? detected.getCharset(OperatingSystem.NATIVE_CHARSET) : OperatingSystem.NATIVE_CHARSET; + if (detected == null || !detected.isSupported()) return OperatingSystem.NATIVE_CHARSET; + var charset = detected.getCharset(); + if (charset == StandardCharsets.UTF_8 || charset == StandardCharsets.US_ASCII) return StandardCharsets.UTF_8; + return OperatingSystem.NATIVE_CHARSET; } public static class LevCalculator { From 79dc5c923fd1aae5d5b5d9f72bb8eabd8c50c87d Mon Sep 17 00:00:00 2001 From: ToobLac Date: Fri, 10 Jul 2026 22:05:21 +0800 Subject: [PATCH 5/5] update --- .../jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java | 2 +- HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java index 149bb45af62..66ad9fb32ad 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java @@ -240,7 +240,7 @@ private Map loadOptions() { LOG.warning("Failed to read instance options file", e); return options; } - optionsFileCharset = StringUtils.maybeDetectTextEncoding(bytes); + optionsFileCharset = StringUtils.detectMaybeNativeTextEncoding(bytes); new String(bytes, optionsFileCharset).lines().forEach(s -> { if (StringUtils.isNotBlank(s)) { var entry = s.split(":", 2); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java index a9974a043d5..45df1dee6f8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java @@ -634,7 +634,7 @@ public static List deserializeStringList(String json) { return JsonUtils.fromNonNullJson(json, JsonUtils.listTypeOf(String.class)); } - public static Charset maybeDetectTextEncoding(byte[] data) { + public static Charset detectMaybeNativeTextEncoding(byte[] data) { var detector = new UniversalDetector(); detector.handleData(data); detector.dataEnd();