diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java index 59e016a23..8039cc54e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java @@ -40,6 +40,7 @@ import org.jackhuang.hmcl.util.platform.Architecture; import org.jackhuang.hmcl.util.platform.CommandBuilder; import org.jackhuang.hmcl.util.platform.NativeUtils; import org.jackhuang.hmcl.util.platform.OperatingSystem; +import org.jackhuang.hmcl.util.platform.SystemInfo; import java.io.File; import java.io.IOException; @@ -258,6 +259,9 @@ public final class Launcher extends Application { LOG.info("XDG Session Type: " + System.getenv("XDG_SESSION_TYPE")); LOG.info("XDG Current Desktop: " + System.getenv("XDG_CURRENT_DESKTOP")); } + + Lang.thread(SystemInfo::initialize, "Detection System Information", true); + launch(Launcher.class, args); } catch (Throwable e) { // Fucking JavaFX will suppress the exception and will break our crash reporter. CRASH_REPORTER.uncaughtException(Thread.currentThread(), e); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModUpdatesPage.java index fcd284104..05209706f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModUpdatesPage.java @@ -46,7 +46,6 @@ import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.io.CSVTable; import java.net.URL; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.LocalDateTime; @@ -157,7 +156,7 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage { Path path = Paths.get("hmcl-mod-update-list-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH-mm-ss")) + ".csv").toAbsolutePath(); Controllers.taskDialog(Task.runAsync(() -> { - CSVTable csvTable = CSVTable.createEmpty(); + CSVTable csvTable = new CSVTable(); csvTable.set(0, 0, "Source File Name"); csvTable.set(1, 0, "Current Version"); @@ -171,7 +170,7 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage { csvTable.set(3, i + 1, objects.get(i).source.get()); } - csvTable.write(Files.newOutputStream(path)); + csvTable.write(path); FXUtils.showFileInExplorer(path); }).whenComplete(Schedulers.javafx(), exception -> { diff --git a/HMCLCore/build.gradle.kts b/HMCLCore/build.gradle.kts index 098ee5bd0..18b2c6c45 100644 --- a/HMCLCore/build.gradle.kts +++ b/HMCLCore/build.gradle.kts @@ -16,6 +16,7 @@ dependencies { api(libs.jsoup) api(libs.chardet) api(libs.jna) + api(libs.pci.ids) compileOnlyApi(libs.jetbrains.annotations) } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/CSVTable.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/CSVTable.java index a839cebf1..098ad4df9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/CSVTable.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/CSVTable.java @@ -19,66 +19,113 @@ package org.jackhuang.hmcl.util.io; import org.jackhuang.hmcl.util.InfiniteSizeList; +import org.jetbrains.annotations.NotNull; import java.io.*; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; public final class CSVTable { - private final List> table = new InfiniteSizeList<>(); + private int columnCount = 0; + private int rowCount = 0; + private final InfiniteSizeList> table = new InfiniteSizeList<>(); - private CSVTable() { + public CSVTable() { } - public static CSVTable createEmpty() { - return new CSVTable(); + public int getColumnCount() { + return columnCount; } - public String get(int x, int y) { + public int getRowCount() { + return rowCount; + } + + public @NotNull String get(int x, int y) { List row = table.get(y); if (row == null) { - return null; + return ""; } - return row.get(x); + String value = row.get(x); + return value != null ? value : ""; } public void set(int x, int y, String txt) { - List row = table.get(y); + if (x < 0 || y < 0) + throw new IllegalArgumentException("x or y must be greater than or equal to 0"); + + InfiniteSizeList row = table.get(y); if (row == null) { row = new InfiniteSizeList<>(x); table.set(y, row); } row.set(x, txt); + + columnCount = Integer.max(columnCount, x + 1); + rowCount = Integer.max(rowCount, y + 1); } - public void write(OutputStream outputStream) throws IOException { - try (PrintWriter printWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter( - outputStream, StandardCharsets.UTF_8)), false - )) { - for (List row : this.table) { - if (row != null) { - for (int j = 0; j < row.size(); j++) { - String txt = row.get(j); - if (txt != null) { - printWriter.write(this.escape(txt)); - } + public void write(Path file) throws IOException { + try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) { + write(writer); + } + } - if (j != row.size() - 1) { - printWriter.write(','); + public void write(Appendable out) throws IOException { + for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) { + List row = this.table.get(rowIndex); + if (row != null) { + for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) { + String txt = row.get(columnIndex); + if (txt != null) { + if (txt.indexOf('"') < 0 && txt.indexOf(',') < 0 && txt.indexOf('\n') < 0 && txt.indexOf('\r') < 0) + out.append(txt); + else { + out.append('"'); + for (int i = 0; i < txt.length(); i++) { + char c = txt.charAt(i); + switch (c) { + case '"': + out.append("\\\""); + break; + case '\r': + out.append("\\r"); + break; + case '\n': + out.append("\\n"); + break; + default: + out.append(c); + break; + } + } + out.append('"'); } } - } - printWriter.write('\n'); + if (columnIndex < columnCount - 1) + out.append(','); + } + } else { + for (int columnIndex = 0; columnIndex < columnCount - 1; columnIndex++) { + out.append(','); + } } + + out.append('\n'); } } - private String escape(String txt) { - if (!txt.contains("\"") && !txt.contains(",")) { - return txt; - } else { - return "\"" + txt.replace("\"", "\"\"") + "\""; + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + try { + write(builder); + } catch (IOException e) { + throw new UncheckedIOException(e); } + return builder.toString(); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/SystemInfo.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/SystemInfo.java new file mode 100644 index 000000000..f0096e926 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/SystemInfo.java @@ -0,0 +1,85 @@ +/* + * 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; + +import org.jackhuang.hmcl.util.platform.hardware.GraphicsCard; +import org.jackhuang.hmcl.util.platform.hardware.HardwareDetector; +import org.jackhuang.hmcl.util.platform.linux.LinuxHardwareDetector; +import org.jackhuang.hmcl.util.platform.macos.MacOSHardwareDetector; +import org.jackhuang.hmcl.util.platform.windows.WindowsHardwareDetector; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +import static org.jackhuang.hmcl.util.logging.Logger.LOG; + +public final class SystemInfo { + + private static final class Holder { + public static final HardwareDetector DETECTOR; + + static { + if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) + DETECTOR = new WindowsHardwareDetector(); + else if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX) + DETECTOR = new LinuxHardwareDetector(); + else if (OperatingSystem.CURRENT_OS == OperatingSystem.OSX) + DETECTOR = new MacOSHardwareDetector(); + else + DETECTOR = new HardwareDetector(); + } + + public static final @Nullable List GRAPHICS_CARDS = DETECTOR.detectGraphicsCards(); + } + + public static void initialize() { + StringBuilder builder = new StringBuilder("System Info:"); + List graphicsCards = getGraphicsCards(); + + if (graphicsCards != null) { + if (graphicsCards.isEmpty()) + builder.append("\n - GPU: Not Found"); + else if (graphicsCards.size() == 1) + builder.append("\n - GPU: ").append(graphicsCards.get(0)); + else { + int index = 1; + for (GraphicsCard graphicsCard : graphicsCards) { + builder.append("\n - GPU ").append(index++).append(": ").append(graphicsCard); + } + } + } + + OperatingSystem.PhysicalMemoryStatus memoryStatus = OperatingSystem.getPhysicalMemoryStatus(); + if (memoryStatus.getTotal() > 0 && memoryStatus.getAvailable() > 0) { + builder.append("\n - Memory: ") + .append(String.format("%.2f GiB / %.2f GiB (%d%%)", + memoryStatus.getUsedGB(), memoryStatus.getTotalGB(), + (int) (((double) memoryStatus.getUsed() / memoryStatus.getTotal()) * 100) + )); + } + + LOG.info(builder.toString()); + } + + public static @Nullable List getGraphicsCards() { + return Holder.GRAPHICS_CARDS; + } + + private SystemInfo() { + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/hardware/GraphicsCard.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/hardware/GraphicsCard.java new file mode 100644 index 000000000..715f7c207 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/hardware/GraphicsCard.java @@ -0,0 +1,261 @@ +/* + * 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.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * @author Glavo + */ +public final class GraphicsCard { + + public static Builder builder() { + return new Builder(); + } + + private final String name; + private final @Nullable Vendor vendor; + private final @Nullable Type type; + private final @Nullable String driver; + private final @Nullable String driverVersion; + + private GraphicsCard(String name, @Nullable Vendor vendor, @Nullable Type type, @Nullable String driver, @Nullable String driverVersion) { + this.name = Objects.requireNonNull(name); + this.vendor = vendor; + this.type = type; + this.driver = driver; + this.driverVersion = driverVersion; + } + + public String getName() { + return name; + } + + public @Nullable Vendor getVendor() { + return vendor; + } + + public @Nullable String getDriverVersion() { + return driverVersion; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(name); + + if (type != null) { + builder.append(" [").append(type).append(']'); + } + + return builder.toString(); + } + + public static final class Vendor { + public static final Vendor INTEL = new Vendor("Intel"); + public static final Vendor NVIDIA = new Vendor("NVIDIA"); + public static final Vendor AMD = new Vendor("AMD"); + public static final Vendor APPLE = new Vendor("Apple"); + public static final Vendor QUALCOMM = new Vendor("Qualcomm"); + public static final Vendor MTK = new Vendor("MTK"); + public static final Vendor VMWARE = new Vendor("VMware"); + public static final Vendor PARALLEL = new Vendor("Parallel"); + public static final Vendor MICROSOFT = new Vendor("Microsoft"); + public static final Vendor MOORE_THREADS = new Vendor("Moore Threads"); + public static final Vendor BROADCOM = new Vendor("Broadcom"); + public static final Vendor IMG = new Vendor("Imagination"); + public static final Vendor LOONGSON = new Vendor("Loongson"); + public static final Vendor JINGJIA_MICRO = new Vendor("Jingjia Micro"); + public static final Vendor HUAWEI = new Vendor("Huawei"); + public static final Vendor ZHAOXIN = new Vendor("Zhaoxin"); + + public static @Nullable Vendor getKnown(String name) { + if (name == null) + return null; + + String lower = name.toLowerCase(Locale.ROOT); + if (lower.startsWith("intel")) return INTEL; + if (lower.startsWith("nvidia")) return NVIDIA; + if (lower.startsWith("advanced micro devices") + || (lower.startsWith("amd") && !(lower.length() > 3 && Character.isAlphabetic(lower.charAt(3))))) + return AMD; + if (lower.equals("brcm") || lower.startsWith("broadcom")) return BROADCOM; + if (lower.startsWith("mediatek")) return MTK; + if (lower.startsWith("qualcomm")) return QUALCOMM; + if (lower.startsWith("apple")) return APPLE; + if (lower.startsWith("microsoft")) return MICROSOFT; + if (lower.startsWith("imagination") || lower.equals("img")) return IMG; + + if (lower.startsWith("loongson")) return LOONGSON; + if (lower.startsWith("moore threads")) return MOORE_THREADS; + if (lower.startsWith("jingjia")) return JINGJIA_MICRO; + if (lower.startsWith("huawei")) return HUAWEI; + if (lower.startsWith("zhaoxin")) return ZHAOXIN; + + return null; + } + + @Contract("null -> null; !null -> !null") + public static Vendor of(String name) { + if (name == null) + return null; + + Vendor known = getKnown(name); + return known != null ? known : new Vendor(name); + } + + public static @Nullable Vendor ofId(int vendorId) { + // https://devicehunt.com/all-pci-vendors + switch (vendorId) { + case 0x106b: + return APPLE; + case 0x1002: + case 0x1022: + case 0x1dd8: // AMD Pensando Systems + case 0x1924: // AMD Solarflare + return AMD; + case 0x8086: + case 0x8087: + case 0x03e7: + return INTEL; + case 0x0955: + case 0x10de: + case 0x12d2: + return NVIDIA; + case 0x1ed5: + return MOORE_THREADS; + case 0x168c: + case 0x5143: + return QUALCOMM; + case 0x14c3: + return MTK; + case 0x15ad: + return VMWARE; + case 0x1ab8: + return PARALLEL; + case 0x1414: + return MICROSOFT; + case 0x182f: + case 0x14e4: + return BROADCOM; + case 0x0014: + return LOONGSON; + case 0x0731: + return JINGJIA_MICRO; + case 0x19e5: + return HUAWEI; + case 0x1d17: + return ZHAOXIN; + default: + return null; + } + } + + private final String name; + + public Vendor(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + return o instanceof Vendor && name.equals(((Vendor) o).name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public String toString() { + return name; + } + } + + public enum Type { + Integrated, + Discrete + } + + public static final class Builder { + private String name; + private Vendor vendor; + private Type type; + private String driver; + private String driverVersion; + + public GraphicsCard build() { + if (name == null) + throw new IllegalStateException("Name not set"); + + return new GraphicsCard(name, vendor, type, driver, driverVersion); + } + + public String getName() { + return name; + } + + public Builder setName(String name) { + this.name = name; + return this; + } + + public Vendor getVendor() { + return vendor; + } + + public Builder setVendor(Vendor vendor) { + this.vendor = vendor; + return this; + } + + public Type getType() { + return type; + } + + public Builder setType(Type type) { + this.type = type; + return this; + } + + public String getDriver() { + return driver; + } + + public Builder setDriver(String driver) { + this.driver = driver; + return this; + } + + public String getDriverVersion() { + return driverVersion; + } + + public Builder setDriverVersion(String driverVersion) { + this.driverVersion = driverVersion; + return this; + } + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/hardware/HardwareDetector.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/hardware/HardwareDetector.java new file mode 100644 index 000000000..52dc1ddeb --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/hardware/HardwareDetector.java @@ -0,0 +1,31 @@ +/* + * 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.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author Glavo + */ +public class HardwareDetector { + public @Nullable List detectGraphicsCards() { + return null; + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/linux/LinuxGPUDetector.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/linux/LinuxGPUDetector.java new file mode 100644 index 000000000..38657f4fc --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/linux/LinuxGPUDetector.java @@ -0,0 +1,315 @@ +/* + * 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.linux; + +import org.glavo.pci.ids.PCIIDsDatabase; +import org.glavo.pci.ids.model.Device; +import org.glavo.pci.ids.model.Vendor; +import org.jackhuang.hmcl.util.Lang; +import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.platform.hardware.GraphicsCard; + +import java.io.*; +import java.lang.ref.SoftReference; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import static org.jackhuang.hmcl.util.logging.Logger.LOG; + +/** + * @author Glavo + * @see gpu_linux.c + */ +final class LinuxGPUDetector { + private static volatile SoftReference databaseCache; + + private static final Pattern PCI_MODALIAS_PATTERN = + Pattern.compile("pci:v(?\\p{XDigit}{8})d(?\\p{XDigit}{8})sv(?\\p{XDigit}{8})sd(?\\p{XDigit}{8})bc(?\\p{XDigit}{2})sc(?\\p{XDigit}{2})i\\p{XDigit}{2}"); + private static final Pattern PCI_DEVICE_PATTERN = + Pattern.compile("(?\\p{XDigit}+):(?\\p{XDigit}+):(?\\p{XDigit}+)\\.(?\\p{XDigit}+)"); + private static final Pattern OF_DEVICE_PATTERN = + Pattern.compile("of:NgpuT[^C]*C(?.*)"); + + private static PCIIDsDatabase getPCIIDsDatabase() { + SoftReference databaseWeakReference = LinuxGPUDetector.databaseCache; + PCIIDsDatabase pciIDsDatabase; + + if (databaseCache != null) { + pciIDsDatabase = databaseWeakReference.get(); + if (pciIDsDatabase != null) { + return pciIDsDatabase; + } + } + + for (String path : new String[]{ + "/usr/share/misc/pci.ids", + "/usr/share/hwdata/pci.ids", + "/usr/local/share/hwdata/pci.ids" + }) { + Path p = Paths.get(path); + if (Files.isRegularFile(p)) { + try { + pciIDsDatabase = PCIIDsDatabase.load(p); + databaseCache = new SoftReference<>(pciIDsDatabase); + return pciIDsDatabase; + } catch (IOException e) { + return null; + } + } + } + + return null; + } + + private static void detectDriver(GraphicsCard.Builder builder, Path deviceDir) { + try { + Path driverDir = Files.readSymbolicLink(deviceDir.resolve("driver")); + if (driverDir.getNameCount() > 0) { + + String name = driverDir.getName(driverDir.getNameCount() - 1).toString(); + builder.setDriver(name); + + Path versionFile = deviceDir.resolve("driver/module/version"); + if (Files.isRegularFile(versionFile)) { + builder.setDriverVersion(FileUtils.readText(versionFile).trim()); + } else if ("zx".equals(name)) { + versionFile = deviceDir.resolve("zx_info/driver_version"); + if (Files.isRegularFile(versionFile)) { + builder.setDriverVersion(FileUtils.readText(versionFile).trim()); + } + } + } + } catch (IOException ignored) { + } + } + + private static GraphicsCard detectPCI(Path deviceDir, String modalias) throws IOException { + Matcher matcher = PCI_MODALIAS_PATTERN.matcher(modalias); + if (!matcher.matches()) + return null; + + GraphicsCard.Builder builder = GraphicsCard.builder(); + + int vendorId = Integer.parseInt(matcher.group("vendorId"), 16); + int deviceId = Integer.parseInt(matcher.group("deviceId"), 16); + int classId = Integer.parseInt(matcher.group("classId"), 16); + int subclassId = Integer.parseInt(matcher.group("subclassId"), 16); + + if (classId != 0x03) // PCI_BASE_CLASS_DISPLAY + return null; // Not a GPU device + + Path pciPath = Files.readSymbolicLink(deviceDir); + int nameCount = pciPath.getNameCount(); + if (nameCount == 0) + return null; + + matcher = PCI_DEVICE_PATTERN.matcher(pciPath.getName(nameCount - 1).toString()); + if (!matcher.matches()) + return null; + + int pciDomain = Integer.parseInt(matcher.group("pciDomain"), 16); + int pciBus = Integer.parseInt(matcher.group("pciBus"), 16); + int pciDevice = Integer.parseInt(matcher.group("pciDevice"), 16); + int pciFunc = Integer.parseInt(matcher.group("pciFunc"), 16); + + builder.setVendor(GraphicsCard.Vendor.ofId(vendorId)); + + detectDriver(builder, deviceDir); + + try { + if (builder.getVendor() == GraphicsCard.Vendor.AMD) { + Path hwmon = deviceDir.resolve("hwmon"); + try (Stream subDirs = Files.list(hwmon)) { + for (Path subDir : Lang.toIterable(subDirs)) { + if (Files.isDirectory(subDir) && !subDir.getFileName().toString().startsWith(".")) { + builder.setType(Files.exists(subDir.resolve("in1_input")) + ? GraphicsCard.Type.Integrated + : GraphicsCard.Type.Discrete); + break; + } + } + } catch (IOException ignored) { + } + + Path revisionFile = deviceDir.resolve("revision"); + if (Files.isRegularFile(revisionFile)) { + String revisionString = FileUtils.readText(revisionFile).trim(); + int revision = Integer.decode(revisionString); + String prefix = String.format("%X,\t%X,\t", deviceId, revision); + //noinspection DataFlowIssue + try (BufferedReader reader = new BufferedReader(new InputStreamReader( + LinuxHardwareDetector.class.getResourceAsStream("/assets/platform/amdgpu.ids"), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + if (line.startsWith(prefix)) { + builder.setName(line.substring(prefix.length())); + break; + } + } + } + } + + } else if (builder.getVendor() == GraphicsCard.Vendor.INTEL) { + builder.setType(pciDevice == 20 ? GraphicsCard.Type.Integrated : GraphicsCard.Type.Discrete); + } + } catch (Throwable ignored) { + } + + if (builder.getName() == null || builder.getVendor() == null) { + PCIIDsDatabase database = getPCIIDsDatabase(); + if (database != null) { + Vendor vendor = database.findVendor(vendorId); + if (vendor != null) { + if (builder.getVendor() == null) + builder.setVendor(GraphicsCard.Vendor.of(vendor.getName())); + + if (builder.getName() == null) { + Device device = vendor.getDevices().get(deviceId); + if (device != null) { + matcher = Pattern.compile(".*\\[(?.*)]").matcher(device.getName()); + if (matcher.matches()) + builder.setName(builder.getVendor() + " " + matcher.group("name")); + else + builder.setName(builder.getVendor() + " " + device.getName()); + } + } + } + } + } + + if (builder.getName() == null) { + String subclassStr; + switch (subclassId) { + case 0: // PCI_CLASS_DISPLAY_VGA + subclassStr = " (VGA compatible)"; + break; + case 1: // PCI_CLASS_DISPLAY_XGA + subclassStr = " (XGA compatible)"; + break; + case 2: // PCI_CLASS_DISPLAY_3D + subclassStr = " (3D)"; + break; + default: + subclassStr = ""; + } + + builder.setName(String.format("%s Device %04X%s", + builder.getVendor() != null ? builder.getVendor().toString() : "Unknown", + deviceId, subclassStr)); + } + + if (builder.getType() == null) { + if (builder.getVendor() == GraphicsCard.Vendor.NVIDIA) { + if (builder.getName().startsWith("GeForce") + || builder.getName().startsWith("Quadro") + || builder.getName().startsWith("Tesla")) + builder.setType(GraphicsCard.Type.Discrete); + + } else if (builder.getVendor() == GraphicsCard.Vendor.MOORE_THREADS) { + if (builder.getName().startsWith("MTT ")) + builder.setType(GraphicsCard.Type.Discrete); + } + } + + return builder.build(); + } + + private static GraphicsCard detectOF(Path deviceDir, String modalias) throws IOException { + Matcher matcher = OF_DEVICE_PATTERN.matcher(modalias); + if (!matcher.matches()) + return null; + + GraphicsCard.Builder builder = new GraphicsCard.Builder(); + + String compatible = matcher.group("compatible"); + int idx = compatible.indexOf(','); + if (idx < 0) { + builder.setName(compatible.trim()); + } else { + String vendorName = compatible.substring(0, idx).trim(); + GraphicsCard.Vendor vendor = GraphicsCard.Vendor.getKnown(vendorName); + if (vendor == null) + vendor = new GraphicsCard.Vendor(vendorName.toUpperCase(Locale.ROOT)); + + builder.setName(vendor + " " + compatible.substring(idx + 1).trim()); + builder.setVendor(vendor); + } + + builder.setType(GraphicsCard.Type.Integrated); + + detectDriver(builder, deviceDir); + return builder.build(); + } + + public static List detectAll() { + Path drm = Paths.get("/sys/class/drm"); + if (!Files.isDirectory(drm)) + return Collections.emptyList(); + + ArrayList cards = new ArrayList<>(); + + try (Stream stream = Files.list(drm)) { + for (Path deviceRoot : Lang.toIterable(stream)) { + Path dirName = deviceRoot.getFileName(); + if (dirName == null) + continue; + + String name = dirName.toString(); + if (!name.startsWith("card") || name.indexOf('-') >= 0) + continue; + + Path deviceDir = deviceRoot.resolve("device"); + Path modaliasFile = deviceDir.resolve("modalias"); + if (!Files.isRegularFile(modaliasFile)) + continue; + + try { + String modalias = FileUtils.readText(modaliasFile).trim(); + GraphicsCard graphicsCard = null; + if (modalias.startsWith("pci:")) + graphicsCard = detectPCI(deviceDir, modalias); + else if (modalias.startsWith("of:")) + graphicsCard = detectOF(deviceDir, modalias); + + if (graphicsCard != null) + cards.add(graphicsCard); + } catch (IOException ignored) { + } + } + } catch (Throwable e) { + LOG.warning("Failed to get graphics card info", e); + } finally { + databaseCache = null; + System.gc(); + } + + return Collections.unmodifiableList(cards); + } + + private LinuxGPUDetector() { + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/linux/LinuxHardwareDetector.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/linux/LinuxHardwareDetector.java new file mode 100644 index 000000000..a1793b264 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/linux/LinuxHardwareDetector.java @@ -0,0 +1,37 @@ +/* + * 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.linux; + +import org.jackhuang.hmcl.util.platform.OperatingSystem; +import org.jackhuang.hmcl.util.platform.hardware.GraphicsCard; +import org.jackhuang.hmcl.util.platform.hardware.HardwareDetector; + +import java.util.List; + +/** + * @author Glavo + */ +public final class LinuxHardwareDetector extends HardwareDetector { + + @Override + public List detectGraphicsCards() { + if (OperatingSystem.CURRENT_OS != OperatingSystem.LINUX) + return null; + return LinuxGPUDetector.detectAll(); + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/macos/MacOSHardwareDetector.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/macos/MacOSHardwareDetector.java new file mode 100644 index 000000000..4214d62f7 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/macos/MacOSHardwareDetector.java @@ -0,0 +1,126 @@ +/* + * 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.macos; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.jackhuang.hmcl.task.Schedulers; +import org.jackhuang.hmcl.util.Lang; +import org.jackhuang.hmcl.util.StringUtils; +import org.jackhuang.hmcl.util.gson.JsonUtils; +import org.jackhuang.hmcl.util.io.IOUtils; +import org.jackhuang.hmcl.util.platform.OperatingSystem; +import org.jackhuang.hmcl.util.platform.hardware.GraphicsCard; +import org.jackhuang.hmcl.util.platform.hardware.HardwareDetector; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.jackhuang.hmcl.util.logging.Logger.LOG; + +/** + * @author Glavo + */ +public final class MacOSHardwareDetector extends HardwareDetector { + + @Override + public List detectGraphicsCards() { + if (OperatingSystem.CURRENT_OS != OperatingSystem.OSX) + return null; + + Process process = null; + String json = null; + try { + File devNull = new File("/dev/null"); + + Process finalProcess = process = new ProcessBuilder("/usr/sbin/system_profiler", + "SPDisplaysDataType", + "-json") + .redirectInput(devNull) + .redirectError(devNull) + .start(); + + CompletableFuture future = CompletableFuture.supplyAsync(Lang.wrap(() -> + IOUtils.readFullyAsString(finalProcess.getInputStream(), OperatingSystem.NATIVE_CHARSET)), + Schedulers.io()); + + if (!process.waitFor(15, TimeUnit.SECONDS)) + throw new TimeoutException(); + + if (process.exitValue() != 0) + throw new IOException("Bad exit code: " + process.exitValue()); + + json = future.get(); + + JsonObject object = JsonUtils.GSON.fromJson(json, JsonObject.class); + JsonArray spDisplaysDataType = object.getAsJsonArray("SPDisplaysDataType"); + if (spDisplaysDataType != null) { + ArrayList cards = new ArrayList<>(); + + for (JsonElement element : spDisplaysDataType) { + JsonObject item = element.getAsJsonObject(); + + JsonElement deviceType = item.get("sppci_device_type"); + if (deviceType == null || !"spdisplays_gpu".equals(deviceType.getAsString())) + continue; + + JsonElement model = item.getAsJsonPrimitive("sppci_model"); + JsonElement vendor = item.getAsJsonPrimitive("spdisplays_vendor"); + JsonElement bus = item.getAsJsonPrimitive("sppci_bus"); + + if (model == null) + continue; + + GraphicsCard.Builder builder = GraphicsCard.builder() + .setName(model.getAsString()); + + if (vendor != null) + builder.setVendor(GraphicsCard.Vendor.of(StringUtils.removePrefix(vendor.getAsString(), "sppci_vendor_"))); + + GraphicsCard.Type type = GraphicsCard.Type.Integrated; + if (bus != null) { + String lower = bus.getAsString().toLowerCase(Locale.ROOT); + if (!lower.contains("builtin") && !lower.contains("built_in") & !lower.contains("built-in")) + type = GraphicsCard.Type.Discrete; + } + builder.setType(type); + + cards.add(builder.build()); + } + + return Collections.unmodifiableList(cards); + } + } catch (Throwable e) { + if (process != null && process.isAlive()) + process.destroy(); + + LOG.warning("Failed to get graphics card info" + (json != null ? ": " + json : ""), e); + return Collections.emptyList(); + } + + return Collections.emptyList(); + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WinReg.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WinReg.java new file mode 100644 index 000000000..f725197cf --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WinReg.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.platform.windows; + +/** + * @author Glavo + */ +public abstract class WinReg { + + /** + * @see Predefined Keys + */ + public enum HKEY { + HKEY_CLASSES_ROOT(0x80000000), + HKEY_CURRENT_USER(0x80000001), + HKEY_LOCAL_MACHINE(0x80000002), + HKEY_USERS(0x80000003), + HKEY_PERFORMANCE_DATA(0x80000004), + HKEY_PERFORMANCE_TEXT(0x80000050), + HKEY_PERFORMANCE_NLSTEXT(0x80000060), + HKEY_CURRENT_CONFIG(0x80000005), + HKEY_DYN_DATA(0x80000006), + HKEY_CURRENT_USER_LOCAL_SETTINGS(0x80000007); + + private final int value; + + HKEY(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WindowsHardwareDetector.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WindowsHardwareDetector.java new file mode 100644 index 000000000..a8e91470a --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/windows/WindowsHardwareDetector.java @@ -0,0 +1,136 @@ +/* + * 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.windows; + +import org.jackhuang.hmcl.task.Schedulers; +import org.jackhuang.hmcl.util.Lang; +import org.jackhuang.hmcl.util.StringUtils; +import org.jackhuang.hmcl.util.io.IOUtils; +import org.jackhuang.hmcl.util.platform.OperatingSystem; +import org.jackhuang.hmcl.util.platform.hardware.GraphicsCard; +import org.jackhuang.hmcl.util.platform.hardware.HardwareDetector; + +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.jackhuang.hmcl.util.logging.Logger.LOG; + +/** + * @author Glavo + */ +public final class WindowsHardwareDetector extends HardwareDetector { + + private static List> parsePowerShellFormatList(Iterable lines) { + ArrayList> result = new ArrayList<>(); + Map current = new LinkedHashMap<>(); + + for (String line : lines) { + int idx = line.indexOf(':'); + + if (idx < 0) { + if (!current.isEmpty()) { + result.add(current); + current = new LinkedHashMap<>(); + } + continue; + } + + String key = line.substring(0, idx).trim(); + String value = line.substring(idx + 1).trim(); + + current.put(key, value); + } + + if (!current.isEmpty()) + result.add(current); + + return result; + } + + @Override + public List detectGraphicsCards() { + if (!OperatingSystem.isWindows7OrLater()) + return null; + + Process process = null; + String list = null; + try { + File nul = new File("NUL"); + + String getCimInstance = OperatingSystem.SYSTEM_VERSION.startsWith("6.1") + ? "Get-WmiObject" + : "Get-CimInstance"; + + Process finalProcess = process = new ProcessBuilder("powershell.exe", + "-Command", + String.join(" | ", + getCimInstance + " -Class Win32_VideoController", + "Select-Object Name,AdapterCompatibility,DriverVersion,AdapterDACType", + "Format-List" + )) + .redirectInput(nul) + .redirectError(nul) + .start(); + + CompletableFuture future = CompletableFuture.supplyAsync(Lang.wrap(() -> + IOUtils.readFullyAsString(finalProcess.getInputStream(), OperatingSystem.NATIVE_CHARSET)), + Schedulers.io()); + + if (!process.waitFor(15, TimeUnit.SECONDS)) + throw new TimeoutException(); + + if (process.exitValue() != 0) + throw new IOException("Bad exit code: " + process.exitValue()); + + list = future.get(); + + List> videoControllers = parsePowerShellFormatList(Arrays.asList(list.split("\\R"))); + ArrayList cards = new ArrayList<>(videoControllers.size()); + for (Map videoController : videoControllers) { + String name = videoController.get("Name"); + String adapterCompatibility = videoController.get("AdapterCompatibility"); + String driverVersion = videoController.get("DriverVersion"); + String adapterDACType = videoController.get("AdapterDACType"); + + if (StringUtils.isNotBlank(name)) { + cards.add(GraphicsCard.builder().setName(name) + .setVendor(GraphicsCard.Vendor.of(adapterCompatibility)) + .setDriverVersion(driverVersion) + .setType(StringUtils.isBlank(adapterDACType) + || "Internal".equalsIgnoreCase(adapterDACType) + || "InternalDAC".equalsIgnoreCase(adapterDACType) + ? GraphicsCard.Type.Integrated + : GraphicsCard.Type.Discrete) + .build() + ); + } + } + + return cards; + } catch (Throwable e) { + if (process != null && process.isAlive()) + process.destroy(); + LOG.warning("Failed to get graphics card info" + (list != null ? ": " + list : ""), e); + return Collections.emptyList(); + } + } +} diff --git a/HMCLCore/src/main/resources/assets/platform/amdgpu.ids b/HMCLCore/src/main/resources/assets/platform/amdgpu.ids new file mode 100644 index 000000000..21da6e314 --- /dev/null +++ b/HMCLCore/src/main/resources/assets/platform/amdgpu.ids @@ -0,0 +1,690 @@ +# https://gitlab.freedesktop.org/mesa/drm/-/blob/b65d6ede3ebe64dbde5805007ddef22f390f8050/data/amdgpu.ids +# +# List of AMDGPU IDs +# +# Syntax: +# device_id, revision_id, product_name <-- single tab after comma + +1.0.0 +1114, C2, AMD Radeon 860M Graphics +1114, C3, AMD Radeon 840M Graphics +1114, D2, AMD Radeon 860M Graphics +1114, D3, AMD Radeon 840M Graphics +1309, 00, AMD Radeon R7 Graphics +130A, 00, AMD Radeon R6 Graphics +130B, 00, AMD Radeon R4 Graphics +130C, 00, AMD Radeon R7 Graphics +130D, 00, AMD Radeon R6 Graphics +130E, 00, AMD Radeon R5 Graphics +130F, 00, AMD Radeon R7 Graphics +130F, D4, AMD Radeon R7 Graphics +130F, D5, AMD Radeon R7 Graphics +130F, D6, AMD Radeon R7 Graphics +130F, D7, AMD Radeon R7 Graphics +1313, 00, AMD Radeon R7 Graphics +1313, D4, AMD Radeon R7 Graphics +1313, D5, AMD Radeon R7 Graphics +1313, D6, AMD Radeon R7 Graphics +1315, 00, AMD Radeon R5 Graphics +1315, D4, AMD Radeon R5 Graphics +1315, D5, AMD Radeon R5 Graphics +1315, D6, AMD Radeon R5 Graphics +1315, D7, AMD Radeon R5 Graphics +1316, 00, AMD Radeon R5 Graphics +1318, 00, AMD Radeon R5 Graphics +131B, 00, AMD Radeon R4 Graphics +131C, 00, AMD Radeon R7 Graphics +131D, 00, AMD Radeon R6 Graphics +1435, AE, AMD Custom GPU 0932 +1506, C1, AMD Radeon 610M +1506, C2, AMD Radeon 610M +1506, C3, AMD Radeon 610M +1506, C4, AMD Radeon 610M +150E, C1, AMD Radeon 890M Graphics +150E, C4, AMD Radeon 890M Graphics +150E, C5, AMD Radeon 890M Graphics +150E, C6, AMD Radeon 890M Graphics +150E, D1, AMD Radeon 890M Graphics +150E, D2, AMD Radeon 890M Graphics +150E, D3, AMD Radeon 890M Graphics +1586, C1, Radeon 8060S Graphics +1586, C2, Radeon 8050S Graphics +1586, C4, Radeon 8050S Graphics +1586, D1, Radeon 8060S Graphics +1586, D2, Radeon 8050S Graphics +1586, D4, Radeon 8050S Graphics +1586, D5, Radeon 8040S Graphics +15BF, 00, AMD Radeon 780M Graphics +15BF, 01, AMD Radeon 760M Graphics +15BF, 02, AMD Radeon 780M Graphics +15BF, 03, AMD Radeon 760M Graphics +15BF, C1, AMD Radeon 780M Graphics +15BF, C2, AMD Radeon 780M Graphics +15BF, C3, AMD Radeon 760M Graphics +15BF, C4, AMD Radeon 780M Graphics +15BF, C5, AMD Radeon 740M Graphics +15BF, C6, AMD Radeon 780M Graphics +15BF, C7, AMD Radeon 780M Graphics +15BF, C8, AMD Radeon 760M Graphics +15BF, C9, AMD Radeon 780M Graphics +15BF, CA, AMD Radeon 740M Graphics +15BF, CB, AMD Radeon 760M Graphics +15BF, CC, AMD Radeon 740M Graphics +15BF, CD, AMD Radeon 760M Graphics +15BF, CF, AMD Radeon 780M Graphics +15BF, D0, AMD Radeon 780M Graphics +15BF, D1, AMD Radeon 780M Graphics +15BF, D2, AMD Radeon 780M Graphics +15BF, D3, AMD Radeon 780M Graphics +15BF, D4, AMD Radeon 780M Graphics +15BF, D5, AMD Radeon 760M Graphics +15BF, D6, AMD Radeon 760M Graphics +15BF, D7, AMD Radeon 780M Graphics +15BF, D8, AMD Radeon 740M Graphics +15BF, D9, AMD Radeon 780M Graphics +15BF, DA, AMD Radeon 780M Graphics +15BF, DB, AMD Radeon 760M Graphics +15BF, DC, AMD Radeon 760M Graphics +15BF, DD, AMD Radeon 780M Graphics +15BF, DE, AMD Radeon 740M Graphics +15BF, DF, AMD Radeon 760M Graphics +15BF, F0, AMD Radeon 760M Graphics +15C8, C1, AMD Radeon 740M Graphics +15C8, C2, AMD Radeon 740M Graphics +15C8, C3, AMD Radeon 740M Graphics +15C8, C4, AMD Radeon 740M Graphics +15C8, D1, AMD Radeon 740M Graphics +15C8, D2, AMD Radeon 740M Graphics +15C8, D3, AMD Radeon 740M Graphics +15C8, D4, AMD Radeon 740M Graphics +15D8, 00, AMD Radeon RX Vega 8 Graphics WS +15D8, 91, AMD Radeon Vega 3 Graphics +15D8, 91, AMD Ryzen Embedded R1606G with Radeon Vega Gfx +15D8, 92, AMD Radeon Vega 3 Graphics +15D8, 92, AMD Ryzen Embedded R1505G with Radeon Vega Gfx +15D8, 93, AMD Radeon Vega 1 Graphics +15D8, A1, AMD Radeon Vega 10 Graphics +15D8, A2, AMD Radeon Vega 8 Graphics +15D8, A3, AMD Radeon Vega 6 Graphics +15D8, A4, AMD Radeon Vega 3 Graphics +15D8, B1, AMD Radeon Vega 10 Graphics +15D8, B2, AMD Radeon Vega 8 Graphics +15D8, B3, AMD Radeon Vega 6 Graphics +15D8, B4, AMD Radeon Vega 3 Graphics +15D8, C1, AMD Radeon Vega 10 Graphics +15D8, C2, AMD Radeon Vega 8 Graphics +15D8, C3, AMD Radeon Vega 6 Graphics +15D8, C4, AMD Radeon Vega 3 Graphics +15D8, C5, AMD Radeon Vega 3 Graphics +15D8, C8, AMD Radeon Vega 11 Graphics +15D8, C9, AMD Radeon Vega 8 Graphics +15D8, CA, AMD Radeon Vega 11 Graphics +15D8, CB, AMD Radeon Vega 8 Graphics +15D8, CC, AMD Radeon Vega 3 Graphics +15D8, CE, AMD Radeon Vega 3 Graphics +15D8, CF, AMD Ryzen Embedded R1305G with Radeon Vega Gfx +15D8, D1, AMD Radeon Vega 10 Graphics +15D8, D2, AMD Radeon Vega 8 Graphics +15D8, D3, AMD Radeon Vega 6 Graphics +15D8, D4, AMD Radeon Vega 3 Graphics +15D8, D8, AMD Radeon Vega 11 Graphics +15D8, D9, AMD Radeon Vega 8 Graphics +15D8, DA, AMD Radeon Vega 11 Graphics +15D8, DB, AMD Radeon Vega 3 Graphics +15D8, DB, AMD Radeon Vega 8 Graphics +15D8, DC, AMD Radeon Vega 3 Graphics +15D8, DD, AMD Radeon Vega 3 Graphics +15D8, DE, AMD Radeon Vega 3 Graphics +15D8, DF, AMD Radeon Vega 3 Graphics +15D8, E3, AMD Radeon Vega 3 Graphics +15D8, E4, AMD Ryzen Embedded R1102G with Radeon Vega Gfx +15DD, 81, AMD Ryzen Embedded V1807B with Radeon Vega Gfx +15DD, 82, AMD Ryzen Embedded V1756B with Radeon Vega Gfx +15DD, 83, AMD Ryzen Embedded V1605B with Radeon Vega Gfx +15DD, 84, AMD Radeon Vega 6 Graphics +15DD, 85, AMD Ryzen Embedded V1202B with Radeon Vega Gfx +15DD, 86, AMD Radeon Vega 11 Graphics +15DD, 88, AMD Radeon Vega 8 Graphics +15DD, C1, AMD Radeon Vega 11 Graphics +15DD, C2, AMD Radeon Vega 8 Graphics +15DD, C3, AMD Radeon Vega 3 / 10 Graphics +15DD, C4, AMD Radeon Vega 8 Graphics +15DD, C5, AMD Radeon Vega 3 Graphics +15DD, C6, AMD Radeon Vega 11 Graphics +15DD, C8, AMD Radeon Vega 8 Graphics +15DD, C9, AMD Radeon Vega 11 Graphics +15DD, CA, AMD Radeon Vega 8 Graphics +15DD, CB, AMD Radeon Vega 3 Graphics +15DD, CC, AMD Radeon Vega 6 Graphics +15DD, CE, AMD Radeon Vega 3 Graphics +15DD, CF, AMD Radeon Vega 3 Graphics +15DD, D0, AMD Radeon Vega 10 Graphics +15DD, D1, AMD Radeon Vega 8 Graphics +15DD, D3, AMD Radeon Vega 11 Graphics +15DD, D5, AMD Radeon Vega 8 Graphics +15DD, D6, AMD Radeon Vega 11 Graphics +15DD, D7, AMD Radeon Vega 8 Graphics +15DD, D8, AMD Radeon Vega 3 Graphics +15DD, D9, AMD Radeon Vega 6 Graphics +15DD, E1, AMD Radeon Vega 3 Graphics +15DD, E2, AMD Radeon Vega 3 Graphics +163F, AE, AMD Custom GPU 0405 +163F, E1, AMD Custom GPU 0405 +164E, D8, AMD Radeon 610M +164E, D9, AMD Radeon 610M +164E, DA, AMD Radeon 610M +164E, DB, AMD Radeon 610M +164E, DC, AMD Radeon 610M +1681, 06, AMD Radeon 680M +1681, 07, AMD Radeon 660M +1681, 0A, AMD Radeon 680M +1681, 0B, AMD Radeon 660M +1681, C7, AMD Radeon 680M +1681, C8, AMD Radeon 680M +1681, C9, AMD Radeon 660M +1900, 01, AMD Radeon 780M Graphics +1900, 02, AMD Radeon 760M Graphics +1900, 03, AMD Radeon 780M Graphics +1900, 04, AMD Radeon 760M Graphics +1900, 05, AMD Radeon 780M Graphics +1900, 06, AMD Radeon 780M Graphics +1900, 07, AMD Radeon 760M Graphics +1900, B0, AMD Radeon 780M Graphics +1900, B1, AMD Radeon 780M Graphics +1900, B2, AMD Radeon 780M Graphics +1900, B3, AMD Radeon 780M Graphics +1900, B4, AMD Radeon 780M Graphics +1900, B5, AMD Radeon 780M Graphics +1900, B6, AMD Radeon 780M Graphics +1900, B7, AMD Radeon 760M Graphics +1900, B8, AMD Radeon 760M Graphics +1900, B9, AMD Radeon 780M Graphics +1900, BA, AMD Radeon 780M Graphics +1900, BB, AMD Radeon 780M Graphics +1900, C0, AMD Radeon 780M Graphics +1900, C1, AMD Radeon 760M Graphics +1900, C2, AMD Radeon 780M Graphics +1900, C3, AMD Radeon 760M Graphics +1900, C4, AMD Radeon 780M Graphics +1900, C5, AMD Radeon 780M Graphics +1900, C6, AMD Radeon 760M Graphics +1900, C7, AMD Radeon 780M Graphics +1900, C8, AMD Radeon 760M Graphics +1900, C9, AMD Radeon 780M Graphics +1900, CA, AMD Radeon 760M Graphics +1900, CB, AMD Radeon 780M Graphics +1900, CC, AMD Radeon 780M Graphics +1900, CD, AMD Radeon 760M Graphics +1900, CE, AMD Radeon 780M Graphics +1900, CF, AMD Radeon 760M Graphics +1900, D0, AMD Radeon 780M Graphics +1900, D1, AMD Radeon 760M Graphics +1900, D2, AMD Radeon 780M Graphics +1900, D3, AMD Radeon 760M Graphics +1900, D4, AMD Radeon 780M Graphics +1900, D5, AMD Radeon 780M Graphics +1900, D6, AMD Radeon 760M Graphics +1900, D7, AMD Radeon 780M Graphics +1900, D8, AMD Radeon 760M Graphics +1900, D9, AMD Radeon 780M Graphics +1900, DA, AMD Radeon 760M Graphics +1900, DB, AMD Radeon 780M Graphics +1900, DC, AMD Radeon 780M Graphics +1900, DD, AMD Radeon 760M Graphics +1900, DE, AMD Radeon 780M Graphics +1900, DF, AMD Radeon 760M Graphics +1900, F0, AMD Radeon 780M Graphics +1900, F1, AMD Radeon 780M Graphics +1900, F2, AMD Radeon 780M Graphics +1901, C1, AMD Radeon 740M Graphics +1901, C2, AMD Radeon 740M Graphics +1901, C3, AMD Radeon 740M Graphics +1901, C6, AMD Radeon 740M Graphics +1901, C7, AMD Radeon 740M Graphics +1901, C8, AMD Radeon 740M Graphics +1901, C9, AMD Radeon 740M Graphics +1901, CA, AMD Radeon 740M Graphics +1901, D1, AMD Radeon 740M Graphics +1901, D2, AMD Radeon 740M Graphics +1901, D3, AMD Radeon 740M Graphics +1901, D4, AMD Radeon 740M Graphics +1901, D5, AMD Radeon 740M Graphics +1901, D6, AMD Radeon 740M Graphics +1901, D7, AMD Radeon 740M Graphics +1901, D8, AMD Radeon 740M Graphics +6600, 00, AMD Radeon HD 8600 / 8700M +6600, 81, AMD Radeon R7 M370 +6601, 00, AMD Radeon HD 8500M / 8700M +6604, 00, AMD Radeon R7 M265 Series +6604, 81, AMD Radeon R7 M350 +6605, 00, AMD Radeon R7 M260 Series +6605, 81, AMD Radeon R7 M340 +6606, 00, AMD Radeon HD 8790M +6607, 00, AMD Radeon R5 M240 +6608, 00, AMD FirePro W2100 +6610, 00, AMD Radeon R7 200 Series +6610, 81, AMD Radeon R7 350 +6610, 83, AMD Radeon R5 340 +6610, 87, AMD Radeon R7 200 Series +6611, 00, AMD Radeon R7 200 Series +6611, 87, AMD Radeon R7 200 Series +6613, 00, AMD Radeon R7 200 Series +6617, 00, AMD Radeon R7 240 Series +6617, 87, AMD Radeon R7 200 Series +6617, C7, AMD Radeon R7 240 Series +6640, 00, AMD Radeon HD 8950 +6640, 80, AMD Radeon R9 M380 +6646, 00, AMD Radeon R9 M280X +6646, 80, AMD Radeon R9 M385 +6646, 80, AMD Radeon R9 M470X +6647, 00, AMD Radeon R9 M200X Series +6647, 80, AMD Radeon R9 M380 +6649, 00, AMD FirePro W5100 +6658, 00, AMD Radeon R7 200 Series +665C, 00, AMD Radeon HD 7700 Series +665D, 00, AMD Radeon R7 200 Series +665F, 81, AMD Radeon R7 360 Series +6660, 00, AMD Radeon HD 8600M Series +6660, 81, AMD Radeon R5 M335 +6660, 83, AMD Radeon R5 M330 +6663, 00, AMD Radeon HD 8500M Series +6663, 83, AMD Radeon R5 M320 +6664, 00, AMD Radeon R5 M200 Series +6665, 00, AMD Radeon R5 M230 Series +6665, 83, AMD Radeon R5 M320 +6665, C3, AMD Radeon R5 M435 +6666, 00, AMD Radeon R5 M200 Series +6667, 00, AMD Radeon R5 M200 Series +666F, 00, AMD Radeon HD 8500M +66A1, 02, AMD Instinct MI60 / MI50 +66A1, 06, AMD Radeon Pro VII +66AF, C1, AMD Radeon VII +6780, 00, AMD FirePro W9000 +6784, 00, ATI FirePro V (FireGL V) Graphics Adapter +6788, 00, ATI FirePro V (FireGL V) Graphics Adapter +678A, 00, AMD FirePro W8000 +6798, 00, AMD Radeon R9 200 / HD 7900 Series +6799, 00, AMD Radeon HD 7900 Series +679A, 00, AMD Radeon HD 7900 Series +679B, 00, AMD Radeon HD 7900 Series +679E, 00, AMD Radeon HD 7800 Series +67A0, 00, AMD Radeon FirePro W9100 +67A1, 00, AMD Radeon FirePro W8100 +67B0, 00, AMD Radeon R9 200 Series +67B0, 80, AMD Radeon R9 390 Series +67B1, 00, AMD Radeon R9 200 Series +67B1, 80, AMD Radeon R9 390 Series +67B9, 00, AMD Radeon R9 200 Series +67C0, 00, AMD Radeon Pro WX 7100 Graphics +67C0, 80, AMD Radeon E9550 +67C2, 01, AMD Radeon Pro V7350x2 +67C2, 02, AMD Radeon Pro V7300X +67C4, 00, AMD Radeon Pro WX 7100 Graphics +67C4, 80, AMD Radeon E9560 / E9565 Graphics +67C7, 00, AMD Radeon Pro WX 5100 Graphics +67C7, 80, AMD Radeon E9390 Graphics +67D0, 01, AMD Radeon Pro V7350x2 +67D0, 02, AMD Radeon Pro V7300X +67DF, C0, AMD Radeon Pro 580X +67DF, C1, AMD Radeon RX 580 Series +67DF, C2, AMD Radeon RX 570 Series +67DF, C3, AMD Radeon RX 580 Series +67DF, C4, AMD Radeon RX 480 Graphics +67DF, C5, AMD Radeon RX 470 Graphics +67DF, C6, AMD Radeon RX 570 Series +67DF, C7, AMD Radeon RX 480 Graphics +67DF, CF, AMD Radeon RX 470 Graphics +67DF, D7, AMD Radeon RX 470 Graphics +67DF, E0, AMD Radeon RX 470 Series +67DF, E1, AMD Radeon RX 590 Series +67DF, E3, AMD Radeon RX Series +67DF, E7, AMD Radeon RX 580 Series +67DF, EB, AMD Radeon Pro 580X +67DF, EF, AMD Radeon RX 570 Series +67DF, F7, AMD Radeon RX P30PH +67DF, FF, AMD Radeon RX 470 Series +67E0, 00, AMD Radeon Pro WX Series +67E3, 00, AMD Radeon Pro WX 4100 +67E8, 00, AMD Radeon Pro WX Series +67E8, 01, AMD Radeon Pro WX Series +67E8, 80, AMD Radeon E9260 Graphics +67EB, 00, AMD Radeon Pro V5300X +67EF, C0, AMD Radeon RX Graphics +67EF, C1, AMD Radeon RX 460 Graphics +67EF, C2, AMD Radeon Pro Series +67EF, C3, AMD Radeon RX Series +67EF, C5, AMD Radeon RX 460 Graphics +67EF, C7, AMD Radeon RX Graphics +67EF, CF, AMD Radeon RX 460 Graphics +67EF, E0, AMD Radeon RX 560 Series +67EF, E1, AMD Radeon RX Series +67EF, E2, AMD Radeon RX 560X +67EF, E3, AMD Radeon RX Series +67EF, E5, AMD Radeon RX 560 Series +67EF, E7, AMD Radeon RX 560 Series +67EF, EF, AMD Radeon 550 Series +67EF, FF, AMD Radeon RX 460 Graphics +67FF, C0, AMD Radeon Pro 465 +67FF, C1, AMD Radeon RX 560 Series +67FF, CF, AMD Radeon RX 560 Series +67FF, EF, AMD Radeon RX 560 Series +67FF, FF, AMD Radeon RX 550 Series +6800, 00, AMD Radeon HD 7970M +6801, 00, AMD Radeon HD 8970M +6806, 00, AMD Radeon R9 M290X +6808, 00, AMD FirePro W7000 +6808, 00, ATI FirePro V (FireGL V) Graphics Adapter +6809, 00, ATI FirePro W5000 +6810, 00, AMD Radeon R9 200 Series +6810, 81, AMD Radeon R9 370 Series +6811, 00, AMD Radeon R9 200 Series +6811, 81, AMD Radeon R7 370 Series +6818, 00, AMD Radeon HD 7800 Series +6819, 00, AMD Radeon HD 7800 Series +6820, 00, AMD Radeon R9 M275X +6820, 81, AMD Radeon R9 M375 +6820, 83, AMD Radeon R9 M375X +6821, 00, AMD Radeon R9 M200X Series +6821, 83, AMD Radeon R9 M370X +6821, 87, AMD Radeon R7 M380 +6822, 00, AMD Radeon E8860 +6823, 00, AMD Radeon R9 M200X Series +6825, 00, AMD Radeon HD 7800M Series +6826, 00, AMD Radeon HD 7700M Series +6827, 00, AMD Radeon HD 7800M Series +6828, 00, AMD FirePro W600 +682B, 00, AMD Radeon HD 8800M Series +682B, 87, AMD Radeon R9 M360 +682C, 00, AMD FirePro W4100 +682D, 00, AMD Radeon HD 7700M Series +682F, 00, AMD Radeon HD 7700M Series +6830, 00, AMD Radeon 7800M Series +6831, 00, AMD Radeon 7700M Series +6835, 00, AMD Radeon R7 Series / HD 9000 Series +6837, 00, AMD Radeon HD 7700 Series +683D, 00, AMD Radeon HD 7700 Series +683F, 00, AMD Radeon HD 7700 Series +684C, 00, ATI FirePro V (FireGL V) Graphics Adapter +6860, 00, AMD Radeon Instinct MI25 +6860, 01, AMD Radeon Instinct MI25 +6860, 02, AMD Radeon Instinct MI25 +6860, 03, AMD Radeon Pro V340 +6860, 04, AMD Radeon Instinct MI25x2 +6860, 07, AMD Radeon Pro V320 +6861, 00, AMD Radeon Pro WX 9100 +6862, 00, AMD Radeon Pro SSG +6863, 00, AMD Radeon Vega Frontier Edition +6864, 03, AMD Radeon Pro V340 +6864, 04, AMD Radeon Instinct MI25x2 +6864, 05, AMD Radeon Pro V340 +6868, 00, AMD Radeon Pro WX 8200 +686C, 00, AMD Radeon Instinct MI25 MxGPU +686C, 01, AMD Radeon Instinct MI25 MxGPU +686C, 02, AMD Radeon Instinct MI25 MxGPU +686C, 03, AMD Radeon Pro V340 MxGPU +686C, 04, AMD Radeon Instinct MI25x2 MxGPU +686C, 05, AMD Radeon Pro V340L MxGPU +686C, 06, AMD Radeon Instinct MI25 MxGPU +687F, 01, AMD Radeon RX Vega +687F, C0, AMD Radeon RX Vega +687F, C1, AMD Radeon RX Vega +687F, C3, AMD Radeon RX Vega +687F, C7, AMD Radeon RX Vega +6900, 00, AMD Radeon R7 M260 +6900, 81, AMD Radeon R7 M360 +6900, 83, AMD Radeon R7 M340 +6900, C1, AMD Radeon R5 M465 Series +6900, C3, AMD Radeon R5 M445 Series +6900, D1, AMD Radeon 530 Series +6900, D3, AMD Radeon 530 Series +6901, 00, AMD Radeon R5 M255 +6902, 00, AMD Radeon Series +6907, 00, AMD Radeon R5 M255 +6907, 87, AMD Radeon R5 M315 +6920, 00, AMD Radeon R9 M395X +6920, 01, AMD Radeon R9 M390X +6921, 00, AMD Radeon R9 M390X +6929, 00, AMD FirePro S7150 +6929, 01, AMD FirePro S7100X +692B, 00, AMD FirePro W7100 +6938, 00, AMD Radeon R9 200 Series +6938, F0, AMD Radeon R9 200 Series +6938, F1, AMD Radeon R9 380 Series +6939, 00, AMD Radeon R9 200 Series +6939, F0, AMD Radeon R9 200 Series +6939, F1, AMD Radeon R9 380 Series +694C, C0, AMD Radeon RX Vega M GH Graphics +694E, C0, AMD Radeon RX Vega M GL Graphics +6980, 00, AMD Radeon Pro WX 3100 +6981, 00, AMD Radeon Pro WX 3200 Series +6981, 01, AMD Radeon Pro WX 3200 Series +6981, 10, AMD Radeon Pro WX 3200 Series +6985, 00, AMD Radeon Pro WX 3100 +6986, 00, AMD Radeon Pro WX 2100 +6987, 80, AMD Embedded Radeon E9171 +6987, C0, AMD Radeon 550X Series +6987, C1, AMD Radeon RX 640 +6987, C3, AMD Radeon 540X Series +6987, C7, AMD Radeon 540 +6995, 00, AMD Radeon Pro WX 2100 +6997, 00, AMD Radeon Pro WX 2100 +699F, 81, AMD Embedded Radeon E9170 Series +699F, C0, AMD Radeon 500 Series +699F, C1, AMD Radeon 540 Series +699F, C3, AMD Radeon 500 Series +699F, C7, AMD Radeon RX 550 / 550 Series +699F, C9, AMD Radeon 540 +6FDF, E7, AMD Radeon RX 590 GME +6FDF, EF, AMD Radeon RX 580 2048SP +7300, C1, AMD FirePro S9300 x2 +7300, C8, AMD Radeon R9 Fury Series +7300, C9, AMD Radeon Pro Duo +7300, CA, AMD Radeon R9 Fury Series +7300, CB, AMD Radeon R9 Fury Series +7312, 00, AMD Radeon Pro W5700 +731E, C6, AMD Radeon RX 5700XTB +731E, C7, AMD Radeon RX 5700B +731F, C0, AMD Radeon RX 5700 XT 50th Anniversary +731F, C1, AMD Radeon RX 5700 XT +731F, C2, AMD Radeon RX 5600M +731F, C3, AMD Radeon RX 5700M +731F, C4, AMD Radeon RX 5700 +731F, C5, AMD Radeon RX 5700 XT +731F, CA, AMD Radeon RX 5600 XT +731F, CB, AMD Radeon RX 5600 OEM +7340, C1, AMD Radeon RX 5500M +7340, C3, AMD Radeon RX 5300M +7340, C5, AMD Radeon RX 5500 XT +7340, C7, AMD Radeon RX 5500 +7340, C9, AMD Radeon RX 5500XTB +7340, CF, AMD Radeon RX 5300 +7341, 00, AMD Radeon Pro W5500 +7347, 00, AMD Radeon Pro W5500M +7360, 41, AMD Radeon Pro 5600M +7360, C3, AMD Radeon Pro V520 +7362, C1, AMD Radeon Pro V540 +7362, C3, AMD Radeon Pro V520 +738C, 01, AMD Instinct MI100 +73A1, 00, AMD Radeon Pro V620 +73A3, 00, AMD Radeon Pro W6800 +73A5, C0, AMD Radeon RX 6950 XT +73AE, 00, AMD Radeon Pro V620 MxGPU +73AF, C0, AMD Radeon RX 6900 XT +73BF, C0, AMD Radeon RX 6900 XT +73BF, C1, AMD Radeon RX 6800 XT +73BF, C3, AMD Radeon RX 6800 +73DF, C0, AMD Radeon RX 6750 XT +73DF, C1, AMD Radeon RX 6700 XT +73DF, C2, AMD Radeon RX 6800M +73DF, C3, AMD Radeon RX 6800M +73DF, C5, AMD Radeon RX 6700 XT +73DF, CF, AMD Radeon RX 6700M +73DF, D5, AMD Radeon RX 6750 GRE 12GB +73DF, D7, AMD TDC-235 +73DF, DF, AMD Radeon RX 6700 +73DF, E5, AMD Radeon RX 6750 GRE 12GB +73DF, FF, AMD Radeon RX 6700 +73E0, 00, AMD Radeon RX 6600M +73E1, 00, AMD Radeon Pro W6600M +73E3, 00, AMD Radeon Pro W6600 +73EF, C0, AMD Radeon RX 6800S +73EF, C1, AMD Radeon RX 6650 XT +73EF, C2, AMD Radeon RX 6700S +73EF, C3, AMD Radeon RX 6650M +73EF, C4, AMD Radeon RX 6650M XT +73FF, C1, AMD Radeon RX 6600 XT +73FF, C3, AMD Radeon RX 6600M +73FF, C7, AMD Radeon RX 6600 +73FF, CB, AMD Radeon RX 6600S +73FF, CF, AMD Radeon RX 6600 LE +73FF, DF, AMD Radeon RX 6750 GRE 10GB +7408, 00, AMD Instinct MI250X +740C, 01, AMD Instinct MI250X / MI250 +740F, 02, AMD Instinct MI210 +7421, 00, AMD Radeon Pro W6500M +7422, 00, AMD Radeon Pro W6400 +7423, 00, AMD Radeon Pro W6300M +7423, 01, AMD Radeon Pro W6300 +7424, 00, AMD Radeon RX 6300 +743F, C1, AMD Radeon RX 6500 XT +743F, C3, AMD Radeon RX 6500 +743F, C3, AMD Radeon RX 6500M +743F, C7, AMD Radeon RX 6400 +743F, C8, AMD Radeon RX 6500M +743F, CC, AMD Radeon 6550S +743F, CE, AMD Radeon RX 6450M +743F, CF, AMD Radeon RX 6300M +743F, D3, AMD Radeon RX 6550M +743F, D7, AMD Radeon RX 6400 +7448, 00, AMD Radeon Pro W7900 +7449, 00, AMD Radeon Pro W7800 48GB +744A, 00, AMD Radeon Pro W7900 Dual Slot +744C, C8, AMD Radeon RX 7900 XTX +744C, CC, AMD Radeon RX 7900 XT +744C, CE, AMD Radeon RX 7900 GRE +744C, CF, AMD Radeon RX 7900M +745E, CC, AMD Radeon Pro W7800 +7460, 00, AMD Radeon Pro V710 +7461, 00, AMD Radeon Pro V710 MxGPU +7470, 00, AMD Radeon Pro W7700 +747E, C8, AMD Radeon RX 7800 XT +747E, D8, AMD Radeon RX 7800M +747E, FF, AMD Radeon RX 7700 XT +7480, 00, AMD Radeon Pro W7600 +7480, C0, AMD Radeon RX 7600 XT +7480, C1, AMD Radeon RX 7700S +7480, C2, AMD Radeon RX 7650 GRE +7480, C3, AMD Radeon RX 7600S +7480, C7, AMD Radeon RX 7600M XT +7480, CF, AMD Radeon RX 7600 +7483, CF, AMD Radeon RX 7600M +7489, 00, AMD Radeon Pro W7500 +7499, 00, AMD Radeon Pro W7400 +7499, C0, AMD Radeon RX 7400 +7499, C1, AMD Radeon RX 7300 +74A0, 00, AMD Instinct MI300A +74A1, 00, AMD Instinct MI300X +74A2, 00, AMD Instinct MI308X +74A5, 00, AMD Instinct MI325X +74A9, 00, AMD Instinct MI300X HF +74B5, 00, AMD Instinct MI300X VF +74B6, 00, AMD Instinct MI308X +74BD, 00, AMD Instinct MI300X HF +7550, C0, AMD Radeon RX 9070 XT +7550, C3, AMD Radeon RX 9070 +9830, 00, AMD Radeon HD 8400 / R3 Series +9831, 00, AMD Radeon HD 8400E +9832, 00, AMD Radeon HD 8330 +9833, 00, AMD Radeon HD 8330E +9834, 00, AMD Radeon HD 8210 +9835, 00, AMD Radeon HD 8210E +9836, 00, AMD Radeon HD 8200 / R3 Series +9837, 00, AMD Radeon HD 8280E +9838, 00, AMD Radeon HD 8200 / R3 series +9839, 00, AMD Radeon HD 8180 +983D, 00, AMD Radeon HD 8250 +9850, 00, AMD Radeon R3 Graphics +9850, 03, AMD Radeon R3 Graphics +9850, 40, AMD Radeon R2 Graphics +9850, 45, AMD Radeon R3 Graphics +9851, 00, AMD Radeon R4 Graphics +9851, 01, AMD Radeon R5E Graphics +9851, 05, AMD Radeon R5 Graphics +9851, 06, AMD Radeon R5E Graphics +9851, 40, AMD Radeon R4 Graphics +9851, 45, AMD Radeon R5 Graphics +9852, 00, AMD Radeon R2 Graphics +9852, 40, AMD Radeon E1 Graphics +9853, 00, AMD Radeon R2 Graphics +9853, 01, AMD Radeon R4E Graphics +9853, 03, AMD Radeon R2 Graphics +9853, 05, AMD Radeon R1E Graphics +9853, 06, AMD Radeon R1E Graphics +9853, 07, AMD Radeon R1E Graphics +9853, 08, AMD Radeon R1E Graphics +9853, 40, AMD Radeon R2 Graphics +9854, 00, AMD Radeon R3 Graphics +9854, 01, AMD Radeon R3E Graphics +9854, 02, AMD Radeon R3 Graphics +9854, 05, AMD Radeon R2 Graphics +9854, 06, AMD Radeon R4 Graphics +9854, 07, AMD Radeon R3 Graphics +9855, 02, AMD Radeon R6 Graphics +9855, 05, AMD Radeon R4 Graphics +9856, 00, AMD Radeon R2 Graphics +9856, 01, AMD Radeon R2E Graphics +9856, 02, AMD Radeon R2 Graphics +9856, 05, AMD Radeon R1E Graphics +9856, 06, AMD Radeon R2 Graphics +9856, 07, AMD Radeon R1E Graphics +9856, 08, AMD Radeon R1E Graphics +9856, 13, AMD Radeon R1E Graphics +9874, 81, AMD Radeon R6 Graphics +9874, 84, AMD Radeon R7 Graphics +9874, 85, AMD Radeon R6 Graphics +9874, 87, AMD Radeon R5 Graphics +9874, 88, AMD Radeon R7E Graphics +9874, 89, AMD Radeon R6E Graphics +9874, C4, AMD Radeon R7 Graphics +9874, C5, AMD Radeon R6 Graphics +9874, C6, AMD Radeon R6 Graphics +9874, C7, AMD Radeon R5 Graphics +9874, C8, AMD Radeon R7 Graphics +9874, C9, AMD Radeon R7 Graphics +9874, CA, AMD Radeon R5 Graphics +9874, CB, AMD Radeon R5 Graphics +9874, CC, AMD Radeon R7 Graphics +9874, CD, AMD Radeon R7 Graphics +9874, CE, AMD Radeon R5 Graphics +9874, E1, AMD Radeon R7 Graphics +9874, E2, AMD Radeon R7 Graphics +9874, E3, AMD Radeon R7 Graphics +9874, E4, AMD Radeon R7 Graphics +9874, E5, AMD Radeon R5 Graphics +9874, E6, AMD Radeon R5 Graphics +98E4, 80, AMD Radeon R5E Graphics +98E4, 81, AMD Radeon R4E Graphics +98E4, 83, AMD Radeon R2E Graphics +98E4, 84, AMD Radeon R2E Graphics +98E4, 86, AMD Radeon R1E Graphics +98E4, C0, AMD Radeon R4 Graphics +98E4, C1, AMD Radeon R5 Graphics +98E4, C2, AMD Radeon R4 Graphics +98E4, C4, AMD Radeon R5 Graphics +98E4, C6, AMD Radeon R5 Graphics +98E4, C8, AMD Radeon R4 Graphics +98E4, C9, AMD Radeon R4 Graphics +98E4, CA, AMD Radeon R5 Graphics +98E4, D0, AMD Radeon R2 Graphics +98E4, D1, AMD Radeon R2 Graphics +98E4, D2, AMD Radeon R2 Graphics +98E4, D4, AMD Radeon R2 Graphics +98E4, D9, AMD Radeon R5 Graphics +98E4, DA, AMD Radeon R5 Graphics +98E4, DB, AMD Radeon R3 Graphics +98E4, E1, AMD Radeon R3 Graphics +98E4, E2, AMD Radeon R3 Graphics +98E4, E9, AMD Radeon R4 Graphics +98E4, EA, AMD Radeon R4 Graphics +98E4, EB, AMD Radeon R3 Graphics +98E4, EB, AMD Radeon R4 Graphics diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/util/io/CSVTableTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/io/CSVTableTest.java new file mode 100644 index 000000000..8c7451f90 --- /dev/null +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/io/CSVTableTest.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.io; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Glavo + */ +public final class CSVTableTest { + + @Test + public void testCreate() { + CSVTable table = new CSVTable(); + + assertEquals(0, table.getColumnCount()); + assertEquals(0, table.getRowCount()); + + table.set(0, 0, "test1"); + assertEquals(1, table.getColumnCount()); + assertEquals(1, table.getRowCount()); + + table.set(4, 0, "test2"); + assertEquals(5, table.getColumnCount()); + assertEquals(1, table.getRowCount()); + + table.set(2, 1, "test3"); + assertEquals(5, table.getColumnCount()); + assertEquals(2, table.getRowCount()); + + assertEquals("test1", table.get(0, 0)); + assertEquals("", table.get(1, 0)); + assertEquals("", table.get(0, 1)); + assertEquals("test2", table.get(4, 0)); + assertEquals("test3", table.get(2, 1)); + } + + @Test + public void testToString() { + CSVTable table = new CSVTable(); + table.set(0, 0, "a"); + table.set(1, 0, "b"); + table.set(3, 0, "c"); + table.set(0, 2, "a,b"); + table.set(1, 2, "c"); + table.set(3, 2, "d\"e\n"); + + assertEquals("a,b,,c\n,,,\n\"a,b\",c,,\"d\\\"e\\n\"\n", table.toString()); + + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7ff9ac52a..ff6923997 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,6 +13,7 @@ jsoup = "1.19.1" chardet = "2.5.0" twelvemonkeys = "3.12.0" jna = "5.17.0" +pci-ids = "0.4.0" # plugins shadow = "8.3.6" @@ -33,6 +34,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } chardet = { module = "org.glavo:chardet", version.ref = "chardet" } twelvemonkeys-imageio-webp = { module = "com.twelvemonkeys.imageio:imageio-webp", version.ref = "twelvemonkeys" } jna = { module = "net.java.dev.jna:jna", version.ref = "jna" } +pci-ids = { module = "org.glavo:pci-ids", version.ref = "pci-ids" } [plugins] shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }