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 4fc87b4a9..8d2461e01 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java @@ -52,6 +52,55 @@ public final class StringUtils { return !isBlank(str); } + public static String normalizeWhitespaces(String str) { + if (str == null) + return ""; + + int start = 0; + int end = str.length(); + + while (start < str.length() && Character.isWhitespace(str.charAt(start))) { + start++; + } + while (end > start && Character.isWhitespace(str.charAt(end - 1))) { + end--; + } + + if (end == start) { + return ""; + } + + StringBuilder builder = null; + + int i = start; + while (i < end) { + char ch = str.charAt(i); + if (Character.isWhitespace(ch)) { + int whitespaceEnd = i + 1; + while (whitespaceEnd < end && Character.isWhitespace(str.charAt(whitespaceEnd))) { + whitespaceEnd++; + } + + if (whitespaceEnd - i > 1 || ch != ' ') { + if (builder == null) { + builder = new StringBuilder(end - start); + builder.append(str, start, i); + } + builder.append(' '); + i = whitespaceEnd ; + continue; + } + } + + if (builder != null) { + builder.append(ch); + } + i++; + } + + return builder != null ? builder.toString() : str.substring(start, end); + } + public static String capitalizeFirst(String str) { if (str == null || str.isEmpty()) return str; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/hardware/CentralProcessor.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/hardware/CentralProcessor.java index 04df38776..514cc3f3d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/hardware/CentralProcessor.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/hardware/CentralProcessor.java @@ -17,12 +17,46 @@ */ package org.jackhuang.hmcl.util.platform.hardware; +import org.jackhuang.hmcl.util.StringUtils; import org.jetbrains.annotations.Nullable; /** * @author Glavo */ public final class CentralProcessor { + + public static String cleanName(String name) { + if (name == null) + return null; + + int idx = name.indexOf('@'); + if (idx > 0) + name = name.substring(0, idx); + + name = name.replaceFirst(" (\\d+|Dual|Quad|Six|Eight|Ten)-[Cc]ores?", ""); + name = name.replaceAll(" (CPU|FPU|APU|Processor)", ""); + + if (name.contains("Intel")) { + name = name.replaceFirst("^(\\d+th Gen )?Intel(\\(R\\)|®)? ", "Intel "); + name = name.replaceAll(" ([a-zA-Z]+)\\((?:TM|R|™|®)\\) ", " $1 "); + name = name.replace("Core(TM)2", "Core 2"); + } else if (name.contains("AMD")) { + name = name.replace("(tm)", ""); + + idx = name.indexOf(" w/ Radeon "); // Radeon 780M Graphics + if (idx < 0) + idx = name.indexOf(" with Radeon "); + if (idx < 0) + idx = name.indexOf(" with AMD Radeon "); + if (idx > 0) + name = name.substring(0, idx); + } else if (name.contains("Loongson")) { + name = name.replaceFirst("^Loongson-3A R\\d \\((Loongson-[^)]+)\\)", "$1"); + } + + return StringUtils.normalizeWhitespaces(name); + } + private final String name; private final @Nullable HardwareVendor vendor; private final @Nullable Cores cores; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/linux/LinuxCPUDetector.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/linux/LinuxCPUDetector.java index 07fa50ca9..ac9b0d23e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/linux/LinuxCPUDetector.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/linux/LinuxCPUDetector.java @@ -100,11 +100,13 @@ final class LinuxCPUDetector { String modelName = firstCore.get("model name"); if (modelName == null) modelName = firstCore.get("Model Name"); + if (modelName == null) + modelName = firstCore.get("Model name"); if (modelName == null) modelName = firstCore.get("cpu model"); if (modelName != null) { - builder.setName(modelName); + builder.setName(CentralProcessor.cleanName(modelName)); builder.setVendor(HardwareVendor.of(firstCore.get("vendor_id"))); if (builder.getVendor() == null && modelName.startsWith("Loongson")) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WindowsCPUDetector.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WindowsCPUDetector.java index 589b95617..a360a6710 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WindowsCPUDetector.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WindowsCPUDetector.java @@ -33,7 +33,7 @@ final class WindowsCPUDetector { Object vendor = reg.queryValue(WinReg.HKEY.HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", "VendorIdentifier"); if (name instanceof String) - builder.setName((String) name); + builder.setName(CentralProcessor.cleanName((String) name)); if (vendor instanceof String) builder.setVendor(HardwareVendor.of((String) vendor)); diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/util/StringUtilsTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/StringUtilsTest.java new file mode 100644 index 000000000..46017d1f8 --- /dev/null +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/StringUtilsTest.java @@ -0,0 +1,50 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2025 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.util; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Glavo + */ +public final class StringUtilsTest { + + @Test + public void testNormalizeWhitespaces() { + assertEquals("", StringUtils.normalizeWhitespaces("")); + assertEquals("", StringUtils.normalizeWhitespaces(" ")); + assertEquals("", StringUtils.normalizeWhitespaces(" \t")); + assertEquals("", StringUtils.normalizeWhitespaces(" \t ")); + + assertEquals("abc", StringUtils.normalizeWhitespaces("abc")); + assertEquals("abc", StringUtils.normalizeWhitespaces(" abc")); + assertEquals("abc", StringUtils.normalizeWhitespaces("abc ")); + assertEquals("abc", StringUtils.normalizeWhitespaces(" abc ")); + assertEquals("abc", StringUtils.normalizeWhitespaces(" \tabc \t")); + + assertEquals("a bc", StringUtils.normalizeWhitespaces("a bc")); + assertEquals("a bc", StringUtils.normalizeWhitespaces("a bc")); + assertEquals("a bc", StringUtils.normalizeWhitespaces("a \tbc")); + assertEquals("a bc", StringUtils.normalizeWhitespaces(" a \tbc ")); + assertEquals("a b c", StringUtils.normalizeWhitespaces(" a\tb c ")); + assertEquals("a b c", StringUtils.normalizeWhitespaces(" a \t b c ")); + assertEquals("a b c", StringUtils.normalizeWhitespaces(" a \t b c ")); + } +} diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/util/platform/hardware/CentralProcessorTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/platform/hardware/CentralProcessorTest.java new file mode 100644 index 000000000..2019f2547 --- /dev/null +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/platform/hardware/CentralProcessorTest.java @@ -0,0 +1,68 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2025 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.util.platform.hardware; + +import org.junit.jupiter.api.Test; + +import static org.jackhuang.hmcl.util.platform.hardware.CentralProcessor.cleanName; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Glavo + */ +public final class CentralProcessorTest { + + @Test + public void testCleanName() { + assertEquals("Intel Core i7-12700K", cleanName("12th Gen Intel(R) Core(TM) i7-12700K")); + assertEquals("Intel Core Ultra 9 285K", cleanName("Intel(R) Core(TM) Ultra 9 285K")); + assertEquals("Intel Xeon Platinum 8380", cleanName("Intel(R) Xeon(R) Platinum 8380 CPU @ 2.30GHz")); + assertEquals("Intel Xeon E5-2660 v2", cleanName("Intel(R) Xeon(R) CPU E5-2660 v2 @ 2.20GHz")); + assertEquals("Intel Xeon Phi 7250", cleanName("Intel(R) Xeon Phi(TM) CPU 7250 @ 1.40GHz")); + assertEquals("Intel Celeron N5105", cleanName("Intel(R) Celeron(R) N5105 @ 2.00GHz")); + assertEquals("Intel Pentium Silver J5005", cleanName("Intel(R) Pentium(R) Silver J5005 CPU @ 1.50GHz")); + assertEquals("Intel Atom E3940", cleanName("Intel(R) Atom(TM) Processor E3940 @ 1.60GHz")); + assertEquals("Intel Core i7 X 990", cleanName("Intel(R) Core(TM) i7 CPU X 990 @ 3.47GHz")); + assertEquals("Intel Core 2 Duo T7500", cleanName("Intel(R) Core(TM)2 Duo CPU T7500 @ 2.20GHz")); + assertEquals("Intel Core 2 Quad Q9500", cleanName("Intel(R) Core(TM)2 Quad CPU Q9500 @ 2.83GHz")); + + assertEquals("AMD Ryzen 7 7840HS", cleanName("AMD Ryzen 7 7840HS w/ Radeon 780M Graphics")); + assertEquals("AMD Ryzen 7 6800U", cleanName("AMD Ryzen 7 6800U with Radeon Graphics")); + assertEquals("AMD Ryzen 7 5800X", cleanName("AMD Ryzen 7 5800X 8-Core Processor")); + assertEquals("AMD Ryzen 5 2400G", cleanName("AMD Ryzen 5 2400G with Radeon Vega Graphics")); + assertEquals("AMD EPYC 7713", cleanName("AMD EPYC 7713 64-Core Processor")); + assertEquals("AMD Ryzen Threadripper 3960X", cleanName("AMD Ryzen Threadripper 3960X 24-Core Processor")); + assertEquals("AMD Ryzen Threadripper PRO 5995WX", cleanName("AMD Ryzen Threadripper PRO 5995WX 64-Cores")); + assertEquals("AMD Ryzen Embedded V2748", cleanName("AMD Ryzen Embedded V2748 with Radeon Graphics")); + assertEquals("AMD A8-7410", cleanName("AMD A8-7410 APU with AMD Radeon R5 Graphics")); + assertEquals("AMD FX-8350", cleanName("AMD FX(tm)-8350 Eight-Core Processor")); + assertEquals("AMD Phenom II X6 1055T", cleanName("AMD Phenom(tm) II X6 1055T Processor")); + assertEquals("AMD Athlon 5350", cleanName("AMD Athlon(tm) 5350 APU with Radeon(tm) R3")); + + assertEquals("Hygon C86 7285", cleanName("Hygon C86 7285 32-core Processor")); + assertEquals("Hygon C86 3250", cleanName("Hygon C86 3250 8-core Processor")); + + assertEquals("ZHAOXIN KaiXian KX-6640MA", cleanName("ZHAOXIN KaiXian KX-6640MA@2.2+GHz")); + assertEquals("ZHAOXIN KaiXian KX-U6780A", cleanName("ZHAOXIN KaiXian KX-U6780A@2.7GHz")); + assertEquals("ZHAOXIN KaiSheng KH-40000/16", cleanName("ZHAOXIN KaiSheng KH-40000/16@2.2GHz")); + assertEquals("ZHAOXIN KaiSheng KH-37800D", cleanName("ZHAOXIN KaiSheng KH-37800D@2.7GHz")); + + assertEquals("Loongson-3A3000", cleanName("Loongson-3A R3 (Loongson-3A3000) @ 1400MHz")); + assertEquals("Loongson-3B4000", cleanName("Loongson-3A R4 (Loongson-3B4000) @ 1800MHz")); + } +}