Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac578c044f | ||
|
|
fc68029adc | ||
|
|
f6a92c1806 | ||
|
|
58eb452e90 | ||
|
|
fb8a96a170 | ||
|
|
ced441a830 | ||
|
|
c5693c3450 | ||
|
|
bc27eda220 | ||
|
|
0c40dcdeed | ||
|
|
4282147042 | ||
|
|
0874c98d21 | ||
|
|
0c3c7dd62a | ||
|
|
f663e5568c | ||
|
|
b8c1738f96 | ||
|
|
41c61645e7 | ||
|
|
9fc697751c | ||
|
|
a4db0532b9 | ||
|
|
8954139b5e | ||
|
|
b2f7e724e4 | ||
|
|
0d20910ea3 | ||
|
|
5c90299073 | ||
|
|
fc66cbcf96 | ||
|
|
e7b9a95df0 | ||
|
|
acc9740fa9 | ||
|
|
5789f82617 | ||
|
|
8827fd06d8 | ||
|
|
583e004d71 |
No files matched your search
@@ -59,6 +59,9 @@ Jenkinsfile text eol=lf
|
||||
|
||||
# Minecraft schematic
|
||||
*.litematic binary
|
||||
*.nbt binary
|
||||
*.schem binary
|
||||
*.schematic binary
|
||||
|
||||
# Compiled / intermediate build artefacts
|
||||
*.bin binary
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.jackhuang.hmcl.gradle.l10n.UpsideDownTranslate
|
||||
import org.jackhuang.hmcl.gradle.pack.CreateDeb
|
||||
import org.jackhuang.hmcl.gradle.pack.ReleaseType
|
||||
import org.jackhuang.hmcl.gradle.utils.PropertiesUtils
|
||||
import org.jackhuang.hmcl.gradle.utils.OfficialBuildValueSource
|
||||
import java.net.URI
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
@@ -155,33 +156,28 @@ tasks.compileJava {
|
||||
options.compilerArgs.addAll(addOpens.map { "--add-exports=$it=ALL-UNNAMED" })
|
||||
}
|
||||
|
||||
val hmclProperties = buildList {
|
||||
add("hmcl.version" to project.version.toString())
|
||||
add("hmcl.add-opens" to addOpens.joinToString(" "))
|
||||
System.getenv("GITHUB_SHA")?.let {
|
||||
add("hmcl.version.hash" to it)
|
||||
}
|
||||
add("hmcl.version.type" to versionType)
|
||||
add("hmcl.microsoft.auth.id" to microsoftAuthId)
|
||||
add("hmcl.curseforge.apikey" to curseForgeApiKey)
|
||||
add("hmcl.authlib-injector.version" to libs.authlib.injector.get().version!!)
|
||||
add("hmcl.lwjgl-unsafe-agent.version" to libs.lwjgl.unsafe.agent.get().version!!)
|
||||
}
|
||||
val createPropertiesFile = tasks.register<WriteProperties>("createPropertiesFile") {
|
||||
destinationFile.set(layout.buildDirectory.file("hmcl.properties"))
|
||||
encoding = "UTF-8"
|
||||
|
||||
val hmclPropertiesFile = layout.buildDirectory.file("hmcl.properties")
|
||||
val createPropertiesFile = tasks.register("createPropertiesFile") {
|
||||
outputs.file(hmclPropertiesFile)
|
||||
hmclProperties.forEach { (k, v) -> inputs.property(k, v) }
|
||||
|
||||
doLast {
|
||||
val targetFile = hmclPropertiesFile.get().asFile
|
||||
targetFile.parentFile.mkdir()
|
||||
targetFile.bufferedWriter().use {
|
||||
for ((k, v) in hmclProperties) {
|
||||
it.write("$k=$v\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
property("hmcl.version", project.version.toString())
|
||||
property("hmcl.add-opens", addOpens.joinToString(" "))
|
||||
System.getenv("GITHUB_SHA")?.let { property("hmcl.version.hash", it) }
|
||||
property("hmcl.version.type", versionType)
|
||||
property("hmcl.microsoft.auth.id", microsoftAuthId)
|
||||
property("hmcl.curseforge.apikey", curseForgeApiKey)
|
||||
property("hmcl.authlib-injector.version", libs.authlib.injector.get().version!!)
|
||||
property("hmcl.lwjgl-unsafe-agent.version", libs.lwjgl.unsafe.agent.get().version!!)
|
||||
property("hmcl.official", providers.of(OfficialBuildValueSource::class) {
|
||||
parameters.publicKeyFile.set(layout.projectDirectory.file("src/main/resources/assets/hmcl_signature_publickey.der"))
|
||||
parameters.privateKeyLocation.set(providers.environmentVariable("HMCL_SIGNATURE_KEY"))
|
||||
// Pin the official public key independently of the replaceable resource file.
|
||||
// Under HMCL’s license agreement, you **MUST** change the software name or the version number in
|
||||
// a reasonable way to distinguish it from the original version.
|
||||
// You may modify this hash if and only if you have changed the software name or version number
|
||||
// in accordance with the license requirements.
|
||||
parameters.expectedSha256.set("36aadcfde8626b9d4c7a74facb3fdfda47e703c205a673fbe6a56b1d419f8b8e")
|
||||
})
|
||||
}
|
||||
|
||||
tasks.jar {
|
||||
@@ -237,13 +233,12 @@ tasks.shadowJar {
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
dependsOn(createPropertiesFile)
|
||||
dependsOn(upsideDownTranslate)
|
||||
dependsOn(createLocaleNamesResourceBundle)
|
||||
dependsOn(createLanguageList)
|
||||
|
||||
into("assets/") {
|
||||
from(hmclPropertiesFile)
|
||||
from(createPropertiesFile.flatMap { it.destinationFile })
|
||||
from(embedResources)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.jackhuang.hmcl.util.SwingUtils;
|
||||
import org.jackhuang.hmcl.java.JavaRuntime;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.JarUtils;
|
||||
import org.jackhuang.hmcl.util.io.TLSSecurity;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
@@ -42,6 +43,7 @@ public final class EntryPoint {
|
||||
private EntryPoint() {
|
||||
}
|
||||
|
||||
/// Initializes TLS protection before downloading dependencies and starting the launcher.
|
||||
public static void main(String[] args) {
|
||||
System.getProperties().putIfAbsent("java.net.useSystemProxies", "true");
|
||||
System.getProperties().putIfAbsent("javafx.autoproxy.disable", "true");
|
||||
@@ -49,6 +51,7 @@ public final class EntryPoint {
|
||||
|
||||
createHMCLDirectories();
|
||||
LOG.start(Metadata.HMCL_LOCAL_HOME.resolve("logs"));
|
||||
TLSSecurity.install();
|
||||
|
||||
checkWine();
|
||||
|
||||
|
||||
@@ -42,14 +42,16 @@ public final class Metadata {
|
||||
public static final String WINDOWS_APP_USER_MODEL_ID = "org.jackhuang.hmcl";
|
||||
|
||||
public static final String TITLE = NAME + " " + VERSION;
|
||||
public static final String FULL_TITLE = FULL_NAME + " v" + VERSION;
|
||||
|
||||
/// Whether the build metadata identifies this launcher as an official build.
|
||||
public static final boolean OFFICIAL_BUILD = Boolean.parseBoolean(JarUtils.getAttribute("hmcl.official", "false"));
|
||||
|
||||
public static final int MINIMUM_REQUIRED_JAVA_VERSION = 17;
|
||||
public static final int MINIMUM_SUPPORTED_JAVA_VERSION = 17;
|
||||
public static final int RECOMMENDED_JAVA_VERSION = 21;
|
||||
|
||||
public static final String PUBLISH_URL = "https://hmcl.huangyuhui.net";
|
||||
public static final String DOWNLOAD_URL = PUBLISH_URL + "/download";
|
||||
public static final String REPOSITORY_URL = "https://github.com/HMCL-dev/HMCL";
|
||||
public static final String HMCL_UPDATE_URL = System.getProperty("hmcl.update_source.override", PUBLISH_URL + "/api/update_link");
|
||||
public static final String MANUAL_UPDATE_URL = "https://github.com/HMCL-dev/HMCL/releases";
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.jackhuang.hmcl.game.GameComponentType;
|
||||
import org.jackhuang.hmcl.game.HMCLCacheRepository;
|
||||
import org.jackhuang.hmcl.setting.DownloadSource;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.i18n.LocaleUtils;
|
||||
import org.jackhuang.hmcl.util.RegionUtils;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -46,7 +46,7 @@ public final class HMCLDownloadProvider extends DownloadProvider {
|
||||
DownloadSource source,
|
||||
WebURL defaultUrl, WebURL bmclapiUrl
|
||||
) {
|
||||
if (LocaleUtils.IS_CHINA_MAINLAND) {
|
||||
if ("CN".equals(RegionUtils.MACHINE_REGION)) {
|
||||
return switch (source) {
|
||||
case DEFAULT, OFFICIAL -> DownloadCandidates.of(defaultUrl, bmclapiUrl);
|
||||
case MIRROR -> DownloadCandidates.of(bmclapiUrl, defaultUrl);
|
||||
@@ -66,7 +66,7 @@ public final class HMCLDownloadProvider extends DownloadProvider {
|
||||
|
||||
// true means prefer mirror, false means prefer official, null means do not use mirror
|
||||
@Nullable Boolean mirrorFirst;
|
||||
if (LocaleUtils.IS_CHINA_MAINLAND) {
|
||||
if ("CN".equals(RegionUtils.MACHINE_REGION)) {
|
||||
mirrorFirst = switch (source) {
|
||||
case DEFAULT, MIRROR -> true;
|
||||
case OFFICIAL -> false;
|
||||
@@ -158,7 +158,7 @@ public final class HMCLDownloadProvider extends DownloadProvider {
|
||||
Task<ComponentRemoteVersionList<ForgeRemoteVersion>> fetchOfficial = ForgeRemoteVersion.fetchAsync(this, gameVersion);
|
||||
|
||||
DownloadSource source = versionListSource;
|
||||
if (!LocaleUtils.IS_CHINA_MAINLAND && (source == DownloadSource.DEFAULT || source == DownloadSource.OFFICIAL)) {
|
||||
if (source == DownloadSource.DEFAULT || source == DownloadSource.OFFICIAL) {
|
||||
return fetchOfficial;
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ public final class HMCLDownloadProvider extends DownloadProvider {
|
||||
Task<ComponentRemoteVersionList<NeoForgeRemoteVersion>> fetchOfficial = NeoForgeRemoteVersion.fetchAsync(this, gameVersion);
|
||||
|
||||
DownloadSource source = versionListSource;
|
||||
if (!LocaleUtils.IS_CHINA_MAINLAND && (source == DownloadSource.DEFAULT || source == DownloadSource.OFFICIAL)) {
|
||||
if (source == DownloadSource.DEFAULT || source == DownloadSource.OFFICIAL) {
|
||||
return fetchOfficial;
|
||||
}
|
||||
Task<ComponentRemoteVersionList<NeoForgeRemoteVersion>> fetchBMCL = NeoForgeRemoteVersion.fetchBMCLAsync(bmclRoot, gameVersion);
|
||||
@@ -224,7 +224,7 @@ public final class HMCLDownloadProvider extends DownloadProvider {
|
||||
|
||||
@Override
|
||||
public DownloadCandidates getCleanroomVersionListCandidates() {
|
||||
return LocaleUtils.IS_CHINA_MAINLAND
|
||||
return "CN".equals(RegionUtils.MACHINE_REGION)
|
||||
? DownloadCandidates.of(CleanroomRemoteVersion.LOADER_LIST_URL, WebURL.parse("https://alist.8mi.tech/d/mirror/HMCL-Metadata/Auto/cleanroom/index.json"))
|
||||
: DownloadCandidates.of(CleanroomRemoteVersion.LOADER_LIST_URL);
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ public final class ModpackHelper {
|
||||
);
|
||||
|
||||
static {
|
||||
MultiMCComponents.setImplementation(Metadata.FULL_TITLE);
|
||||
MultiMCComponents.setImplementation(Metadata.FULL_NAME + " v" + Metadata.VERSION);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -24,9 +24,9 @@ import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.terracotta.provider.AbstractTerracottaProvider;
|
||||
import org.jackhuang.hmcl.terracotta.provider.GeneralProvider;
|
||||
import org.jackhuang.hmcl.terracotta.provider.MacOSProvider;
|
||||
import org.jackhuang.hmcl.util.RegionUtils;
|
||||
import org.jackhuang.hmcl.util.gson.JsonSerializable;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.i18n.LocaleUtils;
|
||||
import org.jackhuang.hmcl.util.i18n.LocalizedText;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
@@ -91,7 +91,7 @@ public final class TerracottaMetadata {
|
||||
}
|
||||
|
||||
Stream<String> stream = downloads.stream(), streamCN = downloadsCN.stream();
|
||||
@Unmodifiable List<WebURL> links = (LocaleUtils.IS_CHINA_MAINLAND ? Stream.concat(streamCN, stream) : Stream.concat(stream, streamCN))
|
||||
@Unmodifiable List<WebURL> links = ("CN".equals(RegionUtils.MACHINE_REGION) ? Stream.concat(streamCN, stream) : Stream.concat(stream, streamCN))
|
||||
.map(link -> WebURL.parse(options.replace(link)))
|
||||
.toList();
|
||||
|
||||
|
||||
@@ -19,12 +19,12 @@ package org.jackhuang.hmcl.terracotta;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.glavo.url.WebURL;
|
||||
import org.jackhuang.hmcl.util.RegionUtils;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.JsonSerializable;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.gson.TolerableValidationException;
|
||||
import org.jackhuang.hmcl.util.gson.Validation;
|
||||
import org.jackhuang.hmcl.util.i18n.LocaleUtils;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
@@ -84,7 +84,7 @@ public final class TerracottaNodeList {
|
||||
return false;
|
||||
}
|
||||
|
||||
return StringUtils.isBlank(node.region) || LocaleUtils.IS_CHINA_MAINLAND == "CN".equalsIgnoreCase(node.region);
|
||||
return StringUtils.isBlank(node.region) || node.region.equalsIgnoreCase(RegionUtils.MACHINE_REGION);
|
||||
})
|
||||
.map(it -> WebURL.parse(it.url()))
|
||||
.toList();
|
||||
|
||||
@@ -24,6 +24,9 @@ import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.ReadOnlyStringProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.ButtonBase;
|
||||
@@ -57,6 +60,7 @@ import org.jackhuang.hmcl.ui.terracotta.TerracottaPage;
|
||||
import org.jackhuang.hmcl.ui.instances.GameListPage;
|
||||
import org.jackhuang.hmcl.ui.instances.GameInstancePage;
|
||||
import org.jackhuang.hmcl.ui.instances.Instances;
|
||||
import org.jackhuang.hmcl.upgrade.IntegrityChecker;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
@@ -118,6 +122,36 @@ public final class Controllers {
|
||||
void run() throws Exception;
|
||||
}
|
||||
|
||||
/// The shared launcher title, populated during UI initialization.
|
||||
private static @Nullable StringProperty launcherTitle;
|
||||
|
||||
public static ReadOnlyStringProperty launcherTitleProperty() {
|
||||
if (launcherTitle == null) {
|
||||
launcherTitle = new SimpleStringProperty(Controllers.class, "launcherTitle", Metadata.OFFICIAL_BUILD
|
||||
? i18n("launcher.title", Metadata.VERSION)
|
||||
: i18n("launcher.title.unofficial", Metadata.VERSION));
|
||||
if (Metadata.OFFICIAL_BUILD) {
|
||||
@Nullable Boolean selfVerified = IntegrityChecker.getSelfVerified();
|
||||
if (selfVerified != null) {
|
||||
if (!selfVerified) {
|
||||
launcherTitle.set(i18n("launcher.title.modified", Metadata.VERSION));
|
||||
}
|
||||
} else {
|
||||
Schedulers.io().execute(() -> {
|
||||
if (!IntegrityChecker.isSelfVerified()) {
|
||||
FXUtils.runInFX(() -> {
|
||||
//noinspection DataFlowIssue
|
||||
launcherTitle.set(i18n("launcher.title.modified", Metadata.VERSION));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return launcherTitle;
|
||||
}
|
||||
|
||||
/// Returns the stage currently attached to the main-window decorator.
|
||||
///
|
||||
/// @return the current stage, or `null` before initialization or after detachment
|
||||
@@ -134,14 +168,6 @@ public final class Controllers {
|
||||
return gameInstancePage;
|
||||
}
|
||||
|
||||
@FXThread
|
||||
public static void prepareGameInstancePage() {
|
||||
if (gameInstancePage == null) {
|
||||
LOG.info("Prepare the game instance page");
|
||||
gameInstancePage = FXUtils.prepareNode(new GameInstancePage());
|
||||
}
|
||||
}
|
||||
|
||||
@FXThread
|
||||
public static GameListPage getGameListPage() {
|
||||
return gameListPage.get();
|
||||
@@ -160,14 +186,6 @@ public final class Controllers {
|
||||
return settingsPage;
|
||||
}
|
||||
|
||||
@FXThread
|
||||
public static void prepareSettingsPage() {
|
||||
if (settingsPage == null) {
|
||||
LOG.info("Prepare the settings page");
|
||||
settingsPage = FXUtils.prepareNode(new LauncherSettingsPage());
|
||||
}
|
||||
}
|
||||
|
||||
@FXThread
|
||||
public static AccountListPage getAccountListPage() {
|
||||
return accountListPage.get();
|
||||
@@ -181,14 +199,6 @@ public final class Controllers {
|
||||
return downloadPage;
|
||||
}
|
||||
|
||||
@FXThread
|
||||
public static void prepareDownloadPage() {
|
||||
if (downloadPage == null) {
|
||||
LOG.info("Prepare the download page");
|
||||
downloadPage = FXUtils.prepareNode(new DownloadPage());
|
||||
}
|
||||
}
|
||||
|
||||
@FXThread
|
||||
public static Node getTerracottaPage() {
|
||||
return terracottaPage.get();
|
||||
@@ -215,7 +225,7 @@ public final class Controllers {
|
||||
private static void configureMainStage(Stage stage) {
|
||||
stage.setOnCloseRequest(event -> Launcher.stopApplication());
|
||||
FXUtils.setIcon(stage);
|
||||
stage.setTitle(Metadata.FULL_TITLE);
|
||||
stage.titleProperty().bind(launcherTitleProperty());
|
||||
WindowsNativeUtils.installWindowsAppUserModelRelaunchProperties(stage);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ import javafx.collections.ObservableList;
|
||||
import javafx.collections.ObservableMap;
|
||||
import javafx.event.Event;
|
||||
import javafx.event.EventDispatcher;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.event.EventType;
|
||||
import javafx.geometry.Bounds;
|
||||
import javafx.geometry.Rectangle2D;
|
||||
@@ -1226,16 +1225,6 @@ public final class FXUtils {
|
||||
return node;
|
||||
}
|
||||
|
||||
public static void prepareOnMouseEnter(Node node, Runnable action) {
|
||||
node.addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler<>() {
|
||||
@Override
|
||||
public void handle(MouseEvent e) {
|
||||
node.removeEventFilter(MouseEvent.MOUSE_ENTERED, this);
|
||||
action.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static <T> void onScroll(Node node, List<T> list,
|
||||
ToIntFunction<List<T>> finder,
|
||||
Consumer<T> updater
|
||||
|
||||
@@ -91,6 +91,7 @@ public enum SVG {
|
||||
LOCAL_CAFE("M4 21V19H20V21H4ZM8 17Q6.35 17 5.175 15.825T4 13V3H20Q20.825 3 21.4125 3.5875T22 5V8Q22 8.825 21.4125 9.4125T20 10H18V13Q18 14.65 16.825 15.825T14 17H8ZM8 15H14Q14.825 15 15.4125 14.4125T16 13V5H6V13Q6 13.825 6.5875 14.4125T8 15ZM18 8H20V5H18V8ZM8 15H6 16 8Z"),
|
||||
LOCAL_CAFE_FILL("M4 21V19H20v2H4Zm4-4q-1.65 0-2.825-1.175T4 13V3H20q.825 0 1.4125.5875T22 5V8q0 .825-.5875 1.4125T20 10H18v3q0 1.65-1.175 2.825T14 17H8ZM18 8h2V5H18V8Z"),
|
||||
LOCATION_CITY("M3 21V7H9V5l3-3 3 3v6h6V21H3Zm2-2H7V17H5v2Zm0-4H7V13H5v2Zm0-4H7V9H5v2Zm6 8h2V17H11v2Zm0-4h2V13H11v2Zm0-4h2V9H11v2Zm0-4h2V5H11V7Zm6 12h2V17H17v2Zm0-4h2V13H17v2Z"),
|
||||
MEMORY("M9 15V9h6v6H9Zm2-2h2v-2h-2v2Zm-2 8v-2H7q-.825 0-1.4125-.5875T5 17v-2H3v-2h2v-2H3V9h2V7q0-.825.5875-1.4125T7 5h2V3h2v2h2V3h2v2h2q.825 0 1.4125.5875T19 7v2h2v2h-2v2h2v2h-2v2q0 .825-.5875 1.4125T17 19h-2v2h-2v-2h-2v2H9Zm8-4V7H7v10h10Zm-5-5Z"),
|
||||
MENU("M3 18V16H21V18H3ZM3 13V11H21V13H3ZM3 8V6H21V8H3Z"),
|
||||
MICROSOFT("M4 20H22v2H4V13H20v7h2V4H20v7H4V4h7V20h2V4h9V2H2V22H4"), // Not Material
|
||||
MINIMIZE_CENTER("M6 13v-2h12v2H6Z"), // Not Material
|
||||
@@ -132,6 +133,13 @@ public enum SVG {
|
||||
WARNING("M1 21 12 2 23 21H1ZM4.45 19H19.55L12 6 4.45 19ZM12 18Q12.425 18 12.7125 17.7125T13 17Q13 16.575 12.7125 16.2875T12 16Q11.575 16 11.2875 16.2875T11 17Q11 17.425 11.2875 17.7125T12 18ZM11 15H13V10H11V15ZM12 12.5Z"),
|
||||
WB_SUNNY("M11 4V1H13V4H11ZM11 23V20H13V23H11ZM20 13V11H23V13H20ZM1 13V11H4V13H1ZM18.7 6.7 17.3 5.3 19.05 3.5 20.5 4.95 18.7 6.7ZM4.95 20.5 3.5 19.05 5.3 17.3 6.7 18.7 4.95 20.5ZM19.05 20.5 17.3 18.7 18.7 17.3 20.5 19.05 19.05 20.5ZM5.3 6.7 3.5 4.95 4.95 3.5 6.7 5.3 5.3 6.7ZM12 18Q9.5 18 7.75 16.25T6 12Q6 9.5 7.75 7.75T12 6Q14.5 6 16.25 7.75T18 12Q18 14.5 16.25 16.25T12 18ZM12 16Q13.675 16 14.8375 14.8375T16 12Q16 10.325 14.8375 9.1625T12 8Q10.325 8 9.1625 9.1625T8 12Q8 13.675 9.1625 14.8375T12 16ZM12 12Z"),
|
||||
WB_SUNNY_FILL("M11 4V1h2V4H11Zm0 19V20h2v3H11Zm9-10V11h3v2H20ZM1 13V11H4v2H1ZM18.7 6.7 17.3 5.3l1.75-1.8L20.5 4.95 18.7 6.7ZM4.95 20.5 3.5 19.05 5.3 17.3l1.4 1.4-1.75 1.8Zm14.1 0-1.75-1.8 1.4-1.4 1.8 1.75-1.45 1.45ZM5.3 6.7 3.5 4.95 4.95 3.5 6.7 5.3 5.3 6.7ZM12 18q-2.5 0-4.25-1.75T6 12 7.75 7.75 12 6t4.25 1.75T18 12t-1.75 4.25T12 18Z"),
|
||||
|
||||
// https://lucide.dev/
|
||||
|
||||
LUCIDE_APP_WINDOW("M4 3C2.3553 3 1 4.3553 1 6V18c0 1.6447 1.3553 3 3 3H20c1.6447 0 3-1.3553 3-3V6c0-1.6447-1.3553-3-3-3H6 4zM4 5H5V7H3V6c0-.5713.4287-1 1-1zM7 5H9V7H7V5zm4 0h9c.5713 0 1 .4287 1 1V7H11V5zM3 9H6 21v9c0 .5713-.4287 1-1 1H4c-.5713 0-1-.4287-1-1V9z"),
|
||||
LUCIDE_CPU("M7 1A1 1 0 006 2V3C4.3553 3 3 4.3553 3 6H2A1 1 0 001 7 1 1 0 002 8H3v3H2A1 1 0 001 12a1 1 0 001 1H3v3H2A1 1 0 001 17a1 1 0 001 1H3c0 1.6447 1.3553 3 3 3v1a1 1 0 001 1 1 1 0 001-1V21h3v1a1 1 0 001 1 1 1 0 001-1V21h3v1a1 1 0 001 1 1 1 0 001-1V21c1.6447 0 3-1.3553 3-3h1a1 1 0 001-1 1 1 0 00-1-1H21V13h1a1 1 0 001-1 1 1 0 00-1-1H21V8h1a1 1 0 001-1A1 1 0 0022 6H21c0-1.6447-1.3553-3-3-3V2A1 1 0 0017 1 1 1 0 0016 2V3H13V2A1 1 0 0012 1 1 1 0 0011 2V3H8V2A1 1 0 007 1zM6 5H18c.5713 0 1 .4287 1 1V18c0 .5713-.4287 1-1 1H6c-.5713 0-1-.4287-1-1V6c0-.5713.4287-1 1-1zM9 7C7.9093 7 7 7.9093 7 9v6c0 1.0907.9093 2 2 2h6c1.0907 0 2-.9093 2-2V9c0-1.0907-.9093-2-2-2H9zM9 9h6v6H9V9z"),
|
||||
LUCIDE_GPU("M8 18v2h5V18H8Zm8-8c-.5641 0-1 .4359-1 1s.4359 1 1 1 1-.4359 1-1-.4359-1-1-1ZM8 10c-.5641 0-1 .4359-1 1s.4359 1 1 1c.5641 0 1-.4359 1-1s-.4359-1-1-1Zm8-2c1.645 0 3 1.355 3 3 0 1.645-1.355 3-3 3s-3-1.355-3-3c0-1.645 1.355-3 3-3ZM8 8c1.645 0 3 1.355 3 3 0 1.645-1.355 3-3 3s-3-1.355-3-3C5 9.355 6.355 8 8 8ZM3 6V16H20c.5641 0 1-.4359 1-1V7c0-.5641-.4359-1-1-1H3ZM2 2A1 1 0 013 3V4H20c1.645 0 3 1.355 3 3v8c0 1.645-1.355 3-3 3H15v2c0 1.0927-.9073 2-2 2H8c-1.0927 0-2-.9073-2-2V18H3v3A1 1 0 012 22 1 1 0 011 21V3A1 1 0 012 2Z"),
|
||||
LUCIDE_MEMORY_STICK("M4 5C2.3553 5 1 6.3553 1 8v3 3c0 1.2949.84 2.4102 2 2.8262V18a1 1 0 001 1 1 1 0 001-1V17H7v1a1 1 0 001 1 1 1 0 001-1V17h2v1a1 1 0 001 1 1 1 0 001-1V17h2v1a1 1 0 001 1 1 1 0 001-1V17h2v1a1 1 0 001 1 1 1 0 001-1V16.8262c1.16-.416 2-1.5313 2-2.8262V11 8c0-1.6447-1.3553-3-3-3H4zM4 7H20c.5713 0 1 .4287 1 1v2h-.5a1 1 0 00-1 1 1 1 0 001 1H21v2c0 .5713-.4287 1-1 1H16 12 8 4c-.5713 0-1-.4287-1-1V12h.5a1 1 0 001-1 1 1 0 00-1-1H3V8c0-.5713.4287-1 1-1zM8 9A1 1 0 007 10v2a1 1 0 001 1 1 1 0 001-1V10A1 1 0 008 9zm4 0a1 1 0 00-1 1v2a1 1 0 001 1 1 1 0 001-1V10A1 1 0 0012 9zm4 0a1 1 0 00-1 1v2a1 1 0 001 1 1 1 0 001-1V10A1 1 0 0016 9z"),
|
||||
;
|
||||
|
||||
public static final double DEFAULT_SIZE = 24;
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.auth.microsoft.MicrosoftAccount;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
|
||||
import org.jackhuang.hmcl.game.TexturesLoader;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
@@ -153,7 +154,7 @@ public final class AccountListItemSkin extends SkinBase<AccountListItem> {
|
||||
.start();
|
||||
}
|
||||
});
|
||||
FXUtils.installFastTooltip(btnUpload, i18n("account.skin.upload"));
|
||||
FXUtils.installFastTooltip(btnUpload, i18n("account.skin." + (skinnable.getAccount() instanceof OfflineAccount ? "edit" : "upload")));
|
||||
btnUpload.disableProperty().bind(Bindings.not(skinnable.canUploadSkin()));
|
||||
spinnerUpload.setContent(btnUpload);
|
||||
spinnerUpload.getStyleClass().add("small-spinner-pane");
|
||||
|
||||
@@ -40,7 +40,7 @@ import org.jackhuang.hmcl.ui.construct.AdvancedListItem;
|
||||
import org.jackhuang.hmcl.ui.construct.ClassTitle;
|
||||
import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage;
|
||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||
import org.jackhuang.hmcl.util.i18n.LocaleUtils;
|
||||
import org.jackhuang.hmcl.util.RegionUtils;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
||||
|
||||
@@ -57,7 +57,7 @@ public final class AccountListPage extends DecoratorAnimatedPage implements Deco
|
||||
String property = System.getProperty("hmcl.offline.auth.restricted", "auto");
|
||||
|
||||
if ("false".equals(property)
|
||||
|| "auto".equals(property) && LocaleUtils.IS_CHINA_MAINLAND
|
||||
|| "auto".equals(property) && "CN".equals(RegionUtils.MACHINE_REGION)
|
||||
|| SettingsManager.userSettings().enableOfflineAccountProperty().get())
|
||||
RESTRICTED.set(false);
|
||||
else
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> 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
|
||||
@@ -140,23 +140,21 @@ public class MicrosoftAccountLoginPane extends JFXDialogLayout implements Dialog
|
||||
rootContainer.setAlignment(Pos.TOP_CENTER);
|
||||
|
||||
StackPane warningPane = new StackPane();
|
||||
rootContainer.getChildren().add(warningPane);
|
||||
|
||||
if (!IntegrityChecker.isOfficial()) {
|
||||
var unofficialHintPane = new HintPane(MessageDialogPane.MessageType.WARNING);
|
||||
unofficialHintPane.setSegment(i18n("unofficial.hint"));
|
||||
warningPane.getChildren().add(unofficialHintPane);
|
||||
} else if (Accounts.OAUTH_CALLBACK.getClientId().isEmpty()) {
|
||||
HintPane snapshotHint = new HintPane(MessageDialogPane.MessageType.WARNING);
|
||||
snapshotHint.setSegment(i18n("account.methods.microsoft.snapshot"));
|
||||
warningPane.getChildren().add(snapshotHint);
|
||||
btnLogin.setDisable(true);
|
||||
loginButtonSpinner.setLoading(false);
|
||||
return;
|
||||
warningPane.getChildren().setAll(unofficialHintPane);
|
||||
}
|
||||
|
||||
rootContainer.getChildren().add(warningPane);
|
||||
|
||||
if (currentStep instanceof Step.Init) {
|
||||
if (Accounts.OAUTH_CALLBACK.getClientId().isEmpty()) {
|
||||
HintPane snapshotHint = new HintPane(MessageDialogPane.MessageType.ERROR);
|
||||
snapshotHint.setSegment(i18n("account.methods.microsoft.snapshot"));
|
||||
warningPane.getChildren().setAll(snapshotHint);
|
||||
btnLogin.setDisable(true);
|
||||
loginButtonSpinner.setLoading(false);
|
||||
} else if (currentStep instanceof Step.Init) {
|
||||
btnLogin.setOnAction(e -> this.step.set(new Step.StartAuthorizationCodeLogin()));
|
||||
loginButtonSpinner.setLoading(false);
|
||||
|
||||
|
||||
@@ -26,31 +26,46 @@ import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.util.Duration;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/// Displays one page, retaining the preceding page while a transition runs.
|
||||
/// Content changes must be made on the JavaFX Application Thread.
|
||||
@NotNullByDefault
|
||||
public class TransitionPane extends StackPane {
|
||||
|
||||
private Node currentNode;
|
||||
/// The requested page, or `null` before content is assigned.
|
||||
private @Nullable Node currentNode;
|
||||
|
||||
/// Creates an empty container that clips content to its bounds.
|
||||
public TransitionPane() {
|
||||
FXUtils.setOverflowHidden(this);
|
||||
}
|
||||
|
||||
public Node getCurrentNode() {
|
||||
/// Returns the requested page, or `null` if none has been assigned.
|
||||
public @Nullable Node getCurrentNode() {
|
||||
return currentNode;
|
||||
}
|
||||
|
||||
/// Replaces the content using [Motion#SHORT4] and [Motion#EASE].
|
||||
public final void setContent(Node newView, AnimationProducer transition) {
|
||||
setContent(newView, transition, Motion.SHORT4);
|
||||
}
|
||||
|
||||
/// Replaces the content using the given duration and [Motion#EASE].
|
||||
public final void setContent(Node newView, AnimationProducer transition, Duration duration) {
|
||||
setContent(newView, transition, duration, Motion.EASE);
|
||||
}
|
||||
|
||||
/// Replaces the content using the given animation and timing.
|
||||
/// The first page, repeated assignments of the current page, and replacements
|
||||
/// without animations are displayed immediately. Otherwise, CSS and layout
|
||||
/// are processed in this container before the animation starts in a later
|
||||
/// event-loop task. Mouse input is disabled while the transition is pending
|
||||
/// or running.
|
||||
public void setContent(Node newView, AnimationProducer transition,
|
||||
Duration duration, Interpolator interpolator) {
|
||||
Node previousNode = currentNode != newView && getWidth() > 0 && getHeight() > 0 ? currentNode : null;
|
||||
@Nullable Node previousNode = currentNode != newView && getWidth() > 0 && getHeight() > 0 ? currentNode : null;
|
||||
currentNode = newView;
|
||||
|
||||
if (!AnimationUtils.isAnimationEnabled() || previousNode == null || transition == ContainerAnimations.NONE) {
|
||||
@@ -64,7 +79,7 @@ public class TransitionPane extends StackPane {
|
||||
setMouseTransparent(true);
|
||||
transition.init(this, previousNode, newView);
|
||||
|
||||
CacheHint cacheHint = newView instanceof Cacheable cacheable
|
||||
@Nullable CacheHint cacheHint = newView instanceof Cacheable cacheable
|
||||
? cacheable.getCacheHint(transition)
|
||||
: null;
|
||||
|
||||
@@ -73,8 +88,12 @@ public class TransitionPane extends StackPane {
|
||||
newView.setCacheHint(cacheHint);
|
||||
}
|
||||
|
||||
// runLater or "init" will not work
|
||||
// Let navigation handlers finish updating the page before preparing it.
|
||||
Platform.runLater(() -> {
|
||||
// runLater alone does not guarantee a CSS or layout pass. Keep that
|
||||
// first-use work outside the animation's elapsed time.
|
||||
applyCss();
|
||||
layout();
|
||||
Animation newAnimation = transition.animate(
|
||||
this,
|
||||
previousNode,
|
||||
@@ -95,15 +114,19 @@ public class TransitionPane extends StackPane {
|
||||
|
||||
}
|
||||
|
||||
/// Prepares page properties and creates animations for content replacements.
|
||||
public interface AnimationProducer {
|
||||
/// Resets page transforms, making the old page opaque and the new page transparent.
|
||||
default void init(TransitionPane container, Node previousNode, Node nextNode) {
|
||||
AnimationUtils.reset(previousNode, true);
|
||||
AnimationUtils.reset(nextNode, false);
|
||||
}
|
||||
|
||||
/// Creates the transition between the prepared pages using the given timing.
|
||||
Animation animate(Pane container, Node previousNode, Node nextNode,
|
||||
Duration duration, Interpolator interpolator);
|
||||
|
||||
/// Returns the reverse transition, or `null` if none is specified.
|
||||
default @Nullable TransitionPane.AnimationProducer opposite() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -749,6 +749,7 @@ public final class Decorator {
|
||||
/// Replaces the attached stage when transparency or theme brightness requires a different decoration style.
|
||||
///
|
||||
/// Preserves the scene, navigation, dialogs, normal bounds, supported window states, and focus owner.
|
||||
/// Copies the previous title before the replacement-stage initializer may override or bind it.
|
||||
/// Hidden windows remain hidden. Does nothing while detached or when the current style is still suitable.
|
||||
/// Must run on the JavaFX application thread after theme bindings have updated.
|
||||
private void updateWindowStyle() {
|
||||
@@ -765,8 +766,8 @@ public final class Decorator {
|
||||
@Nullable Node focusOwner = previousStage.getScene().getFocusOwner();
|
||||
|
||||
Stage replacement = new Stage();
|
||||
stageInitializer.accept(replacement);
|
||||
replacement.setTitle(previousStage.getTitle());
|
||||
stageInitializer.accept(replacement);
|
||||
replacement.getIcons().setAll(previousStage.getIcons());
|
||||
replacement.setOnCloseRequest(previousStage.getOnCloseRequest());
|
||||
replacement.setResizable(previousStage.isResizable());
|
||||
|
||||
@@ -52,10 +52,7 @@ import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
||||
import org.jackhuang.hmcl.ui.construct.ComponentList;
|
||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer;
|
||||
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
||||
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
||||
import org.jackhuang.hmcl.ui.construct.*;
|
||||
import org.jackhuang.hmcl.ui.wizard.Navigation;
|
||||
import org.jackhuang.hmcl.ui.wizard.Refreshable;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.jfoenix.controls.JFXDialogLayout;
|
||||
import com.jfoenix.controls.JFXListView;
|
||||
|
||||
import javafx.beans.property.*;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Insets;
|
||||
@@ -71,7 +70,6 @@ import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
||||
@@ -170,10 +168,6 @@ public class AddonUpdatesPage extends BorderPane implements DecoratorPage {
|
||||
}
|
||||
}
|
||||
|
||||
private <T> void setupCellValueFactory(TableColumn<AddonUpdateObject, T> column, Function<AddonUpdateObject, ObservableValue<T>> mapper) {
|
||||
column.setCellValueFactory(param -> mapper.apply(param.getValue()));
|
||||
}
|
||||
|
||||
private void updateFiles() {
|
||||
AddonUpdateTask task = new AddonUpdateTask(
|
||||
downloadProvider,
|
||||
@@ -213,10 +207,10 @@ public class AddonUpdatesPage extends BorderPane implements DecoratorPage {
|
||||
csvTable.set(3, 0, "Update Source");
|
||||
|
||||
for (int i = 0; i < objects.size(); i++) {
|
||||
csvTable.set(0, i + 1, objects.get(i).fileName.get());
|
||||
csvTable.set(1, i + 1, objects.get(i).currentVersion.get());
|
||||
csvTable.set(2, i + 1, objects.get(i).targetVersion.get());
|
||||
csvTable.set(3, i + 1, objects.get(i).source.get());
|
||||
csvTable.set(0, i + 1, objects.get(i).getFileName());
|
||||
csvTable.set(1, i + 1, objects.get(i).getCurrentVersion());
|
||||
csvTable.set(2, i + 1, objects.get(i).getTargetVersion());
|
||||
csvTable.set(3, i + 1, objects.get(i).getSource());
|
||||
}
|
||||
|
||||
csvTable.write(path);
|
||||
@@ -238,34 +232,30 @@ public class AddonUpdatesPage extends BorderPane implements DecoratorPage {
|
||||
|
||||
private static final class AddonUpdateObject {
|
||||
final AddonUpdate data;
|
||||
final BooleanProperty enabled = new SimpleBooleanProperty();
|
||||
final StringProperty fileName = new SimpleStringProperty();
|
||||
final StringProperty currentVersion = new SimpleStringProperty();
|
||||
final StringProperty targetVersion = new SimpleStringProperty();
|
||||
final StringProperty targetVersionType = new SimpleStringProperty();
|
||||
final StringProperty source = new SimpleStringProperty();
|
||||
final String targetVersionType;
|
||||
final String source;
|
||||
String changelog = null;
|
||||
|
||||
final BooleanProperty enabled = new SimpleBooleanProperty();
|
||||
|
||||
final ItemPropertyAsyncCache<Image, AddonUpdateObject> iconCache;
|
||||
|
||||
public AddonUpdateObject(AddonUpdate data) {
|
||||
this.data = data;
|
||||
|
||||
enabled.set(!data.localAddonFile().isDisabled());
|
||||
fileName.set(data.localAddonFile().getFileName());
|
||||
currentVersion.set(data.currentVersion().version());
|
||||
targetVersion.set(data.targetVersion().version());
|
||||
targetVersionType.set(data.targetVersion().versionType().name());
|
||||
source.set(switch (data.currentVersion().source()) {
|
||||
this.targetVersionType = switch (data.targetVersion().versionType()) {
|
||||
case Release -> i18n("addon.channel.release");
|
||||
case Beta -> i18n("addon.channel.beta");
|
||||
case Alpha -> i18n("addon.channel.alpha");
|
||||
};
|
||||
this.source = switch (data.currentVersion().source()) {
|
||||
case CURSEFORGE -> i18n("addon.curseforge");
|
||||
case MODRINTH -> i18n("addon.modrinth");
|
||||
});
|
||||
};
|
||||
|
||||
iconCache = new ItemPropertyAsyncCache.Soft<>(
|
||||
this,
|
||||
this::loadIcon,
|
||||
this::getDefaultIcon
|
||||
);
|
||||
this.enabled.set(!data.localAddonFile().isDisabled());
|
||||
|
||||
this.iconCache = new ItemPropertyAsyncCache.Soft<>(this, this::loadIcon, this::getDefaultIcon);
|
||||
}
|
||||
|
||||
public AddonUpdate getData() {
|
||||
@@ -280,42 +270,30 @@ public class AddonUpdatesPage extends BorderPane implements DecoratorPage {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public StringProperty fileNameProperty() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName.set(fileName);
|
||||
}
|
||||
|
||||
public LocalAddonFile getLocalAddonFile() {
|
||||
public LocalAddonFile<?> getLocalAddonFile() {
|
||||
return data.localAddonFile();
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return data.localAddonFile().getFileName();
|
||||
}
|
||||
|
||||
public String getCurrentVersion() {
|
||||
return currentVersion.get();
|
||||
return data.currentVersion().version();
|
||||
}
|
||||
|
||||
public StringProperty currentVersionProperty() {
|
||||
return currentVersion;
|
||||
public String getTargetVersion() {
|
||||
return data.targetVersion().version();
|
||||
}
|
||||
|
||||
public StringProperty targetVersionProperty() {
|
||||
return targetVersion;
|
||||
}
|
||||
|
||||
public StringProperty targetVersionTypeProperty() {
|
||||
public String getTargetVersionType() {
|
||||
return targetVersionType;
|
||||
}
|
||||
|
||||
public StringProperty sourceProperty() {
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void setSource(String source) {
|
||||
this.source.set(source);
|
||||
}
|
||||
|
||||
private Image getDefaultIcon() {
|
||||
if (data.localAddonFile() instanceof LocalModFile localModFile) {
|
||||
return GameInstanceIconType
|
||||
@@ -424,13 +402,20 @@ public class AddonUpdatesPage extends BorderPane implements DecoratorPage {
|
||||
@Override
|
||||
public void updateItem(AddonUpdateObject item, boolean empty) {
|
||||
AddonUpdateObject oldItem = getItem();
|
||||
boolean oldEmpty = isEmpty();
|
||||
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (oldItem == item && oldEmpty == empty) {
|
||||
return;
|
||||
}
|
||||
|
||||
ripplerContainer.releaseRippleImmediately();
|
||||
|
||||
if (oldItem != null) {
|
||||
enabledBox.selectedProperty().unbindBidirectional(oldItem.enabledProperty());
|
||||
}
|
||||
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (empty || item == null) {
|
||||
setGraphic(null);
|
||||
return;
|
||||
@@ -440,18 +425,13 @@ public class AddonUpdatesPage extends BorderPane implements DecoratorPage {
|
||||
|
||||
item.iconCache.attachValue(imageContainer.imageProperty(), new WeakReference<>(this.itemProperty()));
|
||||
|
||||
LocalAddonFile addonFile = item.getLocalAddonFile();
|
||||
LocalAddonFile<?> addonFile = item.getLocalAddonFile();
|
||||
|
||||
if (oldItem != item) {
|
||||
content.getTags().clear();
|
||||
|
||||
if (addonFile instanceof LocalModFile modFile) {
|
||||
content.addTag(modFile.getId());
|
||||
content.addTag(item.source.get());
|
||||
} else if (addonFile instanceof ResourcePackFile) {
|
||||
content.addTag(item.source.get());
|
||||
}
|
||||
}
|
||||
content.getTags().clear();
|
||||
if (addonFile instanceof LocalModFile modFile)
|
||||
content.addTag(modFile.getId());
|
||||
content.addTag(item.getSource());
|
||||
content.addTag(item.getTargetVersionType());
|
||||
|
||||
if (addonFile instanceof LocalModFile modFile) {
|
||||
content.setTitle(modFile.getName());
|
||||
@@ -459,13 +439,7 @@ public class AddonUpdatesPage extends BorderPane implements DecoratorPage {
|
||||
content.setTitle(resourcePackFile.getFileName());
|
||||
}
|
||||
|
||||
content.setSubtitle(
|
||||
item.getCurrentVersion()
|
||||
+ " → "
|
||||
+ item.targetVersion.get()
|
||||
+ " | "
|
||||
+ item.targetVersionType.get()
|
||||
);
|
||||
content.setSubtitle(item.getCurrentVersion() + " → " + item.getTargetVersion());
|
||||
|
||||
setGraphic(pane);
|
||||
}
|
||||
|
||||
@@ -549,86 +549,10 @@ public class DownloadListPage extends Control implements DecoratorPage {
|
||||
listView.setSelectionModel(new NoneMultipleSelectionModel<>());
|
||||
// ListViewBehavior would consume ESC pressed event, preventing us from handling it, so we ignore it here
|
||||
ignoreEvent(listView, KeyEvent.KEY_PRESSED, e -> e.getCode() == KeyCode.ESCAPE);
|
||||
listView.setCellFactory(x -> new ListCell<>() {
|
||||
private static final Insets PADDING = new Insets(9, 9, 0, 9);
|
||||
private static final Insets LAST_PADDING = new Insets(9, 9, 9, 9);
|
||||
|
||||
private final RipplerContainer graphic;
|
||||
private final StackPane wrapper = new StackPane();
|
||||
|
||||
private final TwoLineListItem content = new TwoLineListItem();
|
||||
private final ImageContainer imageContainer = new ImageContainer(40);
|
||||
|
||||
{
|
||||
setPadding(PADDING);
|
||||
|
||||
HBox container = new HBox(8);
|
||||
container.setPadding(new Insets(8));
|
||||
container.setCursor(Cursor.HAND);
|
||||
container.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
imageContainer.setMouseTransparent(true);
|
||||
|
||||
container.getChildren().setAll(imageContainer, content);
|
||||
HBox.setHgrow(content, Priority.ALWAYS);
|
||||
|
||||
this.graphic = new RipplerContainer(container);
|
||||
wrapper.getChildren().setAll(this.graphic);
|
||||
wrapper.getStyleClass().add("card-no-padding");
|
||||
|
||||
FXUtils.onClicked(wrapper, () -> {
|
||||
RemoteAddon item = getItem();
|
||||
if (item != null)
|
||||
Controllers.navigate(new DownloadPage(getSkinnable(), item, getSkinnable().getInstanceOptional(), getSkinnable().callback));
|
||||
});
|
||||
|
||||
setPrefWidth(0);
|
||||
|
||||
FXUtils.limitCellWidth(listView, this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateItem(RemoteAddon item, boolean empty) {
|
||||
RemoteAddon oldItem = getItem();
|
||||
boolean oldEmpty = isEmpty();
|
||||
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (oldItem == item && oldEmpty == empty) return;
|
||||
|
||||
this.graphic.releaseRippleImmediately();
|
||||
|
||||
if (empty || item == null) {
|
||||
setGraphic(null);
|
||||
} else {
|
||||
setPadding(
|
||||
getIndex() == getListView().getItems().size() - 1
|
||||
? LAST_PADDING
|
||||
: PADDING
|
||||
);
|
||||
|
||||
@Nullable AddonProjectInfoRegistry<?> registry = AddonProjectInfoRegistry.getByAddonType(getSkinnable().type);
|
||||
@Nullable AddonProjectInfo mod = registry != null ? registry.getInfoByCurseForgeId(item.slug()) : null;
|
||||
content.setTitle(mod != null && I18n.isUseChinese() ? mod.displayName() : item.title());
|
||||
String description = item.description();
|
||||
if (description != null) {
|
||||
description = description.replaceAll("\\R", " ");
|
||||
}
|
||||
content.setSubtitle(description);
|
||||
content.getTags().clear();
|
||||
for (String category : item.categories()) {
|
||||
if (getSkinnable().shouldDisplayCategory(category))
|
||||
content.addTag(getSkinnable().getLocalizedCategory(category, null));
|
||||
}
|
||||
iconLoader.load(imageContainer.imageProperty(), item.iconUrl());
|
||||
setGraphic(wrapper);
|
||||
}
|
||||
}
|
||||
});
|
||||
listView.setCellFactory(x -> new DownloadListCell(getSkinnable(), listView, iconLoader));
|
||||
}
|
||||
|
||||
getChildren().setAll(pane);
|
||||
this.getChildren().setAll(pane);
|
||||
}
|
||||
|
||||
private record CategoryIndented(int indent, RemoteAddonRepository.Category category) {
|
||||
@@ -642,4 +566,91 @@ public class DownloadListPage extends Control implements DecoratorPage {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DownloadListCell extends ListCell<RemoteAddon> {
|
||||
private final DownloadListPage page;
|
||||
private final JFXListView<RemoteAddon> listView;
|
||||
private final RemoteImageLoader iconLoader;
|
||||
|
||||
private static final Insets PADDING = new Insets(9, 9, 0, 9);
|
||||
private static final Insets LAST_PADDING = new Insets(9, 9, 9, 9);
|
||||
|
||||
private final RipplerContainer graphic;
|
||||
private final StackPane wrapper = new StackPane();
|
||||
|
||||
private final TwoLineListItem content = new TwoLineListItem();
|
||||
private final ImageContainer imageContainer = new ImageContainer(40);
|
||||
|
||||
private DownloadListCell(DownloadListPage page, JFXListView<RemoteAddon> listView, RemoteImageLoader iconLoader) {
|
||||
this.page = page;
|
||||
this.listView = listView;
|
||||
this.iconLoader = iconLoader;
|
||||
|
||||
setPadding(PADDING);
|
||||
|
||||
HBox container = new HBox(8);
|
||||
container.setPadding(new Insets(8));
|
||||
container.setCursor(Cursor.HAND);
|
||||
container.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
imageContainer.setMouseTransparent(true);
|
||||
|
||||
container.getChildren().setAll(imageContainer, content);
|
||||
HBox.setHgrow(content, Priority.ALWAYS);
|
||||
|
||||
this.graphic = new RipplerContainer(container);
|
||||
wrapper.getChildren().setAll(this.graphic);
|
||||
wrapper.getStyleClass().add("card-no-padding");
|
||||
|
||||
FXUtils.onClicked(wrapper, () -> {
|
||||
RemoteAddon item = getItem();
|
||||
if (item != null) {
|
||||
Controllers.navigate(new DownloadPage(page, item, page.getInstanceOptional(), page.callback));
|
||||
}
|
||||
});
|
||||
|
||||
setPrefWidth(0);
|
||||
|
||||
FXUtils.limitCellWidth(listView, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateItem(RemoteAddon item, boolean empty) {
|
||||
RemoteAddon oldItem = getItem();
|
||||
boolean oldEmpty = isEmpty();
|
||||
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (oldItem == item && oldEmpty == empty) return;
|
||||
|
||||
this.graphic.releaseRippleImmediately();
|
||||
|
||||
if (empty || item == null) {
|
||||
setGraphic(null);
|
||||
} else {
|
||||
setPadding(
|
||||
getIndex() == listView.getItems().size() - 1
|
||||
? LAST_PADDING
|
||||
: PADDING
|
||||
);
|
||||
|
||||
@Nullable AddonProjectInfoRegistry<?> registry = AddonProjectInfoRegistry.getByAddonType(page.type);
|
||||
@Nullable AddonProjectInfo mod = registry != null ? registry.getInfoByCurseForgeId(item.slug()) : null;
|
||||
content.setTitle(mod != null && I18n.isUseChinese() ? mod.displayName() : item.title());
|
||||
String description = item.description();
|
||||
if (description != null) {
|
||||
description = description.replaceAll("\\R", " ");
|
||||
}
|
||||
content.setSubtitle(description);
|
||||
content.getTags().clear();
|
||||
for (String category : item.categories()) {
|
||||
if (page.shouldDisplayCategory(category)) {
|
||||
content.addTag(page.getLocalizedCategory(category, null));
|
||||
}
|
||||
}
|
||||
iconLoader.load(imageContainer.imageProperty(), item.iconUrl());
|
||||
setGraphic(wrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,18 +26,26 @@ import org.jackhuang.hmcl.theme.Themes;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.WeakListenerHolder;
|
||||
import org.jackhuang.hmcl.ui.construct.ComponentList;
|
||||
import org.jackhuang.hmcl.ui.construct.ImageContainer;
|
||||
import org.jackhuang.hmcl.ui.construct.LineButton;
|
||||
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
||||
import org.jackhuang.hmcl.ui.construct.*;
|
||||
import org.jackhuang.hmcl.util.DataSizeUnit;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.SystemInfo;
|
||||
import org.jackhuang.hmcl.util.platform.hardware.CentralProcessor;
|
||||
import org.jackhuang.hmcl.util.platform.hardware.GraphicsCard;
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
@NotNullByDefault
|
||||
public final class AboutPage extends SpinnerPane {
|
||||
|
||||
private final WeakListenerHolder holder = new WeakListenerHolder();
|
||||
@@ -52,22 +60,121 @@ public final class AboutPage extends SpinnerPane {
|
||||
|
||||
ComponentList about = new ComponentList();
|
||||
{
|
||||
var launcher = LineButton.createExternalLinkButton(Metadata.PUBLISH_URL);
|
||||
var launcher = LineButton.createExternalLinkButton(Metadata.REPOSITORY_URL);
|
||||
launcher.setLargeTitle(true);
|
||||
launcher.setLeading(FXUtils.newBuiltinImage("/assets/img/icon.png"));
|
||||
launcher.setTitle("Hello Minecraft! Launcher");
|
||||
launcher.setSubtitle(Metadata.VERSION);
|
||||
|
||||
var author = LineButton.createExternalLinkButton("https://space.bilibili.com/1445341");
|
||||
author.setLargeTitle(true);
|
||||
author.setLeading(FXUtils.newBuiltinImage("/assets/img/yellow_fish.png"));
|
||||
author.setTitle("huanghongxun");
|
||||
author.setSubtitle(i18n("about.author.statement"));
|
||||
|
||||
about.getContent().setAll(launcher, author);
|
||||
about.getContent().setAll(launcher);
|
||||
}
|
||||
|
||||
ComponentList thanks = loadIconedTwoLineList("/assets/about/thanks.json");
|
||||
ComponentList environment = new ComponentList();
|
||||
{
|
||||
var system = new LinePane();
|
||||
system.setLargeTitle(true);
|
||||
system.setLeading(SVG.LUCIDE_APP_WINDOW.createIcon(32));
|
||||
system.setTitle(i18n("about.environment.system"));
|
||||
system.setSubtitle((OperatingSystem.OS_RELEASE_PRETTY_NAME == null
|
||||
? "%s %s (%s)".formatted(OperatingSystem.SYSTEM_NAME, OperatingSystem.SYSTEM_VERSION.getVersion(), Architecture.SYSTEM_ARCH.getDisplayName())
|
||||
: "%s (%s %s, %s)".formatted(OperatingSystem.OS_RELEASE_PRETTY_NAME, OperatingSystem.SYSTEM_NAME, OperatingSystem.SYSTEM_VERSION.getVersion(), Architecture.SYSTEM_ARCH.getDisplayName())));
|
||||
environment.getContent().add(system);
|
||||
|
||||
if (SystemInfo.getTotalMemorySize() > 0) {
|
||||
var memory = new LinePane();
|
||||
memory.setLargeTitle(true);
|
||||
memory.setLeading(SVG.LUCIDE_MEMORY_STICK.createIcon(32));
|
||||
memory.setTitle(i18n("about.environment.memory"));
|
||||
memory.setSubtitle("%.1f GiB".formatted(DataSizeUnit.GIGABYTES.convertFromBytes(SystemInfo.getTotalMemorySize())));
|
||||
environment.getContent().add(memory);
|
||||
}
|
||||
|
||||
@Nullable CentralProcessor cpu = SystemInfo.getCentralProcessor();
|
||||
if (cpu != null) {
|
||||
int cpuCount;
|
||||
int logicalPerPackage;
|
||||
int physicalPerPackage;
|
||||
|
||||
CentralProcessor.Cores cores = cpu.getCores();
|
||||
if (cores != null && cores.packages() > 1) {
|
||||
cpuCount = cores.packages();
|
||||
|
||||
// TODO: Usually, all CPUs on a multi-socket platform have the same core count, but environments
|
||||
// like Linux allow some cores to be offline, in which case cores and threads cannot be evenly
|
||||
// distributed. In the future, we may consider detecting the CPU topology in more detail to
|
||||
// handle this case correctly.
|
||||
logicalPerPackage = cores.logical() / cores.packages();
|
||||
physicalPerPackage = cores.physical() / cores.packages();
|
||||
} else {
|
||||
cpuCount = 1;
|
||||
if (cores != null) {
|
||||
logicalPerPackage = cores.logical();
|
||||
physicalPerPackage = cores.physical();
|
||||
} else {
|
||||
logicalPerPackage = 0;
|
||||
physicalPerPackage = 0;
|
||||
}
|
||||
}
|
||||
|
||||
String cpuName;
|
||||
if (logicalPerPackage == 0) {
|
||||
cpuName = i18n("about.environment.cpu.name", cpu.getName());
|
||||
} else if (logicalPerPackage > 0 && logicalPerPackage != physicalPerPackage)
|
||||
cpuName = i18n("about.environment.cpu.name.cores.hyper_threading", cpu.getName(), physicalPerPackage, logicalPerPackage);
|
||||
else
|
||||
cpuName = i18n("about.environment.cpu.name.cores", cpu.getName(), physicalPerPackage);
|
||||
|
||||
for (int i = 1; i <= cpuCount; i++) {
|
||||
var button = new LinePane();
|
||||
button.setLargeTitle(true);
|
||||
button.setTitle(i == 1 && cpuCount == 1
|
||||
? i18n("about.environment.cpu")
|
||||
: i18n("about.environment.cpu.n", i));
|
||||
button.setSubtitle(cpuName);
|
||||
button.setLeading(SVG.LUCIDE_CPU.createIcon(32));
|
||||
environment.getContent().add(button);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable List<GraphicsCard> graphicsCards = SystemInfo.getGraphicsCards();
|
||||
if (graphicsCards != null) {
|
||||
@Unmodifiable List<GraphicsCard> visibleGraphicsCards = graphicsCards.stream()
|
||||
.filter(card -> !card.isVirtualDisplayAdapter())
|
||||
.toList();
|
||||
int index = 1;
|
||||
for (GraphicsCard graphicsCard : visibleGraphicsCards) {
|
||||
String name = index == 1 && visibleGraphicsCards.size() == 1
|
||||
? i18n("about.environment.gpu")
|
||||
: i18n("about.environment.gpu.n", index);
|
||||
index++;
|
||||
var button = new LinePane();
|
||||
button.setLargeTitle(true);
|
||||
button.setTitle(name);
|
||||
button.setSubtitle(graphicsCard.getName());
|
||||
|
||||
button.setLeading(SVG.LUCIDE_GPU.createIcon(32));
|
||||
|
||||
environment.getContent().add(button);
|
||||
}
|
||||
}
|
||||
|
||||
var java = new LinePane();
|
||||
java.setLargeTitle(true);
|
||||
java.setLeading(SVG.LOCAL_CAFE.createIcon(32));
|
||||
java.setTitle("Java");
|
||||
java.setSubtitle("%s, %s, %s".formatted(
|
||||
Runtime.version().toString(),
|
||||
System.getProperty("java.vm.name"),
|
||||
System.getProperty("java.vendor")));
|
||||
environment.getContent().add(java);
|
||||
|
||||
var javaFX = new LinePane();
|
||||
javaFX.setLargeTitle(true);
|
||||
javaFX.setLeading(SVG.LOCAL_CAFE.createIcon(32));
|
||||
javaFX.setTitle("JavaFX");
|
||||
javaFX.setSubtitle(System.getProperty("javafx.runtime.version"));
|
||||
environment.getContent().add(javaFX);
|
||||
}
|
||||
|
||||
ComponentList deps = loadIconedTwoLineList("/assets/about/deps.json");
|
||||
|
||||
@@ -94,8 +201,8 @@ public final class AboutPage extends SpinnerPane {
|
||||
content.getChildren().setAll(
|
||||
ComponentList.createComponentListTitle(i18n("about")),
|
||||
about,
|
||||
ComponentList.createComponentListTitle(i18n("about.thanks_to")),
|
||||
thanks,
|
||||
ComponentList.createComponentListTitle(i18n("about.environment")),
|
||||
environment,
|
||||
ComponentList.createComponentListTitle(i18n("about.dependency")),
|
||||
deps,
|
||||
ComponentList.createComponentListTitle(i18n("about.legal")),
|
||||
@@ -141,7 +248,6 @@ public final class AboutPage extends SpinnerPane {
|
||||
imageView.setImage(loadImage(image.getAsString()));
|
||||
imageView.setMouseTransparent(true);
|
||||
|
||||
|
||||
button.setLeading(imageView);
|
||||
} else if (image.isJsonObject()) {
|
||||
holder.add(FXUtils.onWeakChangeAndOperate(Themes.darkModeProperty(), darkMode -> button.setLeading(darkMode
|
||||
@@ -169,4 +275,5 @@ public final class AboutPage extends SpinnerPane {
|
||||
|
||||
return componentList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,8 @@
|
||||
package org.jackhuang.hmcl.ui.main;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
@@ -30,10 +32,12 @@ import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
||||
import org.jackhuang.hmcl.util.gson.JsonSerializable;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public class HelpPage extends SpinnerPane {
|
||||
|
||||
@@ -62,7 +66,16 @@ public class HelpPage extends SpinnerPane {
|
||||
private void loadHelp() {
|
||||
showSpinner();
|
||||
Task.supplyAsync(() -> HttpRequest.GET(Metadata.DOCS_URL + "/index.json").getJson(listTypeOf(HelpCategory.class)))
|
||||
.thenAcceptAsync(Schedulers.javafx(), helpCategories -> {
|
||||
.whenComplete(Schedulers.javafx(), (helpCategories, exception) -> {
|
||||
if (exception != null) {
|
||||
LOG.warning("Failed to load help page: ", exception);
|
||||
showLoadFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
List<Node> nodes = new ArrayList<>();
|
||||
|
||||
for (HelpCategory category : helpCategories) {
|
||||
ComponentList categoryPane = new ComponentList();
|
||||
|
||||
@@ -75,11 +88,24 @@ public class HelpPage extends SpinnerPane {
|
||||
categoryPane.getContent().add(item);
|
||||
}
|
||||
|
||||
content.getChildren().add(ComponentList.createComponentListTitle(category.title()));
|
||||
content.getChildren().add(categoryPane);
|
||||
nodes.add(ComponentList.createComponentListTitle(category.title()));
|
||||
nodes.add(categoryPane);
|
||||
}
|
||||
|
||||
content.getChildren().addAll(nodes);
|
||||
|
||||
hideSpinner();
|
||||
}).start();
|
||||
} catch (Exception e) {
|
||||
LOG.warning("Failed to display help page: ", e);
|
||||
showLoadFailure();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void showLoadFailure() {
|
||||
setFailedReason(i18n("download.failed.refresh"));
|
||||
setOnFailedAction(e -> loadHelp());
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
@JsonSerializable
|
||||
|
||||
@@ -118,7 +118,8 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
titleNode.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
ImageView titleIcon = new ImageView(FXUtils.newBuiltinImage("/assets/img/icon-title.png"));
|
||||
Label titleLabel = new Label(Metadata.FULL_TITLE);
|
||||
Label titleLabel = new Label();
|
||||
titleLabel.textProperty().bind(Controllers.launcherTitleProperty());
|
||||
if (I18n.isUpsideDown()) {
|
||||
titleIcon.setRotate(180);
|
||||
titleLabel.setRotate(180);
|
||||
|
||||
@@ -31,7 +31,6 @@ import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.account.AccountAdvancedListItem;
|
||||
import org.jackhuang.hmcl.ui.account.AccountListPopupMenu;
|
||||
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
|
||||
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
|
||||
import org.jackhuang.hmcl.ui.construct.AdvancedListItem;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||
@@ -131,9 +130,6 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
|
||||
@Nullable GameInstanceID currentId = currentGame != null ? currentGame.getId() : null;
|
||||
return Lang.indexWhere(list, instance -> instance.getId().equals(currentId));
|
||||
}, instance -> instance.getRepository().setSelectedInstance(instance));
|
||||
if (AnimationUtils.isAnimationEnabled()) {
|
||||
FXUtils.prepareOnMouseEnter(gameListItem, Controllers::prepareGameInstancePage);
|
||||
}
|
||||
FXUtils.onSecondaryButtonClicked(gameListItem, () -> showGameListPopupMenu(gameListItem));
|
||||
|
||||
// third item in left sidebar
|
||||
@@ -148,9 +144,6 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
|
||||
downloadItem.setLeftIcon(SVG.DOWNLOAD);
|
||||
downloadItem.setTitle(i18n("download"));
|
||||
downloadItem.setOnAction(e -> Controllers.navigate(Controllers.getDownloadPage()));
|
||||
if (AnimationUtils.isAnimationEnabled()) {
|
||||
FXUtils.prepareOnMouseEnter(downloadItem, Controllers::prepareDownloadPage);
|
||||
}
|
||||
|
||||
// fifth item in left sidebar
|
||||
AdvancedListItem launcherSettingsItem = new AdvancedListItem();
|
||||
@@ -160,9 +153,6 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
|
||||
Controllers.getSettingsPage().showGameSettings(GameDirectoryManager.getSelectedRepository());
|
||||
Controllers.navigate(Controllers.getSettingsPage());
|
||||
});
|
||||
if (AnimationUtils.isAnimationEnabled()) {
|
||||
FXUtils.prepareOnMouseEnter(launcherSettingsItem, Controllers::prepareSettingsPage);
|
||||
}
|
||||
|
||||
// sixth item in left sidebar
|
||||
AdvancedListItem terracottaItem = new AdvancedListItem();
|
||||
|
||||
@@ -118,10 +118,10 @@ public final class SettingsPage extends ScrollPane {
|
||||
updateButton.setManaged(outdated);
|
||||
updatePane.pseudoClassStateChanged(PseudoClass.getPseudoClass("active"), outdated);
|
||||
|
||||
if (outdated) {
|
||||
lblUpdateSubProperty.set(i18n("update.newest_version", UpdateChecker.getLatestVersion().version()));
|
||||
} else if (UpdateChecker.isCheckingUpdate()) {
|
||||
if (UpdateChecker.isCheckingUpdate()) {
|
||||
lblUpdateSubProperty.set(i18n("update.checking"));
|
||||
} else if (outdated) {
|
||||
lblUpdateSubProperty.set(i18n("update.newest_version", UpdateChecker.getLatestVersion().version()));
|
||||
} else if (UpdateChecker.errorProperty().get() != null) {
|
||||
Throwable t = UpdateChecker.errorProperty().get();
|
||||
if (t instanceof SelfVerificationException) {
|
||||
|
||||
@@ -53,8 +53,8 @@ import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
||||
import org.jackhuang.hmcl.ui.construct.*;
|
||||
import org.jackhuang.hmcl.ui.instances.Instances;
|
||||
import org.jackhuang.hmcl.util.RegionUtils;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.i18n.LocaleUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.Zipper;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
@@ -540,7 +540,7 @@ public class TerracottaControllerPage extends StackPane {
|
||||
|
||||
VBox content = new VBox(10);
|
||||
content.getChildren().add(ComponentList.createComponentListTitle(i18n("terracotta.status")));
|
||||
if (!LocaleUtils.IS_CHINA_MAINLAND) {
|
||||
if (!"CN".equals(RegionUtils.MACHINE_REGION)) {
|
||||
HintPane hintPane = new HintPane(MessageDialogPane.MessageType.WARNING);
|
||||
hintPane.setText(i18n("terracotta.unsupported.region"));
|
||||
content.getChildren().add(hintPane);
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jackhuang.hmcl.util.DigestUtils;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
||||
import org.jackhuang.hmcl.util.io.JarUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -105,18 +106,25 @@ public final class IntegrityChecker {
|
||||
}
|
||||
}
|
||||
|
||||
private static volatile Boolean selfVerified = null;
|
||||
private static volatile @Nullable Boolean selfVerified = null;
|
||||
|
||||
public static @Nullable Boolean getSelfVerified() {
|
||||
return selfVerified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the current application is verified.
|
||||
* This method is blocking.
|
||||
*/
|
||||
public static boolean isSelfVerified() {
|
||||
@Nullable Boolean selfVerified = IntegrityChecker.selfVerified;
|
||||
|
||||
if (selfVerified != null) {
|
||||
return selfVerified;
|
||||
}
|
||||
|
||||
synchronized (IntegrityChecker.class) {
|
||||
selfVerified = IntegrityChecker.selfVerified;
|
||||
if (selfVerified != null) {
|
||||
return selfVerified;
|
||||
}
|
||||
@@ -134,7 +142,7 @@ public final class IntegrityChecker {
|
||||
LOG.warning("Failed to verify myself, is the JAR corrupt?", e);
|
||||
selfVerified = false;
|
||||
}
|
||||
|
||||
IntegrityChecker.selfVerified = selfVerified;
|
||||
return selfVerified;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import org.jackhuang.hmcl.util.i18n.LocaleUtils;
|
||||
import org.jackhuang.hmcl.util.platform.NativeUtils;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.windows.Kernel32;
|
||||
import org.jackhuang.hmcl.util.platform.windows.WinConstants;
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.ZoneId;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
/// Detects the machine's region.
|
||||
@NotNullByDefault
|
||||
public final class RegionUtils {
|
||||
|
||||
/// The two-letter region code detected at class initialization, or `null` if none can be determined.
|
||||
/// Shared time zones require a matching locale region or Windows user GeoID.
|
||||
/// Later changes to system settings do not update this value.
|
||||
public static final @Nullable String MACHINE_REGION = detectMachineRegion();
|
||||
|
||||
/// Detects the region from the system time zone, using [LocaleUtils#SYSTEM_DEFAULT]
|
||||
/// and the Windows user GeoID to resolve ambiguity or supply a missing mapping.
|
||||
///
|
||||
/// @return the detected region code, or `null` if no region can be determined unambiguously
|
||||
private static @Nullable String detectMachineRegion() {
|
||||
@Unmodifiable List<String> candidates;
|
||||
try {
|
||||
candidates = findRegionsByTimeZone(ZoneId.systemDefault().getId());
|
||||
} catch (Exception e) {
|
||||
LOG.warning("Failed to load time zone region mapping", e);
|
||||
candidates = List.of();
|
||||
}
|
||||
|
||||
if (candidates.size() == 1)
|
||||
return candidates.get(0);
|
||||
|
||||
String localeRegion = LocaleUtils.SYSTEM_DEFAULT.getCountry();
|
||||
if (localeRegion.matches("[A-Z]{2}")) {
|
||||
if ((candidates.isEmpty() || candidates.contains(localeRegion)))
|
||||
return localeRegion;
|
||||
} else {
|
||||
localeRegion = null;
|
||||
}
|
||||
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS && NativeUtils.USE_JNA) {
|
||||
@Nullable Kernel32 kernel32 = Kernel32.INSTANCE;
|
||||
|
||||
if (kernel32 != null) {
|
||||
@Nullable String geoidRegion = mapGeoIdToRegion(kernel32.GetUserGeoID(WinConstants.GEOCLASS_NATION));
|
||||
if (geoidRegion != null && (candidates.isEmpty() || candidates.contains(geoidRegion)))
|
||||
return geoidRegion;
|
||||
}
|
||||
}
|
||||
|
||||
if (!candidates.isEmpty())
|
||||
return candidates.get(0);
|
||||
|
||||
return localeRegion;
|
||||
}
|
||||
|
||||
/// Reads the bundled mapping and returns the requested time zone's candidate regions in source order.
|
||||
///
|
||||
/// @param zoneId the time zone identifier to match against the first CSV field
|
||||
/// @return an unmodifiable candidate list, or an empty list if the time zone is not mapped
|
||||
/// @throws IOException if the resource is missing or cannot be read
|
||||
/// @see <a href="https://data.iana.org/time-zones/tzdb/zone1970.tab">tzdb timezone descriptions</a>
|
||||
@VisibleForTesting
|
||||
static @Unmodifiable List<String> findRegionsByTimeZone(String zoneId) throws IOException {
|
||||
try (@Nullable InputStream input = RegionUtils.class.getResourceAsStream("/assets/timezone_regions.csv")) {
|
||||
if (input == null) {
|
||||
throw new IOException("Missing time zone region mapping");
|
||||
}
|
||||
String prefix = zoneId + ",";
|
||||
for (String line : Lang.toIterable(new String(input.readAllBytes(), StandardCharsets.ISO_8859_1).lines())) {
|
||||
if (line.startsWith(prefix))
|
||||
return List.of(line.substring(prefix.length()).split(","));
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the Windows two-letter region code for a known geographical location identifier.
|
||||
/// Historical codes and the Windows code for Kosovo are retained. Locations for which
|
||||
/// Windows reports the placeholder `XX`, including geographical groupings, are not mapped.
|
||||
///
|
||||
/// @param geoId the Windows geographical location identifier
|
||||
/// @return the uppercase region code, or `null` if the identifier has no mapping
|
||||
/// @see <a href="https://learn.microsoft.com/windows/win32/intl/table-of-geographical-locations">Table of Geographical Locations</a>
|
||||
private static @Nullable String mapGeoIdToRegion(int geoId) {
|
||||
return switch (geoId) {
|
||||
case 2 -> "AG";
|
||||
case 3 -> "AF";
|
||||
case 4 -> "DZ";
|
||||
case 5 -> "AZ";
|
||||
case 6 -> "AL";
|
||||
case 7 -> "AM";
|
||||
case 8 -> "AD";
|
||||
case 9 -> "AO";
|
||||
case 10 -> "AS";
|
||||
case 11 -> "AR";
|
||||
case 12 -> "AU";
|
||||
case 14 -> "AT";
|
||||
case 17 -> "BH";
|
||||
case 18 -> "BB";
|
||||
case 19 -> "BW";
|
||||
case 20 -> "BM";
|
||||
case 21 -> "BE";
|
||||
case 22 -> "BS";
|
||||
case 23 -> "BD";
|
||||
case 24 -> "BZ";
|
||||
case 25 -> "BA";
|
||||
case 26 -> "BO";
|
||||
case 27 -> "MM";
|
||||
case 28 -> "BJ";
|
||||
case 29 -> "BY";
|
||||
case 30 -> "SB";
|
||||
case 32 -> "BR";
|
||||
case 34 -> "BT";
|
||||
case 35 -> "BG";
|
||||
case 37 -> "BN";
|
||||
case 38 -> "BI";
|
||||
case 39 -> "CA";
|
||||
case 40 -> "KH";
|
||||
case 41 -> "TD";
|
||||
case 42 -> "LK";
|
||||
case 43 -> "CG";
|
||||
case 44 -> "CD";
|
||||
case 45 -> "CN";
|
||||
case 46 -> "CL";
|
||||
case 49 -> "CM";
|
||||
case 50 -> "KM";
|
||||
case 51 -> "CO";
|
||||
case 54 -> "CR";
|
||||
case 55 -> "CF";
|
||||
case 56 -> "CU";
|
||||
case 57 -> "CV";
|
||||
case 59 -> "CY";
|
||||
case 61 -> "DK";
|
||||
case 62 -> "DJ";
|
||||
case 63 -> "DM";
|
||||
case 65 -> "DO";
|
||||
case 66 -> "EC";
|
||||
case 67 -> "EG";
|
||||
case 68 -> "IE";
|
||||
case 69 -> "GQ";
|
||||
case 70 -> "EE";
|
||||
case 71 -> "ER";
|
||||
case 72 -> "SV";
|
||||
case 73 -> "ET";
|
||||
case 75 -> "CZ";
|
||||
case 77 -> "FI";
|
||||
case 78 -> "FJ";
|
||||
case 80 -> "FM";
|
||||
case 81 -> "FO";
|
||||
case 84 -> "FR";
|
||||
case 86 -> "GM";
|
||||
case 87 -> "GA";
|
||||
case 88 -> "GE";
|
||||
case 89 -> "GH";
|
||||
case 90 -> "GI";
|
||||
case 91 -> "GD";
|
||||
case 93 -> "GL";
|
||||
case 94 -> "DE";
|
||||
case 98 -> "GR";
|
||||
case 99 -> "GT";
|
||||
case 100 -> "GN";
|
||||
case 101 -> "GY";
|
||||
case 103 -> "HT";
|
||||
case 104 -> "HK";
|
||||
case 106 -> "HN";
|
||||
case 108 -> "HR";
|
||||
case 109 -> "HU";
|
||||
case 110 -> "IS";
|
||||
case 111 -> "ID";
|
||||
case 113 -> "IN";
|
||||
case 114 -> "IO";
|
||||
case 116 -> "IR";
|
||||
case 117 -> "IL";
|
||||
case 118 -> "IT";
|
||||
case 119 -> "CI";
|
||||
case 121 -> "IQ";
|
||||
case 122 -> "JP";
|
||||
case 124 -> "JM";
|
||||
case 125 -> "SJ";
|
||||
case 126 -> "JO";
|
||||
case 129 -> "KE";
|
||||
case 130 -> "KG";
|
||||
case 131 -> "KP";
|
||||
case 133 -> "KI";
|
||||
case 134 -> "KR";
|
||||
case 136 -> "KW";
|
||||
case 137 -> "KZ";
|
||||
case 138 -> "LA";
|
||||
case 139 -> "LB";
|
||||
case 140 -> "LV";
|
||||
case 141 -> "LT";
|
||||
case 142 -> "LR";
|
||||
case 143 -> "SK";
|
||||
case 145 -> "LI";
|
||||
case 146 -> "LS";
|
||||
case 147 -> "LU";
|
||||
case 148 -> "LY";
|
||||
case 149 -> "MG";
|
||||
case 151 -> "MO";
|
||||
case 152 -> "MD";
|
||||
case 154 -> "MN";
|
||||
case 156 -> "MW";
|
||||
case 157 -> "ML";
|
||||
case 158 -> "MC";
|
||||
case 159 -> "MA";
|
||||
case 160 -> "MU";
|
||||
case 162 -> "MR";
|
||||
case 163 -> "MT";
|
||||
case 164 -> "OM";
|
||||
case 165 -> "MV";
|
||||
case 166 -> "MX";
|
||||
case 167 -> "MY";
|
||||
case 168 -> "MZ";
|
||||
case 173 -> "NE";
|
||||
case 174 -> "VU";
|
||||
case 175 -> "NG";
|
||||
case 176 -> "NL";
|
||||
case 177 -> "NO";
|
||||
case 178 -> "NP";
|
||||
case 180 -> "NR";
|
||||
case 181 -> "SR";
|
||||
case 182 -> "NI";
|
||||
case 183 -> "NZ";
|
||||
case 184 -> "PS";
|
||||
case 185 -> "PY";
|
||||
case 187 -> "PE";
|
||||
case 190 -> "PK";
|
||||
case 191 -> "PL";
|
||||
case 192 -> "PA";
|
||||
case 193 -> "PT";
|
||||
case 194 -> "PG";
|
||||
case 195 -> "PW";
|
||||
case 196 -> "GW";
|
||||
case 197 -> "QA";
|
||||
case 198 -> "RE";
|
||||
case 199 -> "MH";
|
||||
case 200 -> "RO";
|
||||
case 201 -> "PH";
|
||||
case 202 -> "PR";
|
||||
case 203 -> "RU";
|
||||
case 204 -> "RW";
|
||||
case 205 -> "SA";
|
||||
case 206 -> "PM";
|
||||
case 207 -> "KN";
|
||||
case 208 -> "SC";
|
||||
case 209 -> "ZA";
|
||||
case 210 -> "SN";
|
||||
case 212 -> "SI";
|
||||
case 213 -> "SL";
|
||||
case 214 -> "SM";
|
||||
case 215 -> "SG";
|
||||
case 216 -> "SO";
|
||||
case 217 -> "ES";
|
||||
case 218 -> "LC";
|
||||
case 219 -> "SD";
|
||||
case 220 -> "SJ";
|
||||
case 221 -> "SE";
|
||||
case 222 -> "SY";
|
||||
case 223 -> "CH";
|
||||
case 224 -> "AE";
|
||||
case 225 -> "TT";
|
||||
case 227 -> "TH";
|
||||
case 228 -> "TJ";
|
||||
case 231 -> "TO";
|
||||
case 232 -> "TG";
|
||||
case 233 -> "ST";
|
||||
case 234 -> "TN";
|
||||
case 235 -> "TR";
|
||||
case 236 -> "TV";
|
||||
case 237 -> "TW";
|
||||
case 238 -> "TM";
|
||||
case 239 -> "TZ";
|
||||
case 240 -> "UG";
|
||||
case 241 -> "UA";
|
||||
case 242 -> "GB";
|
||||
case 244 -> "US";
|
||||
case 245 -> "BF";
|
||||
case 246 -> "UY";
|
||||
case 247 -> "UZ";
|
||||
case 248 -> "VC";
|
||||
case 249 -> "VE";
|
||||
case 251 -> "VN";
|
||||
case 252 -> "VI";
|
||||
case 253 -> "VA";
|
||||
case 254 -> "NA";
|
||||
case 259 -> "WS";
|
||||
case 260 -> "SZ";
|
||||
case 261 -> "YE";
|
||||
case 263 -> "ZM";
|
||||
case 264 -> "ZW";
|
||||
case 269 -> "CS";
|
||||
case 270 -> "ME";
|
||||
case 271 -> "RS";
|
||||
case 273 -> "CW";
|
||||
case 276 -> "SS";
|
||||
case 300 -> "AI";
|
||||
case 301 -> "AQ";
|
||||
case 302 -> "AW";
|
||||
case 306 -> "BV";
|
||||
case 307 -> "KY";
|
||||
case 309 -> "CX";
|
||||
case 311 -> "CC";
|
||||
case 312 -> "CK";
|
||||
case 315 -> "FK";
|
||||
case 317 -> "GF";
|
||||
case 318 -> "PF";
|
||||
case 319 -> "TF";
|
||||
case 321 -> "GP";
|
||||
case 322 -> "GU";
|
||||
case 324 -> "GG";
|
||||
case 325 -> "HM";
|
||||
case 328 -> "JE";
|
||||
case 330 -> "MQ";
|
||||
case 331 -> "YT";
|
||||
case 332 -> "MS";
|
||||
case 333 -> "AN";
|
||||
case 334 -> "NC";
|
||||
case 335 -> "NU";
|
||||
case 336 -> "NF";
|
||||
case 337 -> "MP";
|
||||
case 339 -> "PN";
|
||||
case 342 -> "GS";
|
||||
case 343 -> "SH";
|
||||
case 347 -> "TK";
|
||||
case 349 -> "TC";
|
||||
case 351 -> "VG";
|
||||
case 352 -> "WF";
|
||||
case 15126 -> "IM";
|
||||
case 19618 -> "MK";
|
||||
case 30967 -> "SX";
|
||||
case 31706 -> "MF";
|
||||
case 7299303 -> "TL";
|
||||
case 9914689 -> "XK";
|
||||
case 10028789 -> "AX";
|
||||
case 161832015 -> "BL";
|
||||
case 161832256 -> "UM";
|
||||
case 161832258 -> "BQ";
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
/// Prevents instantiation of this utility class.
|
||||
private RegionUtils() {
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,7 @@
|
||||
},
|
||||
{
|
||||
"title": "CommonMark",
|
||||
"subtitle": "Copyright (c) 2015 Robin Stocker.\nAll rights reserved.",
|
||||
"subtitle": "Copyright © 2015 Robin Stocker.\nLicensed under the BSD 2-clause License.",
|
||||
"externalLink": "https://github.com/commonmark/commonmark-java"
|
||||
}
|
||||
]
|
||||
@@ -1,94 +0,0 @@
|
||||
[
|
||||
{
|
||||
"image" : "/assets/img/yushijinhun.png",
|
||||
"title" : "yushijinhun",
|
||||
"subtitleLocalized" : "about.thanks_to.yushijinhun.statement",
|
||||
"externalLink" : "https://github.com/yushijinhun"
|
||||
},
|
||||
{
|
||||
"image" : "/assets/img/bangbang93.png",
|
||||
"title" : "bangbang93",
|
||||
"subtitleLocalized" : "about.thanks_to.bangbang93.statement",
|
||||
"externalLink" : "https://www.bangbang93.com"
|
||||
},
|
||||
{
|
||||
"image" : "/assets/img/glavo.png",
|
||||
"title" : "Glavo",
|
||||
"subtitleLocalized" : "about.thanks_to.glavo.statement",
|
||||
"externalLink" : "https://github.com/Glavo"
|
||||
},
|
||||
{
|
||||
"image" : "/assets/img/zekerzhayard.png",
|
||||
"title" : "ZekerZhayard",
|
||||
"subtitleLocalized" : "about.thanks_to.zekerzhayard.statement",
|
||||
"externalLink" : "https://github.com/ZekerZhayard"
|
||||
},
|
||||
{
|
||||
"image" : "/assets/img/zkitefly.png",
|
||||
"title" : "Zkitefly",
|
||||
"subtitleLocalized" : "about.thanks_to.zkitefly.statement",
|
||||
"externalLink" : "https://github.com/zkitefly"
|
||||
},
|
||||
{
|
||||
"image" : "/assets/img/burningtnt.png",
|
||||
"title" : "Burning_TNT",
|
||||
"subtitleLocalized" : "about.thanks_to.burningtnt.statement",
|
||||
"externalLink" : "https://github.com/burningtnt"
|
||||
},
|
||||
{
|
||||
"image" : "/assets/img/ShulkerSakura.png",
|
||||
"title" : "ShulkerSakura",
|
||||
"subtitleLocalized" : "about.thanks_to.shulkersakura.statement",
|
||||
"externalLink" : "https://github.com/ShulkerSakura"
|
||||
},
|
||||
{
|
||||
"image" : "/assets/img/gamerteam.png",
|
||||
"title" : "gamerteam",
|
||||
"subtitleLocalized" : "about.thanks_to.gamerteam.statement",
|
||||
"externalLink" : "https://github.com/ZhaiSoul"
|
||||
},
|
||||
{
|
||||
"image" : "/assets/img/red_lnn.png",
|
||||
"title" : "Red_lnn",
|
||||
"subtitleLocalized" : "about.thanks_to.red_lnn.statement",
|
||||
"externalLink" : "https://redlnn.top"
|
||||
},
|
||||
{
|
||||
"image" : "/assets/img/mcmod.png",
|
||||
"titleLocalized" : "about.thanks_to.mcmod",
|
||||
"subtitleLocalized" : "about.thanks_to.mcmod.statement",
|
||||
"externalLink" : "https://www.mcmod.cn"
|
||||
},
|
||||
{
|
||||
"image" : "/assets/img/chest.png",
|
||||
"titleLocalized" : "about.thanks_to.mcbbs",
|
||||
"subtitleLocalized" : "about.thanks_to.mcbbs.statement"
|
||||
},
|
||||
{
|
||||
"image" : "/assets/img/mcim.png",
|
||||
"titleLocalized" : "about.thanks_to.mcim",
|
||||
"subtitleLocalized" : "about.thanks_to.mcim.statement",
|
||||
"externalLink" : "https://github.com/mcmod-info-mirror"
|
||||
},
|
||||
{
|
||||
"image" : "/assets/img/8mi-tech.png",
|
||||
"titleLocalized" : "about.thanks_to.8mi-tech",
|
||||
"subtitleLocalized" : "about.thanks_to.8mi-tech.statement",
|
||||
"externalLink" : "https://8mi.tech"
|
||||
},
|
||||
{
|
||||
"image" : {
|
||||
"light" : "/assets/img/github.png",
|
||||
"dark" : "/assets/img/github-white.png"
|
||||
},
|
||||
"titleLocalized" : "about.thanks_to.contributors",
|
||||
"subtitleLocalized" : "about.thanks_to.contributors.statement",
|
||||
"externalLink" : "https://github.com/HMCL-dev/HMCL/graphs/contributors"
|
||||
},
|
||||
{
|
||||
"image" : "/assets/img/icon.png",
|
||||
"titleLocalized" : "about.thanks_to.users",
|
||||
"subtitleLocalized" : "about.thanks_to.users.statement",
|
||||
"externalLink" : "https://docs.hmcl.net/groups.html"
|
||||
}
|
||||
]
|
||||
@@ -498,7 +498,7 @@
|
||||
506,,,莱姆星云,Lime Nebula,LN
|
||||
507,void-genesis,,Void Genesis,,
|
||||
508,create-flavored,,Create Flavored,,
|
||||
509,terrafirmagreg-vintage,,群峦格雷,TerraFirmaGreg,TFG
|
||||
509,terrafirmagreg-modern,,群峦格雷,TerraFirmaGreg,TFG
|
||||
510,create-astral,,机械动力:星辰,Create: Astral,
|
||||
511,,create-origin,起源创造,CreateOrigin,CO
|
||||
512,random-item-skyblock-recondite-i,,Random Item Island - Recondite I,,
|
||||
@@ -726,7 +726,7 @@
|
||||
738,all-the-mods-9-no-frills,,All the Mods 9 - No Frills,,ATM9NF
|
||||
739,toms-cobblemon,toms-cobblemon,Tom's Cobblemon,,
|
||||
740,life-in-the-village-3,,Life in the village 3,,LitV3
|
||||
741,,,联缘:NEXUS,,
|
||||
741,,,NEXUSrpg,,
|
||||
742,,create-cinematic,Create: Cinematic,,
|
||||
743,,,神秘纪元,Thaumic Epoch,ThE
|
||||
745,,,FTB Genesis,,
|
||||
@@ -998,7 +998,7 @@
|
||||
1017,kumo-o-tagayasu,,耕云钓月,Kumo O Tagayasu,KOTS
|
||||
1018,,,无限战争4,Infinity War4,INF4
|
||||
1019,,,群峦传说:工业长路,,
|
||||
1020,,,自然冒险 新生,Nature Adventure Newborn,NAB
|
||||
1020,,,自然冒险 新生,Nature Adventure Newborn,NAB
|
||||
1021,asig,asig,浅忆基础整合,Asai Integrated Modpack,
|
||||
1022,create-star-valley,,Create: Gears of History,,
|
||||
1023,,,阿尔法计划,AlphaPlan,AP
|
||||
@@ -1155,7 +1155,7 @@
|
||||
1177,ciscos-fantasy-medieval-rpg-dragonfyre,,Cisco's Fantasy Medieval RPG [Dragonfyre],,
|
||||
1178,all-the-mods-10-sky,,All the Mods 10: To the Sky,,ATM10S
|
||||
1179,,,谐伴匠途,,
|
||||
1181,,turbo_vulkan,转变思路优化,New-Path Optimization,NO
|
||||
1181,,turbo_vulkan,转变思路优化,\u200cNew-Path Optimization,NO
|
||||
1182,uls-zsurvival-craft,,Z-Liminex,,
|
||||
1183,,,MITE 重生 3,MITE Reborn 3,MR
|
||||
1184,reclamation-reclaim-the-world,,Reclamation - Reclaim the World!,,
|
||||
@@ -1164,7 +1164,7 @@
|
||||
1187,project-nope-zone,,Project Nope Zone,,PNZ
|
||||
1188,all-the-simple-zero,all-the-simple-zero,一切简易,All the Simple,ATS
|
||||
1189,,,匠战妖域,Tinkers' Warfare Demon Realm,twdr
|
||||
1190,,,空想生存,,
|
||||
1190,reverie-survival,,空想生存,,
|
||||
1191,,,东方起源鉴,Touhou Origins Annals,TOA
|
||||
1192,realm-of-destiny,,生命之境,Realm of Destiny,ROD
|
||||
1193,,,奇妙的世界,Special World,
|
||||
@@ -1241,7 +1241,7 @@
|
||||
1264,arcadia-rpg,,Arcadia[RPG],,
|
||||
1265,ozone-skyblock-reborn-2,,Ozone Skyblock Reborn 2,,
|
||||
1266,witarcland,,维塔克兰,Witarcland,WTL
|
||||
1267,big-globe-optimized-forge,big-globe-optimized-forge,Big Globe Optimized,,
|
||||
1267,big-globe-optimized-forge,big-globe-optimized,Big Globe Optimized,,
|
||||
1268,,656youhua,GD656轻量优化打底整合包,GD656 Ultimate Optimized,GDO
|
||||
1269,terra-firma-far-horizons,,群峦传说:野望,Terra Firma Far Horizons,TFFH
|
||||
1270,,,烬蓝物语 2,BlueAsh Things II,BAT2
|
||||
@@ -1498,3 +1498,20 @@
|
||||
1524,create-village-to-sky,,机械动力:下村上天,Create Village to Sky,
|
||||
1525,essence-odyssey,,本源远征,Essence Odyssey,
|
||||
1526,ftb-presents-architects-exodus,,FTB Presents Architect's Exodus,,
|
||||
1527,,,机械动力 x 格雷科技,Create x GregTech,CTGT
|
||||
1528,infinity-legacy-2,,无限遗产2,Infinity Legacy 2,IL2
|
||||
1529,,,千界万刃,Myriad Dimension Blades,
|
||||
1530,,bmtr,更好的我的世界铁路,Better MTR,BMTR
|
||||
1531,space-tech-integrated,,航天科技:集成,Space Tech: Integrated,
|
||||
1532,,,AE-无限工厂,AE2 Infinite Factory,AIF
|
||||
1533,verdant-sky,,Verdant Sky,,
|
||||
1534,,,空天工程师,Aerogineer,
|
||||
1535,generations-modpack-forge,,Generations,,
|
||||
1536,,,农夫但是劲大,Farmers But Powerful,
|
||||
1537,,,神道传说,,GR
|
||||
1538,createintegrated,,Create Integrated,,
|
||||
1539,vcmc1-16-5,,唯我生存,VeselfCraft,VCMC
|
||||
1540,,,侏罗纪重生之旅,Jurassic Reborn,
|
||||
1541,,,暗涌:深岩恐惧,Undercurrent: Fear In the Deep Rock,FIDR
|
||||
1542,cube-warper,,Cube Warper,,
|
||||
1543,ftb-evolution-2,,FTB Evolution 2,,
|
||||
|
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 16 KiB |
@@ -22,33 +22,20 @@
|
||||
about=About
|
||||
about.copyright=Copyright
|
||||
about.copyright.statement=Copyright © 2013-2026 huangyuhui and contributors.
|
||||
about.author.statement=bilibili @huanghongxun
|
||||
about.claim=EULA
|
||||
about.claim.statement=Click this link for full text.
|
||||
about.environment=Environment
|
||||
about.environment.cpu=CPU
|
||||
about.environment.cpu.n=CPU %d
|
||||
about.environment.cpu.name=%s
|
||||
about.environment.cpu.name.cores=%s (%d cores)
|
||||
about.environment.cpu.name.cores.hyper_threading=%s (%d cores / %d threads)
|
||||
about.environment.gpu=GPU
|
||||
about.environment.gpu.n=GPU %d
|
||||
about.environment.memory=Memory
|
||||
about.environment.system=Operating System
|
||||
about.dependency=Third-party Libraries
|
||||
about.legal=Legal Acknowledgement
|
||||
about.thanks_to=Thanks to
|
||||
about.thanks_to.bangbang93.statement=For providing the BMCLAPI download mirror. Please consider donating!
|
||||
about.thanks_to.burningtnt.statement=Contribute a lot of technical support to HMCL.
|
||||
about.thanks_to.contributors=All contributors on GitHub
|
||||
about.thanks_to.contributors.statement=Without the awesome open-source community, HMCL would not have made it so far.
|
||||
about.thanks_to.gamerteam.statement=For providing the default background image.
|
||||
about.thanks_to.glavo.statement=Responsible for maintaining HMCL.
|
||||
about.thanks_to.zekerzhayard.statement=Contribute a lot of technical support to HMCL.
|
||||
about.thanks_to.zkitefly.statement=Responsible for maintaining the documentation of HMCL.
|
||||
about.thanks_to.mcbbs=MCBBS (Minecraft Chinese Forum)
|
||||
about.thanks_to.mcbbs.statement=For providing the mcbbs.net download mirror for Chinese Mainland users. (No longer available)
|
||||
about.thanks_to.mcim=mcmod-info-mirror
|
||||
about.thanks_to.mcim.statement=For providing the mod info cache acceleration service for Chinese Mainland users.
|
||||
about.thanks_to.mcmod=MCMod (mcmod.cn)
|
||||
about.thanks_to.mcmod.statement=For providing the Simplified Chinese translations and wiki for various mods.
|
||||
about.thanks_to.red_lnn.statement=For providing the default background image.
|
||||
about.thanks_to.shulkersakura.statement=For providing the logo for HMCL.
|
||||
about.thanks_to.users=HMCL User Group Members
|
||||
about.thanks_to.users.statement=Thanks for donations, bug reports, and so on.
|
||||
about.thanks_to.yushijinhun.statement=For providing the authlib-injector related support.
|
||||
about.thanks_to.8mi-tech=8Mi-Tech (Quanzhou) Co., Ltd.
|
||||
about.thanks_to.8mi-tech.statement=Provides an accelerated download service for some of the game's static resources.
|
||||
about.open_source=Open Source
|
||||
about.open_source.statement=GPL v3 (https://github.com/HMCL-dev/HMCL)
|
||||
|
||||
@@ -144,6 +131,7 @@ account.move_to_portable=Convert to Portable Account\nThe account information wi
|
||||
account.password=Password
|
||||
account.portable=Portable
|
||||
account.skin=Skin
|
||||
account.skin.edit=Edit Skin
|
||||
account.skin.file=Skin File
|
||||
account.skin.model=Model
|
||||
account.skin.model.default=Classic
|
||||
@@ -156,7 +144,7 @@ account.skin.type.little_skin=LittleSkin
|
||||
account.skin.type.little_skin.hint=You need to create a player with the same player name as your offline account on your skin provider site. Your skin will now be set to the skin assigned to your player on the skin provider site.
|
||||
account.skin.type.local_file=Local Skin File
|
||||
account.skin.type.steve=Steve
|
||||
account.skin.upload=Upload/Edit Skin
|
||||
account.skin.upload=Upload Skin
|
||||
account.skin.upload.failed=Failed to upload skin.
|
||||
account.skin.invalid_skin=Invalid skin file.
|
||||
account.username=Username
|
||||
@@ -382,6 +370,7 @@ curse.category.6555=Vanilla
|
||||
datetime.format=MMM d, yyyy, h\:mm\:ss a
|
||||
|
||||
download=Download
|
||||
download.china=Due to relevant laws and regulations, HMCL does not currently support downloading game-related content in Chinese Mainland. Please visit the <a href="https://mc.163.com/">official Minecraft website</a> to download Minecraft China Edition.
|
||||
download.code.404=File "%s" not found on the remote server.
|
||||
download.content=Addons
|
||||
download.shader=Shaders
|
||||
@@ -959,6 +948,9 @@ launcher.contact=Contact Us
|
||||
launcher.crash=HMCL has encountered a fatal error! Please copy the following log and ask for help on our Discord, QQ group, GitHub, or other Minecraft forum.
|
||||
launcher.crash.java_internal_error=HMCL has encountered a fatal error because your Java is corrupted. Please uninstall your Java and download a suitable Java <a href="https://bell-sw.com/pages/downloads/#downloads">here</a>.
|
||||
launcher.crash.hmcl_out_dated=HMCL has encountered a fatal error! Your launcher is outdated. Please update your launcher!
|
||||
launcher.title=Hello Minecraft! Launcher v%s
|
||||
launcher.title.modified=Hello Minecraft! Launcher v%s (Modified)
|
||||
launcher.title.unofficial=Hello Minecraft! Launcher v%s (Unofficial Build)
|
||||
launcher.update_java=Please update your Java version.
|
||||
|
||||
libraries.download=Downloading Libraries
|
||||
|
||||
@@ -22,33 +22,20 @@
|
||||
about=حول
|
||||
about.copyright=حقوق النشر
|
||||
about.copyright.statement=حقوق النشر © 2013-2026 huangyuhui والمساهمون.
|
||||
about.author.statement=bilibili @huanghongxun
|
||||
about.claim=اتفاقية الترخيص
|
||||
about.claim.statement=انقر على هذا الرابط للاطلاع على النص الكامل.
|
||||
# about.environment=
|
||||
# about.environment.cpu=
|
||||
# about.environment.cpu.n=
|
||||
# about.environment.cpu.name=
|
||||
# about.environment.cpu.name.cores=
|
||||
# about.environment.cpu.name.cores.hyper_threading=
|
||||
# about.environment.gpu=
|
||||
# about.environment.gpu.n=
|
||||
# about.environment.memory=
|
||||
# about.environment.system=
|
||||
about.dependency=مكتبات الطرف الثالث
|
||||
about.legal=الإقرارات القانونية
|
||||
about.thanks_to=شكر وتقدير
|
||||
about.thanks_to.bangbang93.statement=لتوفيره مرآة التنزيل BMCLAPI. يُرجى التفكير في التبرع!
|
||||
about.thanks_to.burningtnt.statement=قدّم دعماً تقنياً كبيراً لـ HMCL.
|
||||
about.thanks_to.contributors=جميع المساهمين على GitHub
|
||||
about.thanks_to.contributors.statement=بدون مجتمع المصدر المفتوح الرائع، لم يكن HMCL ليصل إلى ما هو عليه اليوم.
|
||||
about.thanks_to.gamerteam.statement=لتوفير صورة الخلفية الافتراضية.
|
||||
about.thanks_to.glavo.statement=مسؤول عن صيانة HMCL.
|
||||
about.thanks_to.zekerzhayard.statement=قدّم دعماً تقنياً كبيراً لـ HMCL.
|
||||
about.thanks_to.zkitefly.statement=مسؤول عن صيانة توثيق HMCL.
|
||||
about.thanks_to.mcbbs=MCBBS (منتدى Minecraft الصيني)
|
||||
about.thanks_to.mcbbs.statement=لتوفير مرآة تنزيل mcbbs.net لمستخدمي البر الرئيسي الصيني. (لم تعد متاحة)
|
||||
about.thanks_to.mcim=mcmod-info-mirror
|
||||
about.thanks_to.mcim.statement=لتوفير خدمة تسريع ذاكرة التخزين المؤقت لمعلومات الموديفكيشن لمستخدمي البر الرئيسي الصيني.
|
||||
about.thanks_to.mcmod=MCMod (mcmod.cn)
|
||||
about.thanks_to.mcmod.statement=لتوفير ترجمات الصينية المبسطة وويكي لمختلف الموديفكيشنز.
|
||||
about.thanks_to.red_lnn.statement=لتوفير صورة الخلفية الافتراضية.
|
||||
about.thanks_to.shulkersakura.statement=لتوفير الشعار الخاص بـ HMCL.
|
||||
about.thanks_to.users=أعضاء مجموعة مستخدمي HMCL
|
||||
about.thanks_to.users.statement=شكراً على التبرعات وتقارير الأخطاء وغير ذلك.
|
||||
about.thanks_to.yushijinhun.statement=لتوفير الدعم المتعلق بـ authlib-injector.
|
||||
# about.thanks_to.8mi-tech=
|
||||
# about.thanks_to.8mi-tech.statement=
|
||||
about.open_source=المصدر المفتوح
|
||||
about.open_source.statement=GPL v3 (https://github.com/HMCL-dev/HMCL)
|
||||
|
||||
@@ -145,6 +132,7 @@ account.move_to_portable=تحويل إلى حساب محمول\nسيتم حفظ
|
||||
account.password=كلمة المرور
|
||||
account.portable=محمول
|
||||
account.skin=المظهر
|
||||
account.skin.edit=تعديل المظهر
|
||||
account.skin.file=ملف المظهر
|
||||
account.skin.model=النموذج
|
||||
account.skin.model.default=كلاسيكي
|
||||
@@ -157,7 +145,7 @@ account.skin.type.little_skin=LittleSkin
|
||||
account.skin.type.little_skin.hint=تحتاج إلى إنشاء لاعب بنفس اسم حسابك غير المتصل على موقع مزود المظهر. سيتم تعيين مظهرك على المظهر المعيّن للاعبك على موقع مزود المظهر.
|
||||
account.skin.type.local_file=ملف مظهر محلي
|
||||
account.skin.type.steve=Steve
|
||||
account.skin.upload=رفع/تعديل المظهر
|
||||
account.skin.upload=رفع المظهر
|
||||
account.skin.upload.failed=فشل رفع المظهر.
|
||||
account.skin.invalid_skin=ملف المظهر غير صالح.
|
||||
account.username=اسم المستخدم
|
||||
@@ -384,6 +372,7 @@ curse.category.4552=سكريبتات
|
||||
datetime.format=MMM d, yyyy, h\:mm\:ss a
|
||||
|
||||
download=تنزيل
|
||||
# download.china=
|
||||
download.code.404=الملف "%s" غير موجود على الخادم البعيد.
|
||||
download.content=الإضافات
|
||||
download.shader=الظلال
|
||||
@@ -960,6 +949,9 @@ launcher.contact=تواصل معنا
|
||||
launcher.crash=واجه HMCL خطأً فادحاً! يُرجى نسخ السجل التالي وطلب المساعدة على Discord أو مجموعة QQ أو GitHub أو أي منتدى Minecraft آخر.
|
||||
launcher.crash.java_internal_error=واجه HMCL خطأً فادحاً بسبب تلف Java. يُرجى إلغاء تثبيت Java وتنزيل إصدار مناسب من <a href="https://bell-sw.com/pages/downloads/#downloads">هنا</a>.
|
||||
launcher.crash.hmcl_out_dated=واجه HMCL خطأً فادحاً! المشغّل قديم. يُرجى تحديثه!
|
||||
# launcher.title=
|
||||
# launcher.title.modified=
|
||||
# launcher.title.unofficial=
|
||||
launcher.update_java=يُرجى تحديث إصدار Java الخاص بك.
|
||||
|
||||
libraries.download=جارٍ تنزيل المكتبات
|
||||
|
||||
@@ -21,33 +21,20 @@
|
||||
about=Über
|
||||
about.copyright=Urheberrecht
|
||||
about.copyright.statement=Copyright © 2013-2026 huangyuhui und Mitwirkende.
|
||||
about.author.statement=bilibili @huanghongxun
|
||||
about.claim=EULA
|
||||
about.claim.statement=Klicken Sie auf diesen Link für den vollständigen Text.
|
||||
# about.environment=
|
||||
# about.environment.cpu=
|
||||
# about.environment.cpu.n=
|
||||
# about.environment.cpu.name=
|
||||
# about.environment.cpu.name.cores=
|
||||
# about.environment.cpu.name.cores.hyper_threading=
|
||||
# about.environment.gpu=
|
||||
# about.environment.gpu.n=
|
||||
# about.environment.memory=
|
||||
# about.environment.system=
|
||||
about.dependency=Drittanbieter-Bibliotheken
|
||||
about.legal=Rechtliche Hinweise
|
||||
about.thanks_to=Danksagung an
|
||||
about.thanks_to.bangbang93.statement=Für die Bereitstellung des BMCLAPI-Download-Spiegels. Bitte ziehen Sie eine Spende in Betracht!
|
||||
about.thanks_to.burningtnt.statement=Hat viel technische Unterstützung für HMCL beigesteuert.
|
||||
about.thanks_to.contributors=Alle Mitwirkenden auf GitHub
|
||||
about.thanks_to.contributors.statement=Ohne die großartige Open-Source-Community wäre HMCL nicht so weit gekommen.
|
||||
about.thanks_to.gamerteam.statement=Für die Bereitstellung des Standard-Hintergrundbilds.
|
||||
about.thanks_to.glavo.statement=Verantwortlich für die Pflege von HMCL.
|
||||
about.thanks_to.zekerzhayard.statement=Hat viel technische Unterstützung für HMCL beigesteuert.
|
||||
about.thanks_to.zkitefly.statement=Verantwortlich für die Pflege der HMCL-Dokumentation.
|
||||
about.thanks_to.mcbbs=MCBBS (Minecraft Chinese Forum)
|
||||
about.thanks_to.mcbbs.statement=Für die Bereitstellung des mcbbs.net-Download-Spiegels für Benutzer auf dem chinesischen Festland. (Nicht mehr verfügbar)
|
||||
about.thanks_to.mcim=mcmod-info-mirror
|
||||
about.thanks_to.mcim.statement=Für die Bereitstellung des Cache-Beschleunigungsdienstes für Mod-Informationen für Benutzer auf dem chinesischen Festland.
|
||||
about.thanks_to.mcmod=MCMod (mcmod.cn)
|
||||
about.thanks_to.mcmod.statement=Für die Bereitstellung der vereinfachten chinesischen Übersetzungen und des Wikis für verschiedene Mods.
|
||||
about.thanks_to.red_lnn.statement=Für die Bereitstellung des Standard-Hintergrundbilds.
|
||||
about.thanks_to.shulkersakura.statement=Für die Bereitstellung des Logos für HMCL.
|
||||
about.thanks_to.users=Mitglieder der HMCL-Benutzergruppe
|
||||
about.thanks_to.users.statement=Danke für Spenden, Fehlerberichte und mehr.
|
||||
about.thanks_to.yushijinhun.statement=Für die Bereitstellung des authlib-injector-Supports.
|
||||
about.thanks_to.8mi-tech=8Mi-Tech (Quanzhou) Co., Ltd.
|
||||
about.thanks_to.8mi-tech.statement=Bietet einen beschleunigten Download-Dienst für einige der statischen Ressourcen des Spiels.
|
||||
about.open_source=Open Source
|
||||
about.open_source.statement=GPL v3 (https://github.com/HMCL-dev/HMCL)
|
||||
|
||||
@@ -143,6 +130,7 @@ account.move_to_portable=In portables Konto umwandeln\nDie Kontoinformationen we
|
||||
account.password=Passwort
|
||||
account.portable=Portabel
|
||||
account.skin=Skin
|
||||
account.skin.edit=Skin bearbeiten
|
||||
account.skin.file=Skin-Datei
|
||||
account.skin.model=Modell
|
||||
account.skin.model.default=Klassisch
|
||||
@@ -155,7 +143,7 @@ account.skin.type.little_skin=LittleSkin
|
||||
account.skin.type.little_skin.hint=Sie müssen einen Spieler mit demselben Spielernamen wie Ihr Offline-Konto auf der Seite Ihres Skin-Anbieters erstellen. Ihr Skin wird nun auf den Skin festgelegt, der Ihrem Spieler auf der Skin-Anbieter-Seite zugewiesen ist.
|
||||
account.skin.type.local_file=Lokale Skin-Datei
|
||||
account.skin.type.steve=Steve
|
||||
account.skin.upload=Skin hochladen/bearbeiten
|
||||
account.skin.upload=Skin hochladen
|
||||
account.skin.upload.failed=Fehler beim Hochladen des Skins.
|
||||
account.skin.invalid_skin=Ungültige Skin-Datei.
|
||||
account.username=Benutzername
|
||||
@@ -381,6 +369,7 @@ curse.category.6555=Vanilla
|
||||
datetime.format=dd.MM.yyyy, HH\:mm\:ss
|
||||
|
||||
download=Herunterladen
|
||||
# download.china=
|
||||
download.code.404=Datei "%s" wurde nicht auf dem Remote-Server gefunden.
|
||||
download.content=Addons
|
||||
download.shader=Shaderpakete
|
||||
@@ -959,6 +948,9 @@ launcher.contact=Kontaktiere uns
|
||||
launcher.crash=HMCL ist auf einen fatalen Fehler gestoßen! Bitte kopieren Sie das folgende Protokoll und bitten Sie in unserem Discord, der QQ-Gruppe, auf GitHub oder in einem anderen Minecraft-Forum um Hilfe.
|
||||
launcher.crash.java_internal_error=HMCL ist auf einen fatalen Fehler gestoßen, da Ihre Java-Installation beschädigt ist. Bitte deinstallieren Sie Java und laden Sie eine geeignete Java-Version <a href="https://bell-sw.com/pages/downloads/#downloads">hier</a> herunter.
|
||||
launcher.crash.hmcl_out_dated=HMCL ist auf einen fatalen Fehler gestoßen! Ihr Launcher ist veraltet. Bitte aktualisieren Sie Ihren Launcher!
|
||||
# launcher.title=
|
||||
# launcher.title.modified=
|
||||
# launcher.title.unofficial=
|
||||
launcher.update_java=Bitte aktualisieren Sie Ihre Java-Version.
|
||||
|
||||
libraries.download=Bibliotheken werden heruntergeladen
|
||||
|
||||
@@ -22,33 +22,20 @@
|
||||
about=Acerca de
|
||||
about.copyright=Copyright
|
||||
# about.copyright.statement=
|
||||
about.author.statement=bilibili @huanghongxun
|
||||
about.claim=EULA
|
||||
about.claim.statement=Haga clic en este enlace para ver el texto completo.
|
||||
# about.environment=
|
||||
# about.environment.cpu=
|
||||
# about.environment.cpu.n=
|
||||
# about.environment.cpu.name=
|
||||
# about.environment.cpu.name.cores=
|
||||
# about.environment.cpu.name.cores.hyper_threading=
|
||||
# about.environment.gpu=
|
||||
# about.environment.gpu.n=
|
||||
# about.environment.memory=
|
||||
# about.environment.system=
|
||||
about.dependency=Bibliotecas de terceros
|
||||
about.legal=Reconocimiento legal
|
||||
about.thanks_to=Gracias a
|
||||
about.thanks_to.bangbang93.statement=Por proporcionar el espejo de descarga de BMCLAPI, ¡por favor, considere hacer una donación!
|
||||
about.thanks_to.burningtnt.statement=Aportar mucho apoyo técnico a HMCL.
|
||||
about.thanks_to.contributors=Todos los colaboradores en GitHub
|
||||
about.thanks_to.contributors.statement=Sin la impresionante comunidad de código abierto, HMCL no habría llegado tan lejos.
|
||||
about.thanks_to.gamerteam.statement=Por proporcionar la imagen de fondo por defecto.
|
||||
about.thanks_to.glavo.statement=Responsable del mantenimiento de HMCL.
|
||||
about.thanks_to.zekerzhayard.statement=Aportar mucho apoyo técnico a HMCL.
|
||||
about.thanks_to.zkitefly.statement=Responsable del mantenimiento de la documentación de HMCL.
|
||||
about.thanks_to.mcbbs=MCBBS (Foro Chino Minecraft)
|
||||
about.thanks_to.mcbbs.statement=Por proporcionar el espejo de descarga de mcbbs.net para los usuarios de China continental. (Ya no disponible)
|
||||
about.thanks_to.mcim=mcmod-info-mirror
|
||||
about.thanks_to.mcim.statement=Por proporcionar el servicio de aceleración de caché de información de mods para los usuarios de China continental.
|
||||
about.thanks_to.mcmod=MCMod (mcmod.cn)
|
||||
about.thanks_to.mcmod.statement=Por proporcionar las traducciones al chino simplificado y la wiki de varios mods.
|
||||
about.thanks_to.red_lnn.statement=Por proporcionar la imagen de fondo por defecto.
|
||||
about.thanks_to.shulkersakura.statement=Por proporcionar el logotipo de HMCL.
|
||||
about.thanks_to.users=Miembros del grupo de usuarios de HMCL
|
||||
about.thanks_to.users.statement=Gracias por las donaciones, los informes de errores, etc.
|
||||
about.thanks_to.yushijinhun.statement=Por proporcionar el soporte relacionado con authlib-injector.
|
||||
# about.thanks_to.8mi-tech=
|
||||
# about.thanks_to.8mi-tech.statement=
|
||||
about.open_source=Código abierto
|
||||
about.open_source.statement=GPL v3 (https://github.com/HMCL-dev/HMCL)
|
||||
|
||||
@@ -143,6 +130,7 @@ account.move_to_portable=Convertir en cuenta portátil\nLa información de la cu
|
||||
account.password=Contraseña
|
||||
account.portable=Cuenta portátil
|
||||
account.skin=Aspecto
|
||||
account.skin.edit=Editar aspecto
|
||||
account.skin.file=Archivo de aspecto
|
||||
account.skin.model=Modelo
|
||||
account.skin.model.default=Clásico
|
||||
@@ -155,7 +143,7 @@ account.skin.type.little_skin=LittleSkin
|
||||
account.skin.type.little_skin.hint=Tienes que crear un jugador con el mismo nombre de jugador que tu cuenta sin conexión en el sitio web del proveedor de aspectos. Tu aspecto se ajustará ahora al aspecto asignado a tu jugador en el sitio web del proveedor de aspectos.
|
||||
account.skin.type.local_file=Archivo local de aspecto
|
||||
account.skin.type.steve=Steve
|
||||
account.skin.upload=Subir/editar aspecto
|
||||
account.skin.upload=Subir aspecto
|
||||
account.skin.upload.failed=No se pudo subir el aspecto.
|
||||
account.skin.invalid_skin=Archivo de aspecto inválido.
|
||||
account.username=Nombre de usuario
|
||||
@@ -382,6 +370,7 @@ curse.category.4552=Scripts
|
||||
datetime.format=d MMM yyyy, H\:mm\:ss
|
||||
|
||||
download=Descargar
|
||||
# download.china=
|
||||
download.code.404=Archivo no encontrado en el servidor remoto: %s
|
||||
download.content=Complementos
|
||||
download.shader=Sombreadores
|
||||
@@ -952,6 +941,9 @@ launcher.contact=Contacta con nosotros
|
||||
launcher.crash=HMCL ha encontrado un error fatal. Por favor, copie el siguiente registro y pida ayuda en nuestra comunidad en Discord, GitHub o Minecraft Forums.
|
||||
launcher.crash.java_internal_error=HMCL ha encontrado un error fatal porque su Java está dañado. Por favor, desinstala tu Java y descarga un Java adecuado <a href="https://bell-sw.com/pages/downloads/#downloads">aquí</a>.
|
||||
launcher.crash.hmcl_out_dated=HMCL ha encontrado un error fatal. Su launcher está desactualizado. Por favor, ¡actualícelo!
|
||||
# launcher.title=
|
||||
# launcher.title.modified=
|
||||
# launcher.title.unofficial=
|
||||
launcher.update_java=Por favor, actualice su versión de Java.
|
||||
|
||||
libraries.download=Descargando bibliotecas
|
||||
|
||||
@@ -21,33 +21,20 @@
|
||||
about=について
|
||||
about.copyright=著作権
|
||||
# about.copyright.statement=
|
||||
about.author.statement=bilibili @huanghongxun
|
||||
about.claim=EULA
|
||||
about.claim.statement=全文はこのリンクをクリック
|
||||
# about.environment=
|
||||
# about.environment.cpu=
|
||||
# about.environment.cpu.n=
|
||||
# about.environment.cpu.name=
|
||||
# about.environment.cpu.name.cores=
|
||||
# about.environment.cpu.name.cores.hyper_threading=
|
||||
# about.environment.gpu=
|
||||
# about.environment.gpu.n=
|
||||
# about.environment.memory=
|
||||
# about.environment.system=
|
||||
about.dependency=依存関係
|
||||
about.legal=法的承認
|
||||
about.thanks_to=感謝します…
|
||||
about.thanks_to.bangbang93.statement=BMCLAPIダウンロードミラープロバイダー。寄付をご検討ください!
|
||||
about.thanks_to.burningtnt.statement=HMCLに多くのテクニカルサポートを提供
|
||||
about.thanks_to.contributors=GitHubのすべての貢献者
|
||||
about.thanks_to.contributors.statement=HMCLをサポートしてくれたオープンソースコミュニティに感謝する
|
||||
about.thanks_to.gamerteam.statement=デフォルトの背景画像プロバイダー
|
||||
about.thanks_to.glavo.statement=HMCLのメンテナ
|
||||
about.thanks_to.zekerzhayard.statement=HMCLに多くのテクニカルサポートを提供
|
||||
about.thanks_to.zkitefly.statement=HMCLドキュメントメンテナ
|
||||
about.thanks_to.mcbbs=MCBBS (Minecraft中国語フォーラム)
|
||||
about.thanks_to.mcbbs.statement=中国本土ユーザー向けのmcbbs.netダウンロードミラープロバイダー(もう利用できません)
|
||||
# about.thanks_to.mcim=
|
||||
# about.thanks_to.mcim.statement=
|
||||
about.thanks_to.mcmod=MCMod (mcmod.cn)
|
||||
about.thanks_to.mcmod.statement=様々なMODの簡体字中国語名前翻訳とウィキを提供する
|
||||
about.thanks_to.red_lnn.statement=デフォルトの背景画像プロバイダー
|
||||
about.thanks_to.shulkersakura.statement=HMCLロゴプロバイダー
|
||||
about.thanks_to.users=HMCLユーザーグループのメンバー
|
||||
about.thanks_to.users.statement=寄付、バグレポートなどに感謝します
|
||||
about.thanks_to.yushijinhun.statement=authlib-injector関連のサポートを提供する
|
||||
# about.thanks_to.8mi-tech=
|
||||
# about.thanks_to.8mi-tech.statement=
|
||||
about.open_source=オープンソース
|
||||
about.open_source.statement=GPL v3 (https://github.com/HMCL-dev/HMCL)
|
||||
|
||||
@@ -132,6 +119,7 @@ account.missing.add=追加するにはここをクリック
|
||||
account.password=パスワード
|
||||
# account.portable=
|
||||
account.skin=スキン
|
||||
account.skin.edit=スキンを編集する
|
||||
account.skin.file=スキンイメージファイル
|
||||
account.skin.model=モデル
|
||||
account.skin.model.default=Classic
|
||||
@@ -370,6 +358,7 @@ curse.category.4552=スクリプト
|
||||
datetime.format=yyyy/MM/dd H:mm:ss
|
||||
|
||||
download=ダウンロード
|
||||
# download.china=
|
||||
download.code.404=リモートサーバーにファイルが見つかりません:%s
|
||||
download.content=ゲームコンテンツ
|
||||
download.shader=シェーダー
|
||||
@@ -762,6 +751,9 @@ launcher.contact=お問い合わせ
|
||||
launcher.crash=Hello Minecraft!ランチャーがクラッシュしました!次のコンテンツをコピーして、MCBBS、Baidu Tieba、GitHub、またはMinecraftForumを介してフィードバックを送信してください。
|
||||
# launcher.crash.java_internal_error=
|
||||
launcher.crash.hmcl_out_dated=Hello Minecraft!ランチャーがクラッシュしました!ランチャーが古くなっています。ランチャーを更新してください!
|
||||
# launcher.title=
|
||||
# launcher.title.modified=
|
||||
# launcher.title.unofficial=
|
||||
launcher.update_java=Javaを更新してください。
|
||||
|
||||
# libraries.download=
|
||||
|
||||
@@ -21,33 +21,20 @@
|
||||
about=有涉
|
||||
about.copyright=持權
|
||||
# about.copyright.statement=
|
||||
about.author.statement=嗶站 @huanghongxun
|
||||
about.claim=客契
|
||||
about.claim.statement=擊鏈以詳閱
|
||||
# about.environment=
|
||||
# about.environment.cpu=
|
||||
# about.environment.cpu.n=
|
||||
# about.environment.cpu.name=
|
||||
# about.environment.cpu.name.cores=
|
||||
# about.environment.cpu.name.cores.hyper_threading=
|
||||
# about.environment.gpu=
|
||||
# about.environment.gpu.n=
|
||||
# about.environment.memory=
|
||||
# about.environment.system=
|
||||
about.dependency=所依
|
||||
about.legal=告以律
|
||||
about.thanks_to=致謝
|
||||
about.thanks_to.bangbang93.statement=鑄 BMCLAPI 焉。望君資勉旃!
|
||||
about.thanks_to.burningtnt.statement=多助於技
|
||||
about.thanks_to.contributors=建議、撰案之諸君
|
||||
about.thanks_to.contributors.statement=苟无開源之社,無以至今日
|
||||
about.thanks_to.gamerteam.statement=供本繪圖
|
||||
about.thanks_to.glavo.statement=常理之
|
||||
about.thanks_to.zekerzhayard.statement=多助於技
|
||||
about.thanks_to.zkitefly.statement=理案文焉
|
||||
about.thanks_to.mcbbs=礦藝館肆
|
||||
about.thanks_to.mcbbs.statement=鑄礦藝館肆引源 (今廢)
|
||||
about.thanks_to.mcim=mcmod-info-mirror
|
||||
about.thanks_to.mcim.statement=另遞改囊之訊於本朝
|
||||
about.thanks_to.mcmod=礦藝改囊典
|
||||
about.thanks_to.mcmod.statement=載改囊之漢名,並書其案文焉
|
||||
about.thanks_to.red_lnn.statement=供本繪圖
|
||||
about.thanks_to.shulkersakura.statement=鑄其徽
|
||||
about.thanks_to.users=HMCL 群組諸員
|
||||
about.thanks_to.users.statement=敬謝諸君之貲勉、報謬、謀案
|
||||
about.thanks_to.yushijinhun.statement=有助於 authlib-injector
|
||||
about.thanks_to.8mi-tech=八藍米資訊科技 (泉州) 有限公所
|
||||
about.thanks_to.8mi-tech.statement=供遊戲部分靜態資源之加速下載役
|
||||
about.open_source=開源
|
||||
about.open_source.statement=GPL v3 (https://github.com/HMCL-dev/HMCL)
|
||||
|
||||
@@ -143,6 +130,7 @@ account.move_to_portable=更爲私戶簿\n戶簿諸文並是啟者而錄
|
||||
account.password=符節
|
||||
account.portable=私戶簿
|
||||
account.skin=外觀
|
||||
account.skin.edit=纂外觀
|
||||
account.skin.file=外觀圖案
|
||||
account.skin.model=形
|
||||
account.skin.model.default=寬
|
||||
@@ -155,7 +143,7 @@ account.skin.type.little_skin=LittleSkin 外觀站
|
||||
account.skin.type.little_skin.hint=君須立角之同名者於外觀站,爰傳外觀。離綫戶簿之外觀,其用外觀站之角也。\n君可求助於右上之鈕。
|
||||
account.skin.type.local_file=案頭之外觀圖案
|
||||
account.skin.type.steve=史迪武
|
||||
account.skin.upload=匯/纂外觀
|
||||
account.skin.upload=匯外觀
|
||||
account.skin.upload.failed=匯之外觀而未成
|
||||
account.skin.invalid_skin=外觀之案有謬
|
||||
account.username=戶名
|
||||
@@ -381,6 +369,7 @@ curse.category.4552=角本
|
||||
datetime.format=yyyy 年 MM 月 dd 日 HH:mm:ss
|
||||
|
||||
download=引
|
||||
# download.china=
|
||||
download.code.404=伺服器無案曰:%s\n君可求助於右上之鈕。
|
||||
download.content=戲案
|
||||
download.shader=光影
|
||||
@@ -774,6 +763,9 @@ launcher.contact=伏惟候告
|
||||
launcher.crash=HMCL 有謬而弗能正。宜鈔下文而報謬于右下之鈕。
|
||||
launcher.crash.java_internal_error=HMCL 不能行,以爪哇壞也。宜去是爪哇,而擊<a href="https://bell-sw.com/pages/downloads/#downloads">此</a>以置爪哇之適者。
|
||||
launcher.crash.hmcl_out_dated=HMCL 有謬而弗能正之。余識君之啟者舊矣,宜新之。
|
||||
launcher.title=Hello Minecraft! Launcher v%s
|
||||
launcher.title.modified=Hello Minecraft! Launcher %s (已改)
|
||||
launcher.title.unofficial=Hello Minecraft! Launcher %s (非官版)
|
||||
launcher.update_java=宜迭更爪哇。\n君可求助於 https://docs.hmcl.net/help.html。
|
||||
|
||||
libraries.download=引之所依
|
||||
|
||||
@@ -21,33 +21,20 @@
|
||||
about=О лаунчере
|
||||
about.copyright=Авторские права
|
||||
about.copyright.statement=Copyright © 2013-2026 huangyuhui и контрибьюторы.
|
||||
about.author.statement=bilibili @huanghongxun
|
||||
about.claim=EULA
|
||||
about.claim.statement=Кликните на ссылку, чтобы увидеть весь текст.
|
||||
# about.environment=
|
||||
# about.environment.cpu=
|
||||
# about.environment.cpu.n=
|
||||
# about.environment.cpu.name=
|
||||
# about.environment.cpu.name.cores=
|
||||
# about.environment.cpu.name.cores.hyper_threading=
|
||||
# about.environment.gpu=
|
||||
# about.environment.gpu.n=
|
||||
# about.environment.memory=
|
||||
# about.environment.system=
|
||||
about.dependency=Сторонние библиотеки
|
||||
about.legal=Правовая информация
|
||||
about.thanks_to=Отдельная благодарность
|
||||
about.thanks_to.bangbang93.statement=За предоставление зеркала загрузки BMCLAPI. Пожалуйста, подумайте о пожертвовании!
|
||||
about.thanks_to.burningtnt.statement=Оказал большую техническую поддержку HMCL.
|
||||
about.thanks_to.contributors=Всем участникам на GitHub
|
||||
about.thanks_to.contributors.statement=Без потрясающего сообщества с открытым исходным кодом HMCL не добился бы таких успехов.
|
||||
about.thanks_to.gamerteam.statement=За предоставление фонового изображения.
|
||||
about.thanks_to.glavo.statement=Отвечает за поддержание HMCL.
|
||||
about.thanks_to.zekerzhayard.statement=Оказал большую техническую поддержку HMCL.
|
||||
about.thanks_to.zkitefly.statement=Отвечает за поддержание документации HMCL.
|
||||
about.thanks_to.mcbbs=MCBBS (Китайский форум Minecraft)
|
||||
about.thanks_to.mcbbs.statement=За предоставление зеркала загрузки mcbbs.net для пользователей материкового Китая. (Больше недоступно)
|
||||
about.thanks_to.mcim=mcmod-info-mirror
|
||||
about.thanks_to.mcim.statement=За предоставление услуги ускорения кэширования информации о модах для пользователей из материкового Китая.
|
||||
about.thanks_to.mcmod=MCMod (mcmod.cn)
|
||||
about.thanks_to.mcmod.statement=За предоставление упрощенного китайского перевода и вики для различных модов.
|
||||
about.thanks_to.red_lnn.statement=За предоставление фонового изображения.
|
||||
about.thanks_to.shulkersakura.statement=За предоставление логотипа для HMCL.
|
||||
about.thanks_to.users=Членам группы пользователей HMCL
|
||||
about.thanks_to.users.statement=Спасибо за пожертвования, отчёты об ошибках и так далее.
|
||||
about.thanks_to.yushijinhun.statement=За предоставление поддержки, связанной с authlib-injector.
|
||||
about.thanks_to.8mi-tech=8Mi-Tech (Quanzhou) Co., Ltd.
|
||||
about.thanks_to.8mi-tech.statement=Обеспечивает ускоренную загрузку некоторых статических ресурсов игры.
|
||||
about.open_source=Открытый исходный код
|
||||
about.open_source.statement=GPL v3 (https://github.com/HMCL-dev/HMCL)
|
||||
|
||||
@@ -139,6 +126,7 @@ account.move_to_portable=Конвертировать в портативный
|
||||
account.password=Пароль
|
||||
account.portable=Портативный
|
||||
account.skin=Скин
|
||||
account.skin.edit=Изменить скин
|
||||
account.skin.file=Файл скина
|
||||
account.skin.model=Модель
|
||||
account.skin.model.default=Классическая
|
||||
@@ -151,7 +139,7 @@ account.skin.type.little_skin=LittleSkin
|
||||
account.skin.type.little_skin.hint=Вам нужно создать игрока с тем же именем, что и у вашего оффлайн-аккаунта на сайте поставщика скинов. Теперь ваш скин будет установлен на скин, назначенный вашему игроку на сайте поставщика скинов.
|
||||
account.skin.type.local_file=Локальный файл скина
|
||||
account.skin.type.steve=Стив
|
||||
account.skin.upload=Загрузить/изменить скин
|
||||
account.skin.upload=Загрузить скин
|
||||
account.skin.upload.failed=Не удалось загрузить скин.
|
||||
account.skin.invalid_skin=Недопустимый файл скина.
|
||||
account.username=Имя пользователя
|
||||
@@ -378,6 +366,7 @@ curse.category.6555=Ванильный
|
||||
datetime.format=d MMM yyyy г., HH\:mm\:ss
|
||||
|
||||
download=Скачать
|
||||
# download.china=
|
||||
download.code.404=Файл «%s» не найден на удаленном сервере.
|
||||
download.content=Аддоны
|
||||
download.shader=Шейдеры
|
||||
@@ -891,6 +880,9 @@ launcher.contact=Связаться с нами
|
||||
launcher.crash=Лаунчер столкнулся с фатальной ошибкой! Скопируйте следующий лог и попросите помощи в нашем Discord, группе QQ, GitHub или на другом форуме Minecraft.
|
||||
launcher.crash.java_internal_error=HMCL столкнулся с критической ошибкой, потому что ваша Java повреждена. Удалите Java и скачайте подходящую <a href="https://bell-sw.com/pages/downloads/#downloads">здесь</a>.
|
||||
launcher.crash.hmcl_out_dated=Лаунчер столкнулся с фатальной ошибкой! Ваш лаунчер устарел. Обновите его!
|
||||
# launcher.title=
|
||||
# launcher.title.modified=
|
||||
# launcher.title.unofficial=
|
||||
launcher.update_java=Пожалуйста, обновите версию Java.
|
||||
|
||||
libraries.download=Скачивание библиотек
|
||||
|
||||
@@ -22,33 +22,20 @@
|
||||
about=Про програму
|
||||
about.copyright=Авторське право
|
||||
about.copyright.statement=Copyright © 2013-2026 huangyuhui та контриб'ютори.
|
||||
about.author.statement=bilibili @huanghongxun
|
||||
about.claim=Ліцензійна угода з кінцевим користувачем (EULA)
|
||||
about.claim.statement=Натисніть це посилання для перегляду повного тексту.
|
||||
# about.environment=
|
||||
# about.environment.cpu=
|
||||
# about.environment.cpu.n=
|
||||
# about.environment.cpu.name=
|
||||
# about.environment.cpu.name.cores=
|
||||
# about.environment.cpu.name.cores.hyper_threading=
|
||||
# about.environment.gpu=
|
||||
# about.environment.gpu.n=
|
||||
# about.environment.memory=
|
||||
# about.environment.system=
|
||||
about.dependency=Сторонні бібліотеки
|
||||
about.legal=Правове визнання
|
||||
about.thanks_to=Подяки
|
||||
about.thanks_to.bangbang93.statement=За надання дзеркала завантаження BMCLAPI. Будь ласка, подумайте про пожертвування!
|
||||
about.thanks_to.burningtnt.statement=Надання великої технічної підтримки HMCL.
|
||||
about.thanks_to.contributors=Усі учасники на GitHub
|
||||
about.thanks_to.contributors.statement=Без чудового спільнотного руху з відкритим кодом, HMCL не дійшла б так далеко.
|
||||
about.thanks_to.gamerteam.statement=За надання типового фонового зображення.
|
||||
about.thanks_to.glavo.statement=Відповідальний за підтримку HMCL.
|
||||
about.thanks_to.zekerzhayard.statement=Надання великої технічної підтримки HMCL.
|
||||
about.thanks_to.zkitefly.statement=Відповідальний за підтримку документації HMCL.
|
||||
about.thanks_to.mcbbs=MCBBS (Китайський форум Minecraft)
|
||||
about.thanks_to.mcbbs.statement=За надання дзеркала завантаження mcbbs.net для користувачів материкового Китаю. (Більше не доступне)
|
||||
about.thanks_to.mcim=mcmod-info-mirror
|
||||
about.thanks_to.mcim.statement=За надання служби прискорення кешу інформації модів для користувачів материкового Китаю.
|
||||
about.thanks_to.mcmod=MCMod (mcmod.cn)
|
||||
about.thanks_to.mcmod.statement=За надання спрощених китайських перекладів та вікі для різних модів.
|
||||
about.thanks_to.red_lnn.statement=За надання типового фонового зображення.
|
||||
about.thanks_to.shulkersakura.statement=За надання логотипу для HMCL.
|
||||
about.thanks_to.users=Учасники групи користувачів HMCL
|
||||
about.thanks_to.users.statement=Дякуємо за пожертви, повідомлення про помилки тощо.
|
||||
about.thanks_to.yushijinhun.statement=За надання підтримки, пов'язаної з authlib-injector.
|
||||
about.thanks_to.8mi-tech=8Mi-Tech (Quanzhou) Co., Ltd.
|
||||
about.thanks_to.8mi-tech.statement=Надає сервіс прискореного завантаження для деяких статичних ресурсів гри.
|
||||
about.open_source=Відкрите джерело
|
||||
about.open_source.statement=GPL v3 (https://github.com/HMCL-dev/HMCL)
|
||||
|
||||
@@ -145,6 +132,7 @@ account.move_to_portable=Перетворити на портативний об
|
||||
account.password=Пароль
|
||||
account.portable=Портативний
|
||||
account.skin=Скін
|
||||
account.skin.edit=Редагувати скін
|
||||
account.skin.file=Файл скіна
|
||||
account.skin.model=Модель
|
||||
account.skin.model.default=Класична
|
||||
@@ -157,7 +145,7 @@ account.skin.type.little_skin=LittleSkin
|
||||
account.skin.type.little_skin.hint=Вам потрібно створити гравця з тим же ім'ям гравця, що й у вашого автономного облікового запису, на сайті постачальника скінів. Ваш скін тепер буде встановлено на скін, призначений вашому гравцю на сайті постачальника скінів.
|
||||
account.skin.type.local_file=Локальний файл скіна
|
||||
account.skin.type.steve=Стів
|
||||
account.skin.upload=Завантажити/Редагувати скін
|
||||
account.skin.upload=Завантажити скін
|
||||
account.skin.upload.failed=Не вдалося завантажити скін.
|
||||
account.skin.invalid_skin=Недійсний файл скіна.
|
||||
account.username=Ім'я користувача
|
||||
@@ -378,6 +366,7 @@ curse.category.6555=Ваніла
|
||||
datetime.format=dd.MM.yyyy, HH:mm:ss
|
||||
|
||||
download=Завантажити
|
||||
# download.china=
|
||||
download.code.404=Файл "%s" не знайдено на віддаленому сервері.
|
||||
download.content=Додатки
|
||||
download.shader=Шейдери
|
||||
@@ -914,6 +903,9 @@ launcher.contact=Зв'яжіться з нами
|
||||
launcher.crash=Hello Minecraft! Лаунчер зіткнувся з фатальною помилкою! Скопіюйте наступний журнал та попросіть допомоги на нашому Discord, групі QQ, GitHub або іншому форумі Minecraft.
|
||||
launcher.crash.java_internal_error=Hello Minecraft! Лаунчер зіткнувся з фатальною помилкою, оскільки ваша Java пошкоджена. Видаліть вашу Java та завантажте відповідну Java <a href="https://bell-sw.com/pages/downloads/#downloads">тут</a>.
|
||||
launcher.crash.hmcl_out_dated=Hello Minecraft! Лаунчер зіткнувся з фатальною помилкою! Ваш лаунчер застарів. Оновіть ваш лаунчер!
|
||||
# launcher.title=
|
||||
# launcher.title.modified=
|
||||
# launcher.title.unofficial=
|
||||
launcher.update_java=Оновіть вашу версію Java.
|
||||
|
||||
libraries.download=Завантаження бібліотек
|
||||
|
||||
@@ -21,33 +21,20 @@
|
||||
about=关于
|
||||
about.copyright=版权
|
||||
about.copyright.statement=版权所有 © 2013-2026 huangyuhui 及贡献者
|
||||
about.author.statement=bilibili @huanghongxun
|
||||
about.claim=用户协议
|
||||
about.claim.statement=点击链接以查看全文
|
||||
about.environment=环境
|
||||
about.environment.cpu=处理器
|
||||
about.environment.cpu.n=处理器 %d
|
||||
about.environment.cpu.name=%s
|
||||
about.environment.cpu.name.cores=%s (%d 核心)
|
||||
about.environment.cpu.name.cores.hyper_threading=%s (%d 核心 / %d 线程)
|
||||
about.environment.gpu=显卡
|
||||
about.environment.gpu.n=显卡 %d
|
||||
about.environment.memory=内存
|
||||
about.environment.system=操作系统
|
||||
about.dependency=依赖
|
||||
about.legal=法律声明
|
||||
about.thanks_to=鸣谢
|
||||
about.thanks_to.bangbang93.statement=提供 BMCLAPI 下载源。请赞助支持 BMCLAPI!
|
||||
about.thanks_to.burningtnt.statement=为 HMCL 贡献许多技术支持
|
||||
about.thanks_to.contributors=所有通过 Issue、Pull Request 等方式参与本项目的贡献者
|
||||
about.thanks_to.contributors.statement=没有开源社区的支持,HMCL 就无法走到今天
|
||||
about.thanks_to.gamerteam.statement=提供默认背景图
|
||||
about.thanks_to.glavo.statement=负责 HMCL 的日常维护
|
||||
about.thanks_to.zekerzhayard.statement=为 HMCL 贡献许多技术支持
|
||||
about.thanks_to.zkitefly.statement=负责维护 HMCL 的文档
|
||||
about.thanks_to.mcbbs=MCBBS (我的世界中文论坛)
|
||||
about.thanks_to.mcbbs.statement=提供 MCBBS 下载源 (现已停止服务)
|
||||
about.thanks_to.mcim=mcmod-info-mirror
|
||||
about.thanks_to.mcim.statement=为中国大陆用户提供模组信息缓存加速服务
|
||||
about.thanks_to.mcmod=MC 百科 (mcmod.cn)
|
||||
about.thanks_to.mcmod.statement=提供模组简体中文名映射表与模组百科
|
||||
about.thanks_to.red_lnn.statement=提供默认背景图
|
||||
about.thanks_to.shulkersakura.statement=提供 HMCL 的徽标
|
||||
about.thanks_to.users=HMCL 用户群成员
|
||||
about.thanks_to.users.statement=感谢用户群成员赞助充电、积极催更、反馈问题、出谋划策
|
||||
about.thanks_to.yushijinhun.statement=提供 authlib-injector 相关支持
|
||||
about.thanks_to.8mi-tech=八蓝米信息科技 (泉州) 有限公司
|
||||
about.thanks_to.8mi-tech.statement=提供了游戏部分静态资源的加速下载服务
|
||||
about.open_source=开源
|
||||
about.open_source.statement=GPL v3 (https://github.com/HMCL-dev/HMCL)
|
||||
|
||||
@@ -144,6 +131,7 @@ account.move_to_portable=转换为便携账户\n该账户的信息会保存至
|
||||
account.password=密码
|
||||
account.portable=便携账户
|
||||
account.skin=皮肤
|
||||
account.skin.edit=编辑皮肤
|
||||
account.skin.file=皮肤图片文件
|
||||
account.skin.model=模型
|
||||
account.skin.model.default=宽型
|
||||
@@ -156,7 +144,7 @@ account.skin.type.little_skin=LittleSkin 皮肤站
|
||||
account.skin.type.little_skin.hint=你需要在皮肤站中创建并使用和该离线账户同名的角色。此时离线账户皮肤将显示为皮肤站上对应角色所设置的皮肤。\n你可以点击右上角帮助按钮进行求助。
|
||||
account.skin.type.local_file=本地皮肤图片文件
|
||||
account.skin.type.steve=Steve
|
||||
account.skin.upload=上传/编辑皮肤
|
||||
account.skin.upload=上传皮肤
|
||||
account.skin.upload.failed=皮肤上传失败
|
||||
account.skin.invalid_skin=无法识别的皮肤文件
|
||||
account.username=用户名
|
||||
@@ -382,6 +370,7 @@ curse.category.6555=原生
|
||||
datetime.format=yyyy 年 MM 月 dd 日 HH:mm:ss
|
||||
|
||||
download=下载
|
||||
download.china=根据相关法律法规,HMCL 在中国大陆地区暂不支持下载游戏相关内容。请前往<a href="https://mc.163.com/">我的世界官方网站</a>下载我的世界中国版。
|
||||
download.code.404=远程服务器不包含需要下载的文件: %s\n你可以点击右上角帮助按钮进行求助。
|
||||
download.content=游戏内容
|
||||
download.shader=光影
|
||||
@@ -777,6 +766,9 @@ launcher.contact=联系我们
|
||||
launcher.crash=HMCL 遇到了无法处理的错误。请复制下列内容并点击右下角的按钮反馈问题。
|
||||
launcher.crash.java_internal_error=HMCL 由于当前 Java 损坏而无法继续运行。请卸载当前 Java,点击 <a href="https://bell-sw.com/pages/downloads/#downloads">此处</a> 安装合适的 Java 版本。
|
||||
launcher.crash.hmcl_out_dated=HMCL 遇到了无法处理的错误。已检测到你的启动器不是最新版本,请更新后再试。
|
||||
launcher.title=Hello Minecraft! Launcher %s
|
||||
launcher.title.modified=Hello Minecraft! Launcher %s (已修改)
|
||||
launcher.title.unofficial=Hello Minecraft! Launcher %s (非官方构建)
|
||||
launcher.update_java=请更新你的 Java。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。
|
||||
|
||||
libraries.download=下载依赖库
|
||||
|
||||
@@ -21,33 +21,20 @@
|
||||
about=關於
|
||||
about.copyright=著作權
|
||||
about.copyright.statement=著作權所有 © 2013-2026 huangyuhui 與貢獻者
|
||||
about.author.statement=bilibili @huanghongxun
|
||||
about.claim=使用者協議
|
||||
about.claim.statement=點擊連結以查看全文
|
||||
about.environment=環境
|
||||
about.environment.cpu=處理器
|
||||
about.environment.cpu.n=處理器 %d
|
||||
about.environment.cpu.name=%s
|
||||
about.environment.cpu.name.cores=%s (%d 核心)
|
||||
about.environment.cpu.name.cores.hyper_threading=%s (%d 核心 / %d 執行緒)
|
||||
about.environment.gpu=顯示卡
|
||||
about.environment.gpu.n=顯示卡 %d
|
||||
about.environment.memory=記憶體
|
||||
about.environment.system=作業系統
|
||||
about.dependency=相依元件
|
||||
about.legal=法律宣告
|
||||
about.thanks_to=鳴謝
|
||||
about.thanks_to.bangbang93.statement=提供 BMCLAPI 下載來源。請贊助支援 BMCLAPI!
|
||||
about.thanks_to.burningtnt.statement=為 HMCL 貢獻許多技術支援
|
||||
about.thanks_to.contributors=所有透過 Issue、Pull Request 等管道參與本專案的貢獻者
|
||||
about.thanks_to.contributors.statement=沒有開源社群的支援,HMCL 無法走到今天
|
||||
about.thanks_to.gamerteam.statement=提供預設背景圖
|
||||
about.thanks_to.glavo.statement=負責 HMCL 的日常維護
|
||||
about.thanks_to.zekerzhayard.statement=為 HMCL 貢獻許多技術支援
|
||||
about.thanks_to.zkitefly.statement=負責維護 HMCL 的文件
|
||||
about.thanks_to.mcbbs=MCBBS (我的世界中文論壇)
|
||||
about.thanks_to.mcbbs.statement=提供 MCBBS 下載源 (現已停止服務)
|
||||
about.thanks_to.mcim=mcmod-info-mirror
|
||||
about.thanks_to.mcim.statement=為中國大陸使用者提供模組資訊快取加速服務
|
||||
about.thanks_to.mcmod=MC 百科 (mcmod.cn)
|
||||
about.thanks_to.mcmod.statement=提供模組簡體中文名映射表與模組百科
|
||||
about.thanks_to.red_lnn.statement=提供預設背景圖
|
||||
about.thanks_to.shulkersakura.statement=提供 HMCL 的標誌
|
||||
about.thanks_to.users=HMCL 使用者群組成員
|
||||
about.thanks_to.users.statement=感謝使用者群組成員贊助充電、積極催更、回報問題、出謀劃策
|
||||
about.thanks_to.yushijinhun.statement=提供 authlib-injector 相關支援
|
||||
about.thanks_to.8mi-tech=八藍米信息科技 (泉州) 有限公司
|
||||
about.thanks_to.8mi-tech.statement=提供了遊戲部分靜態資源的加速下載服務
|
||||
about.open_source=開放原始碼
|
||||
about.open_source.statement=GPL v3 (https://github.com/HMCL-dev/HMCL/)
|
||||
|
||||
@@ -142,6 +129,7 @@ account.move_to_portable=轉換為可攜式帳戶\n該帳戶的資訊會儲存
|
||||
account.password=密碼
|
||||
account.portable=可攜式帳戶
|
||||
account.skin=外觀
|
||||
account.skin.edit=編輯外觀
|
||||
account.skin.file=外觀圖片檔案
|
||||
account.skin.model=模型
|
||||
account.skin.model.default=寬型
|
||||
@@ -154,7 +142,7 @@ account.skin.type.little_skin=LittleSkin 皮膚站
|
||||
account.skin.type.little_skin.hint=你需要在皮膚站中新增並使用和該離線帳戶同名角色。此時離線帳戶外觀將為皮膚站上對應角色所設定的外觀。
|
||||
account.skin.type.local_file=本機外觀圖片檔案
|
||||
account.skin.type.steve=Steve
|
||||
account.skin.upload=上傳/編輯外觀
|
||||
account.skin.upload=上傳外觀
|
||||
account.skin.upload.failed=外觀上傳失敗
|
||||
account.skin.invalid_skin=無法識別的外觀檔案
|
||||
account.username=使用者名稱
|
||||
@@ -380,6 +368,7 @@ curse.category.6555=原生
|
||||
datetime.format=yyyy 年 MM 月 dd 日 HH:mm:ss
|
||||
|
||||
download=下載
|
||||
download.china=根據相關法律法規,HMCL 在中國大陸地區暫不支援下載遊戲相關內容。請前往<a href="https://mc.163.com/">我的世界官方網站</a>下載我的世界中國版。
|
||||
download.code.404=遠端伺服器沒有需要下載的檔案:%s
|
||||
download.content=遊戲內容
|
||||
download.shader=光影
|
||||
@@ -771,6 +760,9 @@ launcher.contact=聯絡我們
|
||||
launcher.crash=HMCL 遇到了無法處理的錯誤。請複製下列內容並透過 GitHub、Discord 或 HMCL QQ 群回報問題。
|
||||
launcher.crash.java_internal_error=HMCL 由於目前 Java 損壞而無法繼續執行。請移除目前 Java,點擊 <a href="https://bell-sw.com/pages/downloads/#downloads">此處</a> 安裝合適的 Java 版本。
|
||||
launcher.crash.hmcl_out_dated=HMCL 遇到了無法處理的錯誤。已偵測到你的啟動器不是最新版本,請更新後重試!
|
||||
launcher.title=Hello Minecraft! Launcher %s
|
||||
launcher.title.modified=Hello Minecraft! Launcher %s (已修改)
|
||||
launcher.title.unofficial=Hello Minecraft! Launcher %s (非官方建置)
|
||||
launcher.update_java=請更新你的 Java
|
||||
|
||||
libraries.download=下載依賴庫
|
||||
|
||||
@@ -0,0 +1,549 @@
|
||||
# IANA tzdb version: 2026e
|
||||
# Generated from https://data.iana.org/time-zones/tzdb-2026e/zone1970.tab (public domain).
|
||||
# Compatibility links from https://data.iana.org/time-zones/tzdb-2026e/backward (public domain).
|
||||
# Update with ./gradlew updateTimeZoneRegions --tzdb-version 2026e.
|
||||
Africa/Abidjan,CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG
|
||||
Africa/Accra,CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG
|
||||
Africa/Addis_Ababa,KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT
|
||||
Africa/Algiers,DZ
|
||||
Africa/Asmara,KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT
|
||||
Africa/Asmera,KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT
|
||||
Africa/Bamako,CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG
|
||||
Africa/Bangui,NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE
|
||||
Africa/Banjul,CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG
|
||||
Africa/Bissau,GW
|
||||
Africa/Blantyre,MZ,BI,BW,CD,MW,RW,ZM,ZW
|
||||
Africa/Brazzaville,NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE
|
||||
Africa/Bujumbura,MZ,BI,BW,CD,MW,RW,ZM,ZW
|
||||
Africa/Cairo,EG
|
||||
Africa/Casablanca,MA
|
||||
Africa/Ceuta,ES
|
||||
Africa/Conakry,CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG
|
||||
Africa/Dakar,CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG
|
||||
Africa/Dar_es_Salaam,KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT
|
||||
Africa/Djibouti,KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT
|
||||
Africa/Douala,NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE
|
||||
Africa/El_Aaiun,EH
|
||||
Africa/Freetown,CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG
|
||||
Africa/Gaborone,MZ,BI,BW,CD,MW,RW,ZM,ZW
|
||||
Africa/Harare,MZ,BI,BW,CD,MW,RW,ZM,ZW
|
||||
Africa/Johannesburg,ZA,LS,SZ
|
||||
Africa/Juba,SS
|
||||
Africa/Kampala,KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT
|
||||
Africa/Khartoum,SD
|
||||
Africa/Kigali,MZ,BI,BW,CD,MW,RW,ZM,ZW
|
||||
Africa/Kinshasa,NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE
|
||||
Africa/Lagos,NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE
|
||||
Africa/Libreville,NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE
|
||||
Africa/Lome,CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG
|
||||
Africa/Luanda,NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE
|
||||
Africa/Lubumbashi,MZ,BI,BW,CD,MW,RW,ZM,ZW
|
||||
Africa/Lusaka,MZ,BI,BW,CD,MW,RW,ZM,ZW
|
||||
Africa/Malabo,NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE
|
||||
Africa/Maputo,MZ,BI,BW,CD,MW,RW,ZM,ZW
|
||||
Africa/Maseru,ZA,LS,SZ
|
||||
Africa/Mbabane,ZA,LS,SZ
|
||||
Africa/Mogadishu,KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT
|
||||
Africa/Monrovia,LR
|
||||
Africa/Nairobi,KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT
|
||||
Africa/Ndjamena,TD
|
||||
Africa/Niamey,NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE
|
||||
Africa/Nouakchott,CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG
|
||||
Africa/Ouagadougou,CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG
|
||||
Africa/Porto-Novo,NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE
|
||||
Africa/Sao_Tome,ST
|
||||
Africa/Timbuktu,CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG
|
||||
Africa/Tripoli,LY
|
||||
Africa/Tunis,TN
|
||||
Africa/Windhoek,NA
|
||||
America/Adak,US
|
||||
America/Anchorage,US
|
||||
America/Anguilla,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Antigua,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Araguaina,BR
|
||||
America/Argentina/Buenos_Aires,AR
|
||||
America/Argentina/Catamarca,AR
|
||||
America/Argentina/ComodRivadavia,AR
|
||||
America/Argentina/Cordoba,AR
|
||||
America/Argentina/Jujuy,AR
|
||||
America/Argentina/La_Rioja,AR
|
||||
America/Argentina/Mendoza,AR
|
||||
America/Argentina/Rio_Gallegos,AR
|
||||
America/Argentina/Salta,AR
|
||||
America/Argentina/San_Juan,AR
|
||||
America/Argentina/San_Luis,AR
|
||||
America/Argentina/Tucuman,AR
|
||||
America/Argentina/Ushuaia,AR
|
||||
America/Aruba,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Asuncion,PY
|
||||
America/Atikokan,PA,CA,KY
|
||||
America/Atka,US
|
||||
America/Bahia,BR
|
||||
America/Bahia_Banderas,MX
|
||||
America/Barbados,BB
|
||||
America/Belem,BR
|
||||
America/Belize,BZ
|
||||
America/Blanc-Sablon,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Boa_Vista,BR
|
||||
America/Bogota,CO
|
||||
America/Boise,US
|
||||
America/Buenos_Aires,AR
|
||||
America/Cambridge_Bay,CA
|
||||
America/Campo_Grande,BR
|
||||
America/Cancun,MX
|
||||
America/Caracas,VE
|
||||
America/Catamarca,AR
|
||||
America/Cayenne,GF
|
||||
America/Cayman,PA,CA,KY
|
||||
America/Chicago,US
|
||||
America/Chihuahua,MX
|
||||
America/Ciudad_Juarez,MX
|
||||
America/Coral_Harbour,PA,CA,KY
|
||||
America/Cordoba,AR
|
||||
America/Costa_Rica,CR
|
||||
America/Coyhaique,CL
|
||||
America/Creston,US,CA
|
||||
America/Cuiaba,BR
|
||||
America/Curacao,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Danmarkshavn,GL
|
||||
America/Dawson,CA
|
||||
America/Dawson_Creek,CA
|
||||
America/Denver,US
|
||||
America/Detroit,US
|
||||
America/Dominica,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Edmonton,CA
|
||||
America/Eirunepe,BR
|
||||
America/El_Salvador,SV
|
||||
America/Ensenada,MX
|
||||
America/Fort_Nelson,CA
|
||||
America/Fort_Wayne,US
|
||||
America/Fortaleza,BR
|
||||
America/Glace_Bay,CA
|
||||
America/Godthab,GL
|
||||
America/Goose_Bay,CA
|
||||
America/Grand_Turk,TC
|
||||
America/Grenada,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Guadeloupe,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Guatemala,GT
|
||||
America/Guayaquil,EC
|
||||
America/Guyana,GY
|
||||
America/Halifax,CA
|
||||
America/Havana,CU
|
||||
America/Hermosillo,MX
|
||||
America/Indiana/Indianapolis,US
|
||||
America/Indiana/Knox,US
|
||||
America/Indiana/Marengo,US
|
||||
America/Indiana/Petersburg,US
|
||||
America/Indiana/Tell_City,US
|
||||
America/Indiana/Vevay,US
|
||||
America/Indiana/Vincennes,US
|
||||
America/Indiana/Winamac,US
|
||||
America/Indianapolis,US
|
||||
America/Inuvik,CA
|
||||
America/Iqaluit,CA
|
||||
America/Jamaica,JM
|
||||
America/Jujuy,AR
|
||||
America/Juneau,US
|
||||
America/Kentucky/Louisville,US
|
||||
America/Kentucky/Monticello,US
|
||||
America/Knox_IN,US
|
||||
America/Kralendijk,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/La_Paz,BO
|
||||
America/Lima,PE
|
||||
America/Los_Angeles,US
|
||||
America/Louisville,US
|
||||
America/Lower_Princes,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Maceio,BR
|
||||
America/Managua,NI
|
||||
America/Manaus,BR
|
||||
America/Marigot,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Martinique,MQ
|
||||
America/Matamoros,MX
|
||||
America/Mazatlan,MX
|
||||
America/Mendoza,AR
|
||||
America/Menominee,US
|
||||
America/Merida,MX
|
||||
America/Metlakatla,US
|
||||
America/Mexico_City,MX
|
||||
America/Miquelon,PM
|
||||
America/Moncton,CA
|
||||
America/Monterrey,MX
|
||||
America/Montevideo,UY
|
||||
America/Montreal,CA,BS
|
||||
America/Montserrat,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Nassau,CA,BS
|
||||
America/New_York,US
|
||||
America/Nipigon,CA,BS
|
||||
America/Nome,US
|
||||
America/Noronha,BR
|
||||
America/North_Dakota/Beulah,US
|
||||
America/North_Dakota/Center,US
|
||||
America/North_Dakota/New_Salem,US
|
||||
America/Nuuk,GL
|
||||
America/Ojinaga,MX
|
||||
America/Panama,PA,CA,KY
|
||||
America/Pangnirtung,CA
|
||||
America/Paramaribo,SR
|
||||
America/Phoenix,US,CA
|
||||
America/Port-au-Prince,HT
|
||||
America/Port_of_Spain,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Porto_Acre,BR
|
||||
America/Porto_Velho,BR
|
||||
America/Puerto_Rico,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Punta_Arenas,CL
|
||||
America/Rainy_River,CA
|
||||
America/Rankin_Inlet,CA
|
||||
America/Recife,BR
|
||||
America/Regina,CA
|
||||
America/Resolute,CA
|
||||
America/Rio_Branco,BR
|
||||
America/Rosario,AR
|
||||
America/Santa_Isabel,MX
|
||||
America/Santarem,BR
|
||||
America/Santiago,CL
|
||||
America/Santo_Domingo,DO
|
||||
America/Sao_Paulo,BR
|
||||
America/Scoresbysund,GL
|
||||
America/Shiprock,US
|
||||
America/Sitka,US
|
||||
America/St_Barthelemy,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/St_Johns,CA
|
||||
America/St_Kitts,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/St_Lucia,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/St_Thomas,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/St_Vincent,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Swift_Current,CA
|
||||
America/Tegucigalpa,HN
|
||||
America/Thule,GL
|
||||
America/Thunder_Bay,CA,BS
|
||||
America/Tijuana,MX
|
||||
America/Toronto,CA,BS
|
||||
America/Tortola,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Vancouver,CA
|
||||
America/Virgin,PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI
|
||||
America/Whitehorse,CA
|
||||
America/Winnipeg,CA
|
||||
America/Yakutat,US
|
||||
America/Yellowknife,CA
|
||||
Antarctica/Casey,AQ
|
||||
Antarctica/Davis,AQ
|
||||
Antarctica/DumontDUrville,PG,AQ,FM
|
||||
Antarctica/Macquarie,AU
|
||||
Antarctica/Mawson,AQ
|
||||
Antarctica/McMurdo,NZ,AQ
|
||||
Antarctica/Palmer,AQ
|
||||
Antarctica/Rothera,AQ
|
||||
Antarctica/South_Pole,NZ,AQ
|
||||
Antarctica/Syowa,SA,AQ,KW,YE
|
||||
Antarctica/Troll,AQ
|
||||
Antarctica/Vostok,AQ
|
||||
Arctic/Longyearbyen,DE,DK,NO,SE,SJ
|
||||
Asia/Aden,SA,AQ,KW,YE
|
||||
Asia/Almaty,KZ
|
||||
Asia/Amman,JO
|
||||
Asia/Anadyr,RU
|
||||
Asia/Aqtau,KZ
|
||||
Asia/Aqtobe,KZ
|
||||
Asia/Ashgabat,TM
|
||||
Asia/Ashkhabad,TM
|
||||
Asia/Atyrau,KZ
|
||||
Asia/Baghdad,IQ
|
||||
Asia/Bahrain,QA,BH
|
||||
Asia/Baku,AZ
|
||||
Asia/Bangkok,TH,CX,KH,LA,VN
|
||||
Asia/Barnaul,RU
|
||||
Asia/Beirut,LB
|
||||
Asia/Bishkek,KG
|
||||
Asia/Brunei,MY,BN
|
||||
Asia/Calcutta,IN
|
||||
Asia/Chita,RU
|
||||
Asia/Choibalsan,MN
|
||||
Asia/Chongqing,CN
|
||||
Asia/Chungking,CN
|
||||
Asia/Colombo,LK
|
||||
Asia/Dacca,BD
|
||||
Asia/Damascus,SY
|
||||
Asia/Dhaka,BD
|
||||
Asia/Dili,TL
|
||||
Asia/Dubai,AE,OM,RE,SC,TF
|
||||
Asia/Dushanbe,TJ
|
||||
Asia/Famagusta,CY
|
||||
Asia/Gaza,PS
|
||||
Asia/Harbin,CN
|
||||
Asia/Hebron,PS
|
||||
Asia/Ho_Chi_Minh,VN
|
||||
Asia/Hong_Kong,HK
|
||||
Asia/Hovd,MN
|
||||
Asia/Irkutsk,RU
|
||||
Asia/Istanbul,TR
|
||||
Asia/Jakarta,ID
|
||||
Asia/Jayapura,ID
|
||||
Asia/Jerusalem,IL
|
||||
Asia/Kabul,AF
|
||||
Asia/Kamchatka,RU
|
||||
Asia/Karachi,PK
|
||||
Asia/Kashgar,CN
|
||||
Asia/Kathmandu,NP
|
||||
Asia/Katmandu,NP
|
||||
Asia/Khandyga,RU
|
||||
Asia/Kolkata,IN
|
||||
Asia/Krasnoyarsk,RU
|
||||
Asia/Kuala_Lumpur,SG,AQ,MY
|
||||
Asia/Kuching,MY,BN
|
||||
Asia/Kuwait,SA,AQ,KW,YE
|
||||
Asia/Macao,MO
|
||||
Asia/Macau,MO
|
||||
Asia/Magadan,RU
|
||||
Asia/Makassar,ID
|
||||
Asia/Manila,PH
|
||||
Asia/Muscat,AE,OM,RE,SC,TF
|
||||
Asia/Nicosia,CY
|
||||
Asia/Novokuznetsk,RU
|
||||
Asia/Novosibirsk,RU
|
||||
Asia/Omsk,RU
|
||||
Asia/Oral,KZ
|
||||
Asia/Phnom_Penh,TH,CX,KH,LA,VN
|
||||
Asia/Pontianak,ID
|
||||
Asia/Pyongyang,KP
|
||||
Asia/Qatar,QA,BH
|
||||
Asia/Qostanay,KZ
|
||||
Asia/Qyzylorda,KZ
|
||||
Asia/Rangoon,MM,CC
|
||||
Asia/Riyadh,SA,AQ,KW,YE
|
||||
Asia/Saigon,VN
|
||||
Asia/Sakhalin,RU
|
||||
Asia/Samarkand,UZ
|
||||
Asia/Seoul,KR
|
||||
Asia/Shanghai,CN
|
||||
Asia/Singapore,SG,AQ,MY
|
||||
Asia/Srednekolymsk,RU
|
||||
Asia/Taipei,TW
|
||||
Asia/Tashkent,UZ
|
||||
Asia/Tbilisi,GE
|
||||
Asia/Tehran,IR
|
||||
Asia/Tel_Aviv,IL
|
||||
Asia/Thimbu,BT
|
||||
Asia/Thimphu,BT
|
||||
Asia/Tokyo,JP,AU
|
||||
Asia/Tomsk,RU
|
||||
Asia/Ujung_Pandang,ID
|
||||
Asia/Ulaanbaatar,MN
|
||||
Asia/Ulan_Bator,MN
|
||||
Asia/Urumqi,CN
|
||||
Asia/Ust-Nera,RU
|
||||
Asia/Vientiane,TH,CX,KH,LA,VN
|
||||
Asia/Vladivostok,RU
|
||||
Asia/Yakutsk,RU
|
||||
Asia/Yangon,MM,CC
|
||||
Asia/Yekaterinburg,RU
|
||||
Asia/Yerevan,AM
|
||||
Atlantic/Azores,PT
|
||||
Atlantic/Bermuda,BM
|
||||
Atlantic/Canary,ES
|
||||
Atlantic/Cape_Verde,CV
|
||||
Atlantic/Faeroe,FO
|
||||
Atlantic/Faroe,FO
|
||||
Atlantic/Jan_Mayen,DE,DK,NO,SE,SJ
|
||||
Atlantic/Madeira,PT
|
||||
Atlantic/Reykjavik,CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG
|
||||
Atlantic/South_Georgia,GS
|
||||
Atlantic/St_Helena,CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG
|
||||
Atlantic/Stanley,FK
|
||||
Australia/ACT,AU
|
||||
Australia/Adelaide,AU
|
||||
Australia/Brisbane,AU
|
||||
Australia/Broken_Hill,AU
|
||||
Australia/Canberra,AU
|
||||
Australia/Currie,AU
|
||||
Australia/Darwin,AU
|
||||
Australia/Eucla,AU
|
||||
Australia/Hobart,AU
|
||||
Australia/LHI,AU
|
||||
Australia/Lindeman,AU
|
||||
Australia/Lord_Howe,AU
|
||||
Australia/Melbourne,AU
|
||||
Australia/NSW,AU
|
||||
Australia/North,AU
|
||||
Australia/Perth,AU
|
||||
Australia/Queensland,AU
|
||||
Australia/South,AU
|
||||
Australia/Sydney,AU
|
||||
Australia/Tasmania,AU
|
||||
Australia/Victoria,AU
|
||||
Australia/West,AU
|
||||
Australia/Yancowinna,AU
|
||||
Brazil/Acre,BR
|
||||
Brazil/DeNoronha,BR
|
||||
Brazil/East,BR
|
||||
Brazil/West,BR
|
||||
CET,BE,LU,NL
|
||||
Canada/Atlantic,CA
|
||||
Canada/Central,CA
|
||||
Canada/Eastern,CA,BS
|
||||
Canada/Mountain,CA
|
||||
Canada/Newfoundland,CA
|
||||
Canada/Pacific,CA
|
||||
Canada/Saskatchewan,CA
|
||||
Canada/Yukon,CA
|
||||
Chile/Continental,CL
|
||||
Chile/EasterIsland,CL
|
||||
Cuba,CU
|
||||
EET,GR
|
||||
Egypt,EG
|
||||
Eire,IE
|
||||
Europe/Amsterdam,BE,LU,NL
|
||||
Europe/Andorra,AD
|
||||
Europe/Astrakhan,RU
|
||||
Europe/Athens,GR
|
||||
Europe/Belfast,GB,GG,IM,JE
|
||||
Europe/Belgrade,RS,BA,HR,ME,MK,SI
|
||||
Europe/Berlin,DE,DK,NO,SE,SJ
|
||||
Europe/Bratislava,CZ,SK
|
||||
Europe/Brussels,BE,LU,NL
|
||||
Europe/Bucharest,RO
|
||||
Europe/Budapest,HU
|
||||
Europe/Busingen,CH,DE,LI
|
||||
Europe/Chisinau,MD
|
||||
Europe/Copenhagen,DE,DK,NO,SE,SJ
|
||||
Europe/Dublin,IE
|
||||
Europe/Gibraltar,GI
|
||||
Europe/Guernsey,GB,GG,IM,JE
|
||||
Europe/Helsinki,FI,AX
|
||||
Europe/Isle_of_Man,GB,GG,IM,JE
|
||||
Europe/Istanbul,TR
|
||||
Europe/Jersey,GB,GG,IM,JE
|
||||
Europe/Kaliningrad,RU
|
||||
Europe/Kiev,UA
|
||||
Europe/Kirov,RU
|
||||
Europe/Kyiv,UA
|
||||
Europe/Lisbon,PT
|
||||
Europe/Ljubljana,RS,BA,HR,ME,MK,SI
|
||||
Europe/London,GB,GG,IM,JE
|
||||
Europe/Luxembourg,BE,LU,NL
|
||||
Europe/Madrid,ES
|
||||
Europe/Malta,MT
|
||||
Europe/Mariehamn,FI,AX
|
||||
Europe/Minsk,BY
|
||||
Europe/Monaco,FR,MC
|
||||
Europe/Moscow,RU
|
||||
Europe/Nicosia,CY
|
||||
Europe/Oslo,DE,DK,NO,SE,SJ
|
||||
Europe/Paris,FR,MC
|
||||
Europe/Podgorica,RS,BA,HR,ME,MK,SI
|
||||
Europe/Prague,CZ,SK
|
||||
Europe/Riga,LV
|
||||
Europe/Rome,IT,SM,VA
|
||||
Europe/Samara,RU
|
||||
Europe/San_Marino,IT,SM,VA
|
||||
Europe/Sarajevo,RS,BA,HR,ME,MK,SI
|
||||
Europe/Saratov,RU
|
||||
Europe/Simferopol,RU,UA
|
||||
Europe/Skopje,RS,BA,HR,ME,MK,SI
|
||||
Europe/Sofia,BG
|
||||
Europe/Stockholm,DE,DK,NO,SE,SJ
|
||||
Europe/Tallinn,EE
|
||||
Europe/Tirane,AL
|
||||
Europe/Tiraspol,MD
|
||||
Europe/Ulyanovsk,RU
|
||||
Europe/Uzhgorod,UA
|
||||
Europe/Vaduz,CH,DE,LI
|
||||
Europe/Vatican,IT,SM,VA
|
||||
Europe/Vienna,AT
|
||||
Europe/Vilnius,LT
|
||||
Europe/Volgograd,RU
|
||||
Europe/Warsaw,PL
|
||||
Europe/Zagreb,RS,BA,HR,ME,MK,SI
|
||||
Europe/Zaporozhye,UA
|
||||
Europe/Zurich,CH,DE,LI
|
||||
GB,GB,GG,IM,JE
|
||||
GB-Eire,GB,GG,IM,JE
|
||||
Hongkong,HK
|
||||
Iceland,CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG
|
||||
Indian/Antananarivo,KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT
|
||||
Indian/Chagos,IO
|
||||
Indian/Christmas,TH,CX,KH,LA,VN
|
||||
Indian/Cocos,MM,CC
|
||||
Indian/Comoro,KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT
|
||||
Indian/Kerguelen,MV,TF
|
||||
Indian/Mahe,AE,OM,RE,SC,TF
|
||||
Indian/Maldives,MV,TF
|
||||
Indian/Mauritius,MU
|
||||
Indian/Mayotte,KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT
|
||||
Indian/Reunion,AE,OM,RE,SC,TF
|
||||
Iran,IR
|
||||
Israel,IL
|
||||
Jamaica,JM
|
||||
Japan,JP,AU
|
||||
Kwajalein,MH
|
||||
Libya,LY
|
||||
MET,BE,LU,NL
|
||||
Mexico/BajaNorte,MX
|
||||
Mexico/BajaSur,MX
|
||||
Mexico/General,MX
|
||||
NZ,NZ,AQ
|
||||
NZ-CHAT,NZ
|
||||
Navajo,US
|
||||
PRC,CN
|
||||
Pacific/Apia,WS
|
||||
Pacific/Auckland,NZ,AQ
|
||||
Pacific/Bougainville,PG
|
||||
Pacific/Chatham,NZ
|
||||
Pacific/Chuuk,PG,AQ,FM
|
||||
Pacific/Easter,CL
|
||||
Pacific/Efate,VU
|
||||
Pacific/Enderbury,KI
|
||||
Pacific/Fakaofo,TK
|
||||
Pacific/Fiji,FJ
|
||||
Pacific/Funafuti,KI,MH,TV,UM,WF
|
||||
Pacific/Galapagos,EC
|
||||
Pacific/Gambier,PF
|
||||
Pacific/Guadalcanal,SB,FM
|
||||
Pacific/Guam,GU,MP
|
||||
Pacific/Honolulu,US
|
||||
Pacific/Johnston,US
|
||||
Pacific/Kanton,KI
|
||||
Pacific/Kiritimati,KI
|
||||
Pacific/Kosrae,FM
|
||||
Pacific/Kwajalein,MH
|
||||
Pacific/Majuro,KI,MH,TV,UM,WF
|
||||
Pacific/Marquesas,PF
|
||||
Pacific/Midway,AS,UM
|
||||
Pacific/Nauru,NR
|
||||
Pacific/Niue,NU
|
||||
Pacific/Norfolk,NF
|
||||
Pacific/Noumea,NC
|
||||
Pacific/Pago_Pago,AS,UM
|
||||
Pacific/Palau,PW
|
||||
Pacific/Pitcairn,PN
|
||||
Pacific/Pohnpei,SB,FM
|
||||
Pacific/Ponape,SB,FM
|
||||
Pacific/Port_Moresby,PG,AQ,FM
|
||||
Pacific/Rarotonga,CK
|
||||
Pacific/Saipan,GU,MP
|
||||
Pacific/Samoa,AS,UM
|
||||
Pacific/Tahiti,PF
|
||||
Pacific/Tarawa,KI,MH,TV,UM,WF
|
||||
Pacific/Tongatapu,TO
|
||||
Pacific/Truk,PG,AQ,FM
|
||||
Pacific/Wake,KI,MH,TV,UM,WF
|
||||
Pacific/Wallis,KI,MH,TV,UM,WF
|
||||
Pacific/Yap,PG,AQ,FM
|
||||
Poland,PL
|
||||
Portugal,PT
|
||||
ROK,KR
|
||||
Singapore,SG,AQ,MY
|
||||
Turkey,TR
|
||||
US/Alaska,US
|
||||
US/Aleutian,US
|
||||
US/Arizona,US,CA
|
||||
US/Central,US
|
||||
US/East-Indiana,US
|
||||
US/Eastern,US
|
||||
US/Hawaii,US
|
||||
US/Indiana-Starke,US
|
||||
US/Michigan,US
|
||||
US/Mountain,US
|
||||
US/Pacific,US
|
||||
US/Samoa,AS,UM
|
||||
W-SU,RU
|
||||
WET,PT
|
||||
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/// Verifies exact time zone lookup and preservation of candidate regions.
|
||||
@NotNullByDefault
|
||||
final class RegionUtilsTest {
|
||||
/// Reads candidates in source order and exposes an unmodifiable list.
|
||||
@Test
|
||||
void loadsUnmodifiableCandidateMapping() throws IOException {
|
||||
assertEquals(List.of("CN"), RegionUtils.findRegionsByTimeZone("Asia/Shanghai"));
|
||||
@Unmodifiable List<String> candidates = RegionUtils.findRegionsByTimeZone("Asia/Tokyo");
|
||||
assertEquals(List.of("JP", "AU"), candidates);
|
||||
assertThrows(UnsupportedOperationException.class, () -> candidates.add("US"));
|
||||
}
|
||||
|
||||
/// Does not match missing identifiers, partial names, or region fields.
|
||||
@Test
|
||||
void matchesCompleteTimeZoneField() throws IOException {
|
||||
for (String zoneId : List.of("Etc/UTC", "Asia/Shang", "CN", "")) {
|
||||
assertEquals(List.of(), RegionUtils.findRegionsByTimeZone(zoneId));
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds system time zone aliases without dropping the target's candidate countries.
|
||||
@Test
|
||||
void loadsCompatibilityAliases() throws IOException {
|
||||
assertEquals(List.of("BE", "LU", "NL"), RegionUtils.findRegionsByTimeZone("Europe/Amsterdam"));
|
||||
assertEquals(List.of("DE", "DK", "NO", "SE", "SJ"), RegionUtils.findRegionsByTimeZone("Europe/Stockholm"));
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ import static org.jackhuang.hmcl.util.io.NetworkUtils.decodeURL;
|
||||
|
||||
/**
|
||||
* @author yushijinhun
|
||||
* @see <a href="https://github.com/yushijinhun/authlib-injector/wiki/%E5%90%AF%E5%8A%A8%E5%99%A8%E6%8A%80%E6%9C%AF%E8%A7%84%E8%8C%83#dnd-%E6%96%B9%E5%BC%8F%E6%B7%BB%E5%8A%A0-yggdrasil-%E6%9C%8D%E5%8A%A1%E7%AB%AF">Launcher Technical Specification for Authlib-Injector</a>
|
||||
* @see <a href="https://yushijinhun.github.io/authlib-injector/en/launcher-technical-specification.html#configuration-via-drag-and-drop">Launcher Technical Specification for Authlib-Injector</a>
|
||||
*/
|
||||
public final class AuthlibInjectorDnD {
|
||||
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.event;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
/// @author huangyuhui
|
||||
public final class EventBus extends ClassValue<EventManager<?>> {
|
||||
|
||||
public static final EventBus EVENT_BUS = new EventBus();
|
||||
|
||||
private EventBus() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EventManager<?> computeValue(@NotNull Class<?> type) {
|
||||
return new EventManager<>();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Event> EventManager<T> channel(Class<T> clazz) {
|
||||
return (EventManager<T>) get(clazz);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Event.Result fireEvent(Event obj) {
|
||||
LOG.info(obj + " gets fired");
|
||||
|
||||
return ((EventManager<Event>) get(obj.getClass())).fireEvent(obj);
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.event;
|
||||
|
||||
import org.jackhuang.hmcl.util.ToStringBuilder;
|
||||
import org.jackhuang.hmcl.util.platform.ManagedProcess;
|
||||
|
||||
/**
|
||||
* This event gets fired when we launch the JVM and it got crashed.
|
||||
* <br>
|
||||
* This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS}
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class JVMLaunchFailedEvent extends Event {
|
||||
|
||||
private final ManagedProcess process;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param source {@link org.jackhuang.hmcl.launch.ExitWaiter}
|
||||
* @param process the crashed process.
|
||||
*/
|
||||
public JVMLaunchFailedEvent(Object source, ManagedProcess process) {
|
||||
super(source);
|
||||
this.process = process;
|
||||
}
|
||||
|
||||
public ManagedProcess getProcess() {
|
||||
return process;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("source", source)
|
||||
.append("process", process)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.event;
|
||||
|
||||
import org.jackhuang.hmcl.util.ToStringBuilder;
|
||||
import org.jackhuang.hmcl.util.platform.ManagedProcess;
|
||||
|
||||
/**
|
||||
* This event gets fired when a JavaProcess exited abnormally and the exit code is not zero.
|
||||
*
|
||||
* This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS}
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class ProcessExitedAbnormallyEvent extends Event {
|
||||
|
||||
private final ManagedProcess process;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param source {@link org.jackhuang.hmcl.launch.ExitWaiter}
|
||||
* @param process The process that exited abnormally.
|
||||
*/
|
||||
public ProcessExitedAbnormallyEvent(Object source, ManagedProcess process) {
|
||||
super(source);
|
||||
this.process = process;
|
||||
}
|
||||
|
||||
public ManagedProcess getProcess() {
|
||||
return process;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("source", source)
|
||||
.append("process", process)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.event;
|
||||
|
||||
import org.jackhuang.hmcl.util.platform.ManagedProcess;
|
||||
|
||||
/**
|
||||
* This event gets fired when minecraft process exited successfully and the exit code is 0.
|
||||
* <br>
|
||||
* This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS}
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class ProcessStoppedEvent extends Event {
|
||||
|
||||
private final ManagedProcess process;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param source {@link org.jackhuang.hmcl.launch.ExitWaiter}
|
||||
* @param process minecraft process
|
||||
*/
|
||||
public ProcessStoppedEvent(Object source, ManagedProcess process) {
|
||||
super(source);
|
||||
this.process = process;
|
||||
}
|
||||
|
||||
public ManagedProcess getProcess() {
|
||||
return process;
|
||||
}
|
||||
}
|
||||
@@ -307,8 +307,10 @@ public abstract class DefaultGameRepository implements GameRepository {
|
||||
/// Loads one instance directory without renaming on-disk JSON or jar files.
|
||||
///
|
||||
/// When the conventional `versions/<id>/<id>.json` is missing but the directory contains exactly
|
||||
/// one JSON file, that manifest path is recorded on the instance. The primary jar is derived as
|
||||
/// the sibling path with the same base name.
|
||||
/// one JSON file, that manifest path is recorded on the instance. If there are multiple JSON
|
||||
/// files, selects the first parseable manifest in file-name order with a non-blank main class,
|
||||
/// a parent instance, or non-empty patches. The primary jar is derived as the sibling path
|
||||
/// with the same base name.
|
||||
///
|
||||
/// @param snapshot the unsealed snapshot that will own the instance
|
||||
/// @param dir the instance directory under `versions/`
|
||||
@@ -332,12 +334,13 @@ public abstract class DefaultGameRepository implements GameRepository {
|
||||
json = conventionalJson;
|
||||
} else {
|
||||
List<Path> jsons = FileUtils.listFilesByExtension(dir, "json");
|
||||
if (jsons.size() != 1) {
|
||||
@Nullable Path candidate = jsons.size() == 1 ? jsons.get(0) : findInstanceManifest(jsons);
|
||||
if (candidate == null) {
|
||||
LOG.info("No available json file found, ignoring instance " + id);
|
||||
return null;
|
||||
}
|
||||
|
||||
json = jsons.get(0);
|
||||
json = candidate;
|
||||
if (!json.equals(conventionalJson)) {
|
||||
manifestFileOverride = json;
|
||||
}
|
||||
@@ -361,8 +364,35 @@ public abstract class DefaultGameRepository implements GameRepository {
|
||||
return createInstance(snapshot, id, manifest, manifestFileOverride);
|
||||
}
|
||||
|
||||
/// Finds the first parseable manifest in file-name order with a non-blank main class,
|
||||
/// a parent instance, or non-empty patches, skipping unreadable and malformed candidates.
|
||||
///
|
||||
/// @param jsons the candidate JSON files; the list is not modified
|
||||
/// @return the selected path, or `null` if no candidate matches
|
||||
private static @Nullable Path findInstanceManifest(List<Path> jsons) {
|
||||
for (Path json : jsons.stream().sorted(Comparator.comparing(FileUtils::getName)).toList()) {
|
||||
try {
|
||||
GameInstanceManifest manifest = readInstanceManifest(json);
|
||||
@Nullable String mainClass = manifest.mainClass();
|
||||
if ((mainClass != null && !mainClass.isBlank())
|
||||
|| manifest.inheritsFrom() != null
|
||||
|| !manifest.getPatches().isEmpty()) {
|
||||
return json;
|
||||
}
|
||||
} catch (IOException | RuntimeException ignored) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Reads a non-null instance manifest from a JSON file.
|
||||
///
|
||||
/// @param json the manifest file
|
||||
/// @return the parsed manifest
|
||||
/// @throws IOException if the file cannot be read
|
||||
/// @throws JsonParseException if the JSON is malformed or does not describe a manifest
|
||||
private static GameInstanceManifest readInstanceManifest(Path json) throws IOException, JsonParseException {
|
||||
GameInstanceManifest manifest = JsonUtils.fromJsonFile(json, GameInstanceManifest.class);
|
||||
@Nullable GameInstanceManifest manifest = JsonUtils.fromJsonFile(json, GameInstanceManifest.class);
|
||||
if (manifest == null) {
|
||||
throw new JsonParseException("Manifest is null");
|
||||
}
|
||||
|
||||
@@ -140,16 +140,18 @@ public enum JavaVersionConstraint {
|
||||
},
|
||||
// Minecraft>=1.13 may crash when generating world on Java [1.8,1.8.0_51)
|
||||
VANILLA_JAVA_8_51(false, GameVersionNumber.atLeast("1.13"), VersionNumber.atLeast("1.8.0_51")),
|
||||
// On Linux, JDK 9+ cannot launch Minecraft<=1.12.2, since JDK 9+ does not accept loading native library built in different arch.
|
||||
// For example, JDK 9+ 64-bit cannot load 32-bit lwjgl native library.
|
||||
/// Restricts legacy Linux x86-64 instances to Java 8 because newer JDKs reject mismatched native-library architectures.
|
||||
/// An explicitly declared newer Java requirement takes precedence; Cleanroom remains exempt.
|
||||
VANILLA_LINUX_JAVA_8(true, GameVersionNumber.atMost("1.12.999"), VersionNumber.atMost("1.8.999")) {
|
||||
/// Applies the native-library restriction only when the manifest does not require newer Java.
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version,
|
||||
@Nullable JavaRuntime java, @Nullable GameComponentAnalyzer analyzer) {
|
||||
return OperatingSystem.CURRENT_OS == OperatingSystem.LINUX
|
||||
&& Architecture.SYSTEM_ARCH == Architecture.X86_64
|
||||
&& (java == null || java.getArchitecture() == Architecture.X86_64)
|
||||
&& (analyzer == null || !analyzer.has(GameComponentType.CLEANROOM));
|
||||
&& (analyzer == null || !analyzer.has(GameComponentType.CLEANROOM))
|
||||
&& super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.launch;
|
||||
|
||||
import org.jackhuang.hmcl.event.EventBus;
|
||||
import org.jackhuang.hmcl.event.JVMLaunchFailedEvent;
|
||||
import org.jackhuang.hmcl.event.ProcessExitedAbnormallyEvent;
|
||||
import org.jackhuang.hmcl.event.ProcessStoppedEvent;
|
||||
import org.jackhuang.hmcl.util.Log4jLevel;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.platform.ManagedProcess;
|
||||
@@ -67,13 +63,10 @@ final class ExitWaiter implements Runnable {
|
||||
"Could not create the Java Virtual Machine.",
|
||||
"Error occurred during initialization of VM",
|
||||
"A fatal exception has occurred. Program will exit.")) {
|
||||
EventBus.EVENT_BUS.fireEvent(new JVMLaunchFailedEvent(this, process));
|
||||
exitType = ProcessListener.ExitType.JVM_ERROR;
|
||||
} else if (exitCode != 0 || StringUtils.containsOne(errorLines,
|
||||
"Crash report saved to", "Could not save crash report to", "This crash report has been saved to:",
|
||||
"Unable to launch", "An exception was thrown, the game will display an error screen and halt.")) {
|
||||
EventBus.EVENT_BUS.fireEvent(new ProcessExitedAbnormallyEvent(this, process));
|
||||
|
||||
if (exitCode == 137 && OperatingSystem.CURRENT_OS.isLinuxOrBSD()) {
|
||||
exitType = ProcessListener.ExitType.SIGKILL;
|
||||
} else {
|
||||
@@ -83,8 +76,6 @@ final class ExitWaiter implements Runnable {
|
||||
exitType = ProcessListener.ExitType.NORMAL;
|
||||
}
|
||||
|
||||
EventBus.EVENT_BUS.fireEvent(new ProcessStoppedEvent(this, process));
|
||||
|
||||
watcher.accept(exitCode, exitType);
|
||||
} catch (InterruptedException e) {
|
||||
watcher.accept(1, ProcessListener.ExitType.INTERRUPTED);
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.glavo.url.WebURL;
|
||||
import org.jackhuang.hmcl.download.DownloadCandidate;
|
||||
import org.jackhuang.hmcl.download.DownloadCandidates;
|
||||
import org.jackhuang.hmcl.event.Event;
|
||||
import org.jackhuang.hmcl.event.EventBus;
|
||||
import org.jackhuang.hmcl.event.EventManager;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
import org.jackhuang.hmcl.util.io.*;
|
||||
@@ -587,7 +586,7 @@ public abstract class FetchTask<T extends @UnknownNullability Object> extends Ta
|
||||
|
||||
private static final Timer timer = new Timer("DownloadSpeedRecorder", true);
|
||||
private static final AtomicLong downloadSpeed = new AtomicLong(0L);
|
||||
public static final EventManager<SpeedEvent> SPEED_EVENT = EventBus.EVENT_BUS.channel(SpeedEvent.class);
|
||||
public static final EventManager<SpeedEvent> SPEED_EVENT = new EventManager<>();
|
||||
|
||||
static {
|
||||
timer.schedule(new TimerTask() {
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HexFormat;
|
||||
import java.util.Objects;
|
||||
|
||||
/// An immutable byte sequence with content-based equality and a cached hash code.
|
||||
/// Use [#copyOf(byte[])] to copy mutable input, or [#wrap(byte[])] to retain an
|
||||
/// array that the caller guarantees will not be modified.
|
||||
@NotNullByDefault
|
||||
public final class ByteArrayKey {
|
||||
/// The retained bytes, which must not change after construction.
|
||||
private final byte @Unmodifiable [] bytes;
|
||||
|
||||
/// The content hash computed during construction.
|
||||
private final int hashCode;
|
||||
|
||||
/// Retains the supplied bytes without copying and computes their content hash.
|
||||
/// The array must not be modified after construction.
|
||||
private ByteArrayKey(byte @Unmodifiable [] bytes) {
|
||||
this.bytes = Objects.requireNonNull(bytes);
|
||||
this.hashCode = Arrays.hashCode(bytes);
|
||||
}
|
||||
|
||||
/// Creates a key by parsing pairs of hexadecimal digits without separators.
|
||||
/// Both uppercase and lowercase ASCII digits are accepted; an empty string creates an empty key.
|
||||
/// The decoded array is owned by the key and is not copied again.
|
||||
///
|
||||
/// @param hex the hexadecimal representation of the bytes
|
||||
/// @throws IllegalArgumentException if the length is odd or a character is not a hexadecimal digit
|
||||
/// @throws NullPointerException if the string is null
|
||||
public static ByteArrayKey fromHexString(String hex) {
|
||||
return new ByteArrayKey(HexFormat.of().parseHex(hex));
|
||||
}
|
||||
|
||||
/// Creates a key containing a copy of the supplied bytes, which may be empty.
|
||||
/// Subsequent changes to the input array do not affect the key.
|
||||
///
|
||||
/// @param bytes the bytes to copy
|
||||
/// @throws NullPointerException if the array is null
|
||||
public static ByteArrayKey copyOf(byte[] bytes) {
|
||||
return new ByteArrayKey(bytes.clone());
|
||||
}
|
||||
|
||||
/// Creates a key that retains the supplied array without copying it.
|
||||
/// The caller must ensure that the array is never modified after this call, including
|
||||
/// through other references. Modifying it violates the key's immutability and can make
|
||||
/// its cached hash inconsistent with its contents, invalidating hash-collection lookups.
|
||||
/// Use [#copyOf(byte[])] when this requirement cannot be met.
|
||||
///
|
||||
/// @param bytes the bytes to retain, which may be empty
|
||||
/// @throws NullPointerException if the array is null
|
||||
public static ByteArrayKey wrap(byte @Unmodifiable [] bytes) {
|
||||
return new ByteArrayKey(bytes);
|
||||
}
|
||||
|
||||
/// Returns a new array containing this key's bytes.
|
||||
/// Changes to the returned array do not affect this key.
|
||||
public byte[] toByteArray() {
|
||||
return bytes.clone();
|
||||
}
|
||||
|
||||
/// Returns whether the other object is a [ByteArrayKey] with the same byte sequence.
|
||||
/// Hash collisions do not make distinct byte sequences equal.
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return this == other || other instanceof ByteArrayKey key
|
||||
&& hashCode == key.hashCode && Arrays.equals(bytes, key.bytes);
|
||||
}
|
||||
|
||||
/// Returns the cached value of [Arrays#hashCode(byte[])] for this key's bytes.
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
/// @author Glavo
|
||||
@NotNullByDefault
|
||||
public abstract class ResourceCleaner implements AutoCloseable {
|
||||
private static final VarHandle CLEANED_HANDLE;
|
||||
|
||||
public static ResourceCleaner acquire(Semaphore semaphore) throws InterruptedException {
|
||||
semaphore.acquire();
|
||||
return new ResourceCleaner() {
|
||||
@Override
|
||||
protected void clean() {
|
||||
semaphore.release();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static {
|
||||
try {
|
||||
CLEANED_HANDLE = MethodHandles.lookup()
|
||||
.findVarHandle(ResourceCleaner.class, "cleaned", boolean.class);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private volatile boolean cleaned = false;
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (!(boolean) CLEANED_HANDLE.getAndSet(this, true)) {
|
||||
clean();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void clean();
|
||||
}
|
||||
@@ -18,10 +18,6 @@
|
||||
package org.jackhuang.hmcl.util.i18n;
|
||||
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.platform.NativeUtils;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.windows.Kernel32;
|
||||
import org.jackhuang.hmcl.util.platform.windows.WinConstants;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
@@ -31,9 +27,6 @@ import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
@@ -48,29 +41,6 @@ public final class LocaleUtils {
|
||||
|
||||
public static final Locale SYSTEM_DEFAULT = Locale.getDefault();
|
||||
|
||||
public static final boolean IS_CHINA_MAINLAND = isChinaMainland();
|
||||
|
||||
private static boolean isChinaMainland() {
|
||||
if ("Asia/Shanghai".equals(ZoneId.systemDefault().getId()))
|
||||
return true;
|
||||
|
||||
// Check if the time zone is UTC+8
|
||||
if (ZonedDateTime.now().getOffset().getTotalSeconds() == Duration.ofHours(8).toSeconds()) {
|
||||
if ("CN".equals(LocaleUtils.SYSTEM_DEFAULT.getCountry()))
|
||||
return true;
|
||||
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS && NativeUtils.USE_JNA) {
|
||||
Kernel32 kernel32 = Kernel32.INSTANCE;
|
||||
|
||||
// https://learn.microsoft.com/windows/win32/intl/table-of-geographical-locations
|
||||
if (kernel32 != null && kernel32.GetUserGeoID(WinConstants.GEOCLASS_NATION) == 45) // China
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static final Locale LOCALE_ZH_HANS = Locale.forLanguageTag("zh-Hans");
|
||||
public static final Locale LOCALE_ZH_HANT = Locale.forLanguageTag("zh-Hant");
|
||||
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.io;
|
||||
|
||||
import org.jackhuang.hmcl.util.ByteArrayKey;
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import javax.net.ssl.CertPathTrustManagerParameters;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.CertPathValidatorException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.PKIXBuilderParameters;
|
||||
import java.security.cert.PKIXCertPathChecker;
|
||||
import java.security.cert.TrustAnchor;
|
||||
import java.security.cert.X509CertSelector;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
/// Installs TLS certificate validation that rejects known compromised CA public keys.
|
||||
@NotNullByDefault
|
||||
public final class TLSSecurity {
|
||||
/// Prevents instantiation.
|
||||
private TLSSecurity() {
|
||||
}
|
||||
|
||||
/// Replaces the process-wide default TLS context and HTTPS socket factory.
|
||||
/// Uses the JSSE default trust store, including `javax.net.ssl.trustStore` overrides,
|
||||
/// and the `com.sun.net.ssl.checkRevocation` setting. Client credentials use the JSSE
|
||||
/// `javax.net.ssl.keyStore` properties. Normal PKIX validation and HTTPS hostname
|
||||
/// verification remain enabled. Existing connections and explicitly
|
||||
/// configured TLS contexts are not changed.
|
||||
///
|
||||
/// Uses the configured default trust manager algorithm, which must support PKIX parameters.
|
||||
/// Preinstalled default contexts and socket factories are replaced, not wrapped.
|
||||
/// This method must be called after logging is initialized and before network activity.
|
||||
/// If configuration fails, logs the cause and installs a context that rejects all certificate
|
||||
/// authentication so that offline startup can continue without unprotected TLS requests.
|
||||
///
|
||||
/// @throws IllegalStateException if even the rejecting TLS context cannot be installed
|
||||
public static void install() {
|
||||
SSLContext context;
|
||||
try {
|
||||
context = SSLContext.getInstance("TLS");
|
||||
TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
factory.init(new CertPathTrustManagerParameters(createParameters(null)));
|
||||
context.init(createKeyManagers(), factory.getTrustManagers(), null);
|
||||
} catch (GeneralSecurityException | IOException | RuntimeException failure) {
|
||||
LOG.warning("TLS configuration failed; default TLS connections will be rejected", failure);
|
||||
try {
|
||||
context = SSLContext.getInstance("TLS");
|
||||
context.init(new KeyManager[0], new X509TrustManager[]{new RejectingTrustManager(failure)}, null);
|
||||
} catch (GeneralSecurityException e) {
|
||||
e.addSuppressed(failure);
|
||||
throw new IllegalStateException("Failed to disable unprotected TLS connections", e);
|
||||
}
|
||||
}
|
||||
SSLContext.setDefault(context);
|
||||
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
|
||||
}
|
||||
|
||||
/// Rejects certificate authentication after TLS configuration fails.
|
||||
@NotNullByDefault
|
||||
private static final class RejectingTrustManager implements X509TrustManager {
|
||||
/// The configuration failure retained as the cause of handshake errors.
|
||||
private final Exception failure;
|
||||
|
||||
/// Retains the configuration failure for subsequent handshake diagnostics.
|
||||
private RejectingTrustManager(Exception failure) {
|
||||
this.failure = failure;
|
||||
}
|
||||
|
||||
/// Always rejects client certificate authentication with the configuration failure.
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
throw new CertificateException("TLS configuration failed", failure);
|
||||
}
|
||||
|
||||
/// Always rejects server certificate authentication with the configuration failure.
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
throw new CertificateException("TLS configuration failed", failure);
|
||||
}
|
||||
|
||||
/// Returns an empty array because no certificate authorities are trusted.
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return new X509Certificate[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads client credentials using the JSSE key store path, type, provider, and password properties.
|
||||
/// An empty path or `NONE` loads the store without a file; PKCS11 requires `NONE`.
|
||||
/// An empty type delegates store selection to the default key manager factory.
|
||||
///
|
||||
/// @throws GeneralSecurityException if the store or key managers cannot be initialized
|
||||
/// @throws IOException if the store cannot be read or its password is incorrect
|
||||
static KeyManager[] createKeyManagers() throws GeneralSecurityException, IOException {
|
||||
String path = System.getProperty("javax.net.ssl.keyStore", "");
|
||||
String type = System.getProperty("javax.net.ssl.keyStoreType", KeyStore.getDefaultType());
|
||||
String provider = System.getProperty("javax.net.ssl.keyStoreProvider", "");
|
||||
String passwordProperty = System.getProperty("javax.net.ssl.keyStorePassword", "");
|
||||
boolean pkcs11 = "PKCS11".equals(type);
|
||||
if (pkcs11 && !"NONE".equals(path))
|
||||
throw new GeneralSecurityException("PKCS11 key stores require javax.net.ssl.keyStore=NONE");
|
||||
|
||||
char @Nullable [] password = passwordProperty.isEmpty() ? null : passwordProperty.toCharArray();
|
||||
try {
|
||||
@Nullable KeyStore store = null;
|
||||
try (@Nullable InputStream input = path.isEmpty() || "NONE".equals(path) ? null : new FileInputStream(path)) {
|
||||
if (!type.isEmpty()) {
|
||||
store = provider.isEmpty() ? KeyStore.getInstance(type) : KeyStore.getInstance(type, provider);
|
||||
store.load(input, password);
|
||||
}
|
||||
}
|
||||
KeyManagerFactory factory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
// Token providers obtain private keys without a separate key password.
|
||||
factory.init(store, pkcs11 ? null : password);
|
||||
return factory.getKeyManagers();
|
||||
} finally {
|
||||
if (password != null)
|
||||
Arrays.fill(password, '\0');
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates PKIX parameters with compromised trust anchors removed and a path checker.
|
||||
/// A null store selects the JSSE default store. Revocation follows the JSSE system property.
|
||||
///
|
||||
/// @throws GeneralSecurityException if initialization fails or no usable trust anchors remain
|
||||
static PKIXBuilderParameters createParameters(@Nullable KeyStore store) throws GeneralSecurityException {
|
||||
TrustManagerFactory defaults = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
defaults.init(store);
|
||||
Set<TrustAnchor> anchors = new HashSet<>();
|
||||
BlockedKeyChecker checker = new BlockedKeyChecker();
|
||||
for (var manager : defaults.getTrustManagers()) {
|
||||
if (manager instanceof X509TrustManager x509) {
|
||||
for (X509Certificate certificate : x509.getAcceptedIssuers()) {
|
||||
// PKIX path checkers do not inspect trust anchors.
|
||||
if (!checker.isBlocked(certificate))
|
||||
anchors.add(new TrustAnchor(certificate, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
PKIXBuilderParameters parameters = new PKIXBuilderParameters(anchors, new X509CertSelector());
|
||||
parameters.setRevocationEnabled(Boolean.getBoolean("com.sun.net.ssl.checkRevocation"));
|
||||
parameters.addCertPathChecker(checker);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/// Rejects compromised public keys in trust anchors and constructed certificate paths.
|
||||
/// Instances are stateless and may be used concurrently.
|
||||
@NotNullByDefault
|
||||
private static final class BlockedKeyChecker extends PKIXCertPathChecker {
|
||||
/// DER SubjectPublicKeyInfo encodings of compromised TLS and code-signing CA public keys.
|
||||
/// Cross-signed certificates are covered because they share the same public keys.
|
||||
/// Sources: [Mozilla incident announcement](https://groups.google.com/a/mozilla.org/g/dev-security-policy/c/0R3XLJL6FuE/m/Kp1v6CPuAAAJ) and
|
||||
/// [pinned certificate list](https://github.com/galadran/we-have-a-problem.com/blob/e2786ff8b707b4951f0db1f83ccaf8ac9af2f7e6/impacted-intermediates.json).
|
||||
private static final @Unmodifiable Set<ByteArrayKey> BLOCKED_KEYS = Set.of(
|
||||
// EM DV TLS CA - G3B
|
||||
ByteArrayKey.fromHexString("3076301006072A8648CE3D020106052B8104002203620004E79ED9C04A9773C4735E51111DA85AC2"
|
||||
+ "BD5E64AB374B80E12CBC2038EC1E4F0E3B812FC9859E1E67AFB04E2F8BC97F7485BA65AA911EE8A4"
|
||||
+ "E45B62D5CC03D56BE678B3C38CB4DC937CA2460E238453C892935D29D44F91E15606B63CCEDAB468"),
|
||||
// EM OV TLS CA - G3B
|
||||
ByteArrayKey.fromHexString("3076301006072A8648CE3D020106052B810400220362000473897AA0EB3CFDCA8B12402FFAF0ABC4"
|
||||
+ "E1212BAF8DEE778AC9EE2D6DB83FAC280C5E05638AE7A2ACD71C322A8E361CDEB9F69C4302B9DD8D"
|
||||
+ "73D1CC6CAAB2CC0AEE4E216CE979DD518A4AF4B626E5684CDD658037ED0C854E4CC12F4CF8646D0D"),
|
||||
// EM EV TLS CA - G3B
|
||||
ByteArrayKey.fromHexString("3076301006072A8648CE3D020106052B810400220362000425DBA85A64D397DA0B1052E9EA546E48"
|
||||
+ "7BEFEA9C9D9D68A0C5EADCFEEF75A69BB5D6FC01F42BFBCA6EB40BCD64C0767E36FFD05D97A813C5"
|
||||
+ "AA0C6C9530E79CF5F0866468F13806420AA5C851840B1E252540E790E4B654C3852ED64AF1E8657D"),
|
||||
// EM DV TLS CA - G3C
|
||||
ByteArrayKey.fromHexString("3076301006072A8648CE3D020106052B81040022036200043A7A7862C8C44D324DDD69033A6782C0"
|
||||
+ "EFEEFE4055E68A123FA0371BA3BD9194514EE172A433E3630F02BD188037D9E5149F9B77DB94A281"
|
||||
+ "DF987B2FEA888B74BDFE8FDA4C1099EE6CA19566FE3EF0E5F6B09A556CCC8BA50561DF1B2A441D01"),
|
||||
// EM OV TLS CA - G3C
|
||||
ByteArrayKey.fromHexString("3076301006072A8648CE3D020106052B8104002203620004138251CD52AC9298C1C8AAD977321DEB"
|
||||
+ "97E709BD0B4CA0ACA55DC8AD51DCFC9D1589A1597E3A5120E1EFD631C63E1835CACAE29869A62E16"
|
||||
+ "31E8A28181AB56616DC45D918ABC09F3AB0E63CF792AA4DCED7387BE37BBA569549F1C02B270ED67"),
|
||||
// EM EV TLS CA - G3C
|
||||
ByteArrayKey.fromHexString("3076301006072A8648CE3D020106052B81040022036200042D56E829A6FB82C64A4697D960AF951E"
|
||||
+ "9067AE91F926D34306DA48A8AAEB7869B6A67E6D27426E0442AA03B36E1988DFF636514EE1CF07F5"
|
||||
+ "BA4B3FF2A06645AE7D31D5866AD31AA3D246EB0B3177EE99CB7566C516CE74808A668F91F0C47FCA"),
|
||||
// EM OV CS CA - G3B
|
||||
ByteArrayKey.fromHexString("3076301006072A8648CE3D020106052B8104002203620004AABB0FB0666ACFCF01FAD52BFCFD4C2B"
|
||||
+ "F2CB3C3497DE73950E5819B61FE695098741C8C491439B34A0431CAFFE5BF9B4C1C8E8AE0001F9F4"
|
||||
+ "DC8AD82C59F3639E96CD6C3D4DB0CEFEC26C3D62517B37761372743F8E8ADD77B10064F2ABE5E050"),
|
||||
// EM OV CS CA - G3C
|
||||
ByteArrayKey.fromHexString("3076301006072A8648CE3D020106052B8104002203620004B9F0E0634702A69CA6AADD443CFD62C4"
|
||||
+ "1B9BA4B20924697D7974E7691A126FB96AFB3E34C2FD3BC5F483061F265DC2A58436347323510436"
|
||||
+ "B28F04F3862363067980E2503E02532D6546235A7165A259A1EF496F741B5FDEE7787208E4B8FAEF")
|
||||
);
|
||||
|
||||
/// Returns whether the certificate contains an exact blocked SubjectPublicKeyInfo encoding.
|
||||
///
|
||||
/// @throws CertPathValidatorException if the public key cannot be encoded
|
||||
private boolean isBlocked(Certificate certificate) throws CertPathValidatorException {
|
||||
byte @Nullable [] encoded = certificate.getPublicKey().getEncoded();
|
||||
if (encoded == null)
|
||||
throw new CertPathValidatorException("Certificate public key cannot be encoded");
|
||||
return BLOCKED_KEYS.contains(ByteArrayKey.wrap(encoded));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(boolean forward) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForwardCheckingSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Unmodifiable Set<String> getSupportedExtensions() {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
/// Rejects a blocked public key without consuming any critical extensions.
|
||||
///
|
||||
/// @throws CertPathValidatorException if the public key is blocked or cannot be encoded
|
||||
@Override
|
||||
public void check(Certificate certificate, @Nullable Collection<String> unresolvedCritExts)
|
||||
throws CertPathValidatorException {
|
||||
if (isBlocked(certificate))
|
||||
throw new CertPathValidatorException("Certificate uses a blocked CA public key");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,13 +64,25 @@ public final class GraphicsCard {
|
||||
private final @Nullable String driverVersion;
|
||||
private final @Unmodifiable List<Path> vulkanDriverFiles;
|
||||
|
||||
private GraphicsCard(String name, @Nullable HardwareVendor vendor, @Nullable Type type, @Nullable String driver, @Nullable String driverVersion, List<Path> vulkanDriverFiles) {
|
||||
/// Whether the detector identified this adapter as a known virtual display adapter.
|
||||
private final boolean virtualDisplayAdapter;
|
||||
|
||||
/// Creates an adapter with the supplied metadata and detection result.
|
||||
private GraphicsCard(String name, @Nullable HardwareVendor vendor, @Nullable Type type, @Nullable String driver, @Nullable String driverVersion, @Unmodifiable List<Path> vulkanDriverFiles, boolean virtualDisplayAdapter) {
|
||||
this.name = Objects.requireNonNull(name);
|
||||
this.vendor = vendor;
|
||||
this.type = type;
|
||||
this.driver = driver;
|
||||
this.driverVersion = driverVersion;
|
||||
this.vulkanDriverFiles = vulkanDriverFiles;
|
||||
this.virtualDisplayAdapter = virtualDisplayAdapter;
|
||||
}
|
||||
|
||||
/// Returns whether this adapter was identified as a known virtual display adapter.
|
||||
/// A false result also includes adapters whose classification is unknown;
|
||||
/// it does not establish that the adapter is physical.
|
||||
public boolean isVirtualDisplayAdapter() {
|
||||
return virtualDisplayAdapter;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -117,8 +129,12 @@ public final class GraphicsCard {
|
||||
private @Nullable String driverVersion;
|
||||
private @Nullable List<Path> vulkanDriverFiles;
|
||||
|
||||
/// Whether the adapter was identified as a known virtual display adapter; initially false.
|
||||
private boolean virtualDisplayAdapter;
|
||||
|
||||
/// Creates an adapter, supplying a vendor-based or unknown name if no name was set.
|
||||
public GraphicsCard build() {
|
||||
String name = this.name;
|
||||
@Nullable String name = this.name;
|
||||
if (name == null) {
|
||||
if (vendor != null)
|
||||
name = vendor + " Graphics";
|
||||
@@ -128,7 +144,17 @@ public final class GraphicsCard {
|
||||
|
||||
List<Path> vulkanDriverFiles = Objects.requireNonNullElse(this.vulkanDriverFiles, List.of());
|
||||
|
||||
return new GraphicsCard(name, vendor, type, driver, driverVersion, List.copyOf(vulkanDriverFiles));
|
||||
return new GraphicsCard(name, vendor, type, driver, driverVersion, List.copyOf(vulkanDriverFiles), virtualDisplayAdapter);
|
||||
}
|
||||
|
||||
/// Sets whether the detector identified the adapter as a known virtual display adapter.
|
||||
///
|
||||
/// @param virtualDisplayAdapter true for an identified virtual display adapter;
|
||||
/// false otherwise, including when classification is unknown
|
||||
/// @return this builder
|
||||
public Builder setVirtualDisplayAdapter(boolean virtualDisplayAdapter) {
|
||||
this.virtualDisplayAdapter = virtualDisplayAdapter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @Nullable String getName() {
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.platform.windows;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.win32.StdCallLibrary;
|
||||
import org.jackhuang.hmcl.util.platform.NativeUtils;
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/// Provides mappings for the OLE/COM functions used by HMCL.
|
||||
///
|
||||
/// @author Glavo
|
||||
@NotNullByDefault
|
||||
public interface Ole32 extends StdCallLibrary {
|
||||
|
||||
/// The loaded Ole32 library, or `null` when JNA is unavailable or the current platform is not Windows.
|
||||
@Nullable Ole32 INSTANCE = NativeUtils.USE_JNA && com.sun.jna.Platform.isWindows()
|
||||
? NativeUtils.load("ole32", Ole32.class)
|
||||
: null;
|
||||
|
||||
/// Frees a block of task memory previously allocated through the COM task allocator.
|
||||
///
|
||||
/// @param pv the memory block to free; ignored when `null`
|
||||
/// @see <a href="https://learn.microsoft.com/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemfree">CoTaskMemFree function</a>
|
||||
void CoTaskMemFree(@Nullable Pointer pv);
|
||||
}
|
||||
@@ -42,16 +42,6 @@ public interface Shell32 extends StdCallLibrary {
|
||||
/// @see <a href="https://learn.microsoft.com/windows/win32/api/shobjidl_core/nf-shobjidl_core-setcurrentprocessexplicitappusermodelid">SetCurrentProcessExplicitAppUserModelID function</a>
|
||||
int SetCurrentProcessExplicitAppUserModelID(WString appID);
|
||||
|
||||
/// Retrieves the application-defined AppUserModelID for the current process.
|
||||
///
|
||||
/// The caller must free the returned string with [`Ole32#CoTaskMemFree(com.sun.jna.Pointer)`]
|
||||
/// when it is no longer needed.
|
||||
///
|
||||
/// @param ppszAppID receives a pointer to the process AppUserModelID string
|
||||
/// @return `S_OK` if the operation succeeds; otherwise a failure `HRESULT`
|
||||
/// @see <a href="https://learn.microsoft.com/windows/win32/api/shobjidl_core/nf-shobjidl_core-getcurrentprocessexplicitappusermodelid">GetCurrentProcessExplicitAppUserModelID function</a>
|
||||
int GetCurrentProcessExplicitAppUserModelID(PointerByReference ppszAppID);
|
||||
|
||||
/// Retrieves an [`IPropertyStore`] interface for the given window.
|
||||
///
|
||||
/// @param hwnd the window whose property store is requested
|
||||
|
||||
@@ -124,6 +124,9 @@ final class WindowsGPUDetector {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads graphics card information from the display adapter registry keys.
|
||||
/// Uses the driver description when the adapter name is missing or blank;
|
||||
/// if both are missing or blank, leaves the name to the builder's fallback.
|
||||
private static List<GraphicsCard> detectByRegistry(WinReg reg) {
|
||||
final WinReg.HKEY hkey = WinReg.HKEY.HKEY_LOCAL_MACHINE;
|
||||
final String displayDevices = "SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\";
|
||||
@@ -134,14 +137,18 @@ final class WindowsGPUDetector {
|
||||
if (!graphicsCardPattern.matcher(subkey).find())
|
||||
continue;
|
||||
|
||||
String name = regValueToString(reg.queryValue(hkey, subkey, "HardwareInformation.AdapterString"));
|
||||
String vendor = regValueToString(reg.queryValue(hkey, subkey, "ProviderName"));
|
||||
String driverVersion = regValueToString(reg.queryValue(hkey, subkey, "DriverVersion"));
|
||||
String dacType = regValueToString(reg.queryValue(hkey, subkey, "HardwareInformation.DacType"));
|
||||
Object vulkanDriverName = reg.queryValue(hkey, subkey, "VulkanDriverName");
|
||||
@Nullable String name = regValueToString(reg.queryValue(hkey, subkey, "HardwareInformation.AdapterString"));
|
||||
if (StringUtils.isBlank(name))
|
||||
name = regValueToString(reg.queryValue(hkey, subkey, "DriverDesc"));
|
||||
@Nullable String vendor = regValueToString(reg.queryValue(hkey, subkey, "ProviderName"));
|
||||
@Nullable String driverVersion = regValueToString(reg.queryValue(hkey, subkey, "DriverVersion"));
|
||||
@Nullable String dacType = regValueToString(reg.queryValue(hkey, subkey, "HardwareInformation.DacType"));
|
||||
@Nullable Object vulkanDriverName = reg.queryValue(hkey, subkey, "VulkanDriverName");
|
||||
@Nullable String matchingDeviceId = regValueToString(reg.queryValue(hkey, subkey, "MatchingDeviceId"));
|
||||
|
||||
GraphicsCard.Builder builder = GraphicsCard.builder();
|
||||
if (name != null)
|
||||
builder.setVirtualDisplayAdapter("Root\\GameViewerIddDriver".equalsIgnoreCase(matchingDeviceId));
|
||||
if (StringUtils.isNotBlank(name))
|
||||
builder.setName(GraphicsCard.cleanName(name));
|
||||
if (vendor != null)
|
||||
builder.setVendor(HardwareVendor.of(vendor));
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# IANA registry version: 2026-09-17
|
||||
# Generated from https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
|
||||
Arab,ar,fa,ps,ur
|
||||
Armn,hy
|
||||
Beng,as,bn
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
# IANA registry version: 2026-09-17
|
||||
# Generated from https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
|
||||
ak,tw,fat
|
||||
ar,aao,abh,abv,acm,acq,acw,acx,acy,adf,aeb,aec,afb,apc,apd,arb,arq,ars,ary,arz,auz,avl,ayh,ayl,ayn,ayp,pga,shu,ssh
|
||||
ay,ayc,ayr
|
||||
|
||||
|
@@ -49,6 +49,40 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@NotNullByDefault
|
||||
public final class DefaultGameRepositoryDraftTest {
|
||||
|
||||
/// Finds a manifest among unrelated and malformed JSON files and preserves its storage path.
|
||||
@Test
|
||||
public void testRefreshSelectsManifestAmongMultipleJsonFiles(@TempDir Path tempDirectory) throws IOException {
|
||||
TestRepository repository = new TestRepository(tempDirectory);
|
||||
GameInstanceID id = new GameInstanceID("instance");
|
||||
Path root = Files.createDirectories(repository.getLayout().getInstanceRoot(id));
|
||||
Files.writeString(root.resolve("a-broken.json"), "{");
|
||||
Files.writeString(root.resolve("b-settings.json"), "{\"id\":\"settings\",\"mainClass\":42}");
|
||||
Path selected = root.resolve("c-game.json");
|
||||
Files.writeString(selected, "{\"id\":\"original\",\"mainClass\":\"example.Main\"}");
|
||||
Files.writeString(root.resolve("d-game.json"), "{\"id\":\"other\",\"mainClass\":\"other.Main\"}");
|
||||
|
||||
repository.refresh();
|
||||
|
||||
assertEquals(selected, repository.getInstance(id).getManifestFile());
|
||||
assertEquals(id, repository.getInstance(id).getManifest().id());
|
||||
assertEquals("example.Main", repository.getInstance(id).getManifest().mainClass());
|
||||
assertFalse(Files.exists(repository.getLayout().getInstanceJson(id)));
|
||||
}
|
||||
|
||||
/// Ignores directories whose multiple JSON files do not contain an instance manifest.
|
||||
@Test
|
||||
public void testRefreshIgnoresUnrelatedJsonFiles(@TempDir Path tempDirectory) throws IOException {
|
||||
TestRepository repository = new TestRepository(tempDirectory);
|
||||
GameInstanceID id = new GameInstanceID("instance");
|
||||
Path root = Files.createDirectories(repository.getLayout().getInstanceRoot(id));
|
||||
Files.writeString(root.resolve("settings.json"), "{\"id\":\"settings\"}");
|
||||
Files.writeString(root.resolve("empty.json"), "null");
|
||||
|
||||
repository.refresh();
|
||||
|
||||
assertFalse(repository.hasInstance(id));
|
||||
}
|
||||
|
||||
/// Rejects path-segment instance ids before they can reach repository deletion code.
|
||||
@Test
|
||||
public void testRejectsSpecialPathSegmentIds() {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import org.jackhuang.hmcl.java.JavaInfo;
|
||||
import org.jackhuang.hmcl.java.JavaRuntime;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.Platform;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
/// Tests manifest precedence over the legacy Linux Java ceiling.
|
||||
@NotNullByDefault
|
||||
public final class JavaVersionConstraintTest {
|
||||
/// The affected Minecraft version.
|
||||
private static final GameVersionNumber GAME = GameVersionNumber.asGameVersion("1.7.10");
|
||||
/// Synthetic Java 8 metadata; no executable is launched.
|
||||
private static final JavaRuntime JAVA_8 = new JavaRuntime(Path.of("java8"),
|
||||
new JavaInfo(Platform.LINUX_X86_64, "1.8.0_402", "test"), false, true);
|
||||
/// Synthetic Java 25 metadata; no executable is launched.
|
||||
private static final JavaRuntime JAVA_25 = new JavaRuntime(Path.of("java25"),
|
||||
new JavaInfo(Platform.LINUX_X86_64, "25", "test"), false, true);
|
||||
|
||||
/// Modern manifest requirements suppress the ceiling for both discovery and explicit runtime validation.
|
||||
@Test
|
||||
public void testModernRequirementsTakePrecedence() {
|
||||
assumeTrue(OperatingSystem.CURRENT_OS == OperatingSystem.LINUX && Architecture.SYSTEM_ARCH == Architecture.X86_64);
|
||||
for (int major : List.of(17, 25)) {
|
||||
GameInstanceManifest manifest = new GameInstanceManifest(new GameInstanceID("modern"))
|
||||
.withJavaVersion(new GameJavaVersion("test", major));
|
||||
GameComponentAnalyzer analyzer = GameComponentAnalyzer.analyze(manifest, GAME);
|
||||
JavaRuntime runtime = new JavaRuntime(Path.of("java" + major),
|
||||
new JavaInfo(Platform.LINUX_X86_64, String.valueOf(major), "test"), false, true);
|
||||
|
||||
assertFalse(JavaVersionConstraint.VANILLA_LINUX_JAVA_8.appliesToVersion(GAME, manifest, runtime, analyzer));
|
||||
assertFalse(JavaVersionConstraint.VANILLA_LINUX_JAVA_8.appliesToVersion(GAME, manifest, null, analyzer));
|
||||
assertTrue(JavaVersionConstraint.GAME_JSON.appliesToVersion(GAME, manifest, runtime, analyzer));
|
||||
assertFalse(JavaVersionConstraint.GAME_JSON.checkJava(GAME, manifest, JAVA_8, analyzer));
|
||||
assertTrue(JavaVersionConstraint.GAME_JSON.checkJava(GAME, manifest, runtime, analyzer));
|
||||
}
|
||||
}
|
||||
|
||||
/// Missing metadata and Java 8 declarations retain the platform-specific ceiling.
|
||||
@Test
|
||||
public void testLegacyRequirementsRetainCeiling() {
|
||||
GameInstanceManifest manifest = new GameInstanceManifest(new GameInstanceID("legacy"));
|
||||
GameComponentAnalyzer analyzer = GameComponentAnalyzer.analyze(manifest, GAME);
|
||||
boolean affectedPlatform = OperatingSystem.CURRENT_OS == OperatingSystem.LINUX && Architecture.SYSTEM_ARCH == Architecture.X86_64;
|
||||
for (int major : List.of(0, 8, -1)) {
|
||||
@Nullable GameInstanceManifest candidate = major == -1 ? null
|
||||
: major == 0 ? manifest : manifest.withJavaVersion(GameJavaVersion.JAVA_8);
|
||||
assertEquals(affectedPlatform, JavaVersionConstraint.VANILLA_LINUX_JAVA_8.appliesToVersion(GAME, candidate, JAVA_25, analyzer));
|
||||
assertEquals(affectedPlatform, JavaVersionConstraint.VANILLA_LINUX_JAVA_8.appliesToVersion(GAME, candidate, null, analyzer));
|
||||
}
|
||||
assertTrue(JavaVersionConstraint.VANILLA_LINUX_JAVA_8.checkJava(GAME, manifest, JAVA_8, analyzer));
|
||||
assertFalse(JavaVersionConstraint.VANILLA_LINUX_JAVA_8.checkJava(GAME, manifest, JAVA_25, analyzer));
|
||||
}
|
||||
|
||||
/// Newer games, other runtime architectures, and Cleanroom remain exempt.
|
||||
@Test
|
||||
public void testExistingExemptions() {
|
||||
assumeTrue(OperatingSystem.CURRENT_OS == OperatingSystem.LINUX && Architecture.SYSTEM_ARCH == Architecture.X86_64);
|
||||
GameInstanceManifest manifest = new GameInstanceManifest(new GameInstanceID("legacy"));
|
||||
GameComponentAnalyzer analyzer = GameComponentAnalyzer.analyze(manifest, GAME);
|
||||
assertFalse(JavaVersionConstraint.VANILLA_LINUX_JAVA_8.appliesToVersion(
|
||||
GameVersionNumber.asGameVersion("1.13"), manifest, JAVA_25, analyzer));
|
||||
JavaRuntime armRuntime = new JavaRuntime(Path.of("java-arm"),
|
||||
new JavaInfo(Platform.LINUX_ARM64, "25", "test"), false, true);
|
||||
assertFalse(JavaVersionConstraint.VANILLA_LINUX_JAVA_8.appliesToVersion(GAME, manifest, armRuntime, analyzer));
|
||||
GameInstanceManifest cleanroom = manifest.withLibraries(List.of(new Library("com.cleanroommc", "cleanroom", "0.3.0")));
|
||||
GameComponentAnalyzer cleanroomAnalyzer = GameComponentAnalyzer.analyze(cleanroom, GAME);
|
||||
assertTrue(cleanroomAnalyzer.has(GameComponentType.CLEANROOM));
|
||||
assertFalse(JavaVersionConstraint.VANILLA_LINUX_JAVA_8.appliesToVersion(GAME, cleanroom, JAVA_25, cleanroomAnalyzer));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2025 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/// Verifies byte-array ownership and equality when keys are used in hash collections.
|
||||
@NotNullByDefault
|
||||
class ByteArrayKeyTest {
|
||||
/// Keeps map lookup and the cached hash stable after input and output arrays are modified.
|
||||
@Test
|
||||
void ownsItsBytes() {
|
||||
byte[] input = {1, 2, 3};
|
||||
ByteArrayKey key = ByteArrayKey.copyOf(input);
|
||||
int hash = key.hashCode();
|
||||
var map = new HashMap<ByteArrayKey, String>();
|
||||
map.put(key, "value");
|
||||
|
||||
input[0] = 9;
|
||||
byte[] output = key.toByteArray();
|
||||
output[1] = 9;
|
||||
|
||||
assertArrayEquals(new byte[]{1, 2, 3}, key.toByteArray());
|
||||
assertEquals(hash, key.hashCode());
|
||||
assertEquals("value", map.get(ByteArrayKey.copyOf(new byte[]{1, 2, 3})));
|
||||
assertNotSame(output, key.toByteArray());
|
||||
}
|
||||
|
||||
/// Creates equal keys through all factories and keeps the retained array hidden on output.
|
||||
@Test
|
||||
void supportsAllFactories() {
|
||||
byte[] bytes = {0, 10, -1};
|
||||
ByteArrayKey wrapped = ByteArrayKey.wrap(bytes);
|
||||
ByteArrayKey copied = ByteArrayKey.copyOf(bytes);
|
||||
ByteArrayKey parsed = ByteArrayKey.fromHexString("000aFF");
|
||||
assertEquals(copied, wrapped);
|
||||
assertEquals(copied, parsed);
|
||||
assertEquals(copied.hashCode(), wrapped.hashCode());
|
||||
assertEquals(copied.hashCode(), parsed.hashCode());
|
||||
wrapped.toByteArray()[0] = 1;
|
||||
assertEquals(parsed, wrapped);
|
||||
assertEquals(ByteArrayKey.copyOf(new byte[0]), ByteArrayKey.fromHexString(""));
|
||||
assertEquals(ByteArrayKey.copyOf(new byte[0]), ByteArrayKey.wrap(new byte[0]));
|
||||
}
|
||||
|
||||
/// Rejects invalid hexadecimal strings and absent inputs.
|
||||
@Test
|
||||
void rejectsInvalidFactoryArguments() {
|
||||
for (String hex : new String[]{"0", "0g", "00 ff", "0x00"})
|
||||
assertThrows(IllegalArgumentException.class, () -> ByteArrayKey.fromHexString(hex));
|
||||
assertThrows(NullPointerException.class, () -> ByteArrayKey.fromHexString(null));
|
||||
assertThrows(NullPointerException.class, () -> ByteArrayKey.copyOf(null));
|
||||
assertThrows(NullPointerException.class, () -> ByteArrayKey.wrap(null));
|
||||
}
|
||||
|
||||
/// Distinguishes different byte sequences even when their cached hash codes collide.
|
||||
@Test
|
||||
void resolvesHashCollisionsByContent() {
|
||||
ByteArrayKey first = ByteArrayKey.copyOf(new byte[]{0, 31});
|
||||
ByteArrayKey second = ByteArrayKey.copyOf(new byte[]{1, 0});
|
||||
assertEquals(first.hashCode(), second.hashCode());
|
||||
assertNotEquals(first, second);
|
||||
var map = new HashMap<ByteArrayKey, String>();
|
||||
map.put(first, "first");
|
||||
map.put(second, "second");
|
||||
assertEquals(2, map.size());
|
||||
assertEquals("first", map.get(ByteArrayKey.copyOf(new byte[]{0, 31})));
|
||||
assertEquals("second", map.get(ByteArrayKey.copyOf(new byte[]{1, 0})));
|
||||
}
|
||||
|
||||
/// Supports empty sequences and enforces equality and null-input contracts.
|
||||
@Test
|
||||
void comparesByContent() {
|
||||
ByteArrayKey first = ByteArrayKey.copyOf(new byte[]{-128, 0, 127});
|
||||
ByteArrayKey second = ByteArrayKey.copyOf(new byte[]{-128, 0, 127});
|
||||
assertEquals(first, first);
|
||||
assertEquals(first, second);
|
||||
assertEquals(second, first);
|
||||
assertEquals(Arrays.hashCode(new byte[]{-128, 0, 127}), first.hashCode());
|
||||
assertFalse(first.equals(null));
|
||||
assertFalse(first.equals(new byte[]{-128, 0, 127}));
|
||||
assertNotEquals(first, ByteArrayKey.copyOf(new byte[]{-128, 0}));
|
||||
assertEquals(ByteArrayKey.copyOf(new byte[0]), ByteArrayKey.copyOf(new byte[0]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.io;
|
||||
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import javax.net.ssl.CertPathTrustManagerParameters;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.SSLServerSocket;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
import javax.net.ssl.X509KeyManager;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.Security;
|
||||
import java.security.cert.CertPathValidatorException;
|
||||
import java.security.cert.CertStore;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.CollectionCertStoreParameters;
|
||||
import java.security.cert.PKIXBuilderParameters;
|
||||
import java.security.cert.PKIXCertPathChecker;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/// Exercises compromised and independent test certificates with offline PKIX validation.
|
||||
///
|
||||
/// `test-chain.pem` contains a locally generated leaf, intermediate, and self-signed root,
|
||||
/// in that order, without private keys. The certificates use EC `secp256r1` keys and were
|
||||
/// generated with `keytool`, a start date of `2026/01/01 00:00:00`, and a validity of 3650 days.
|
||||
/// The leaf permits server and client authentication; both CAs permit certificate signing.
|
||||
///
|
||||
/// The compromised certificates also match the original Mozilla report attachments:
|
||||
/// [original issuances](https://bugzilla.mozilla.org/attachment.cgi?id=9664066) and
|
||||
/// [cross-signs](https://bugzilla.mozilla.org/attachment.cgi?id=9664067).
|
||||
///
|
||||
/// Public certificate fixtures come from the following pinned source files.
|
||||
/// SHA-256 digests refer to the original downloaded bytes.
|
||||
///
|
||||
/// | Source | Local fixtures | SHA-256 |
|
||||
/// | --- | --- | --- |
|
||||
/// | [impacted-intermediates.json](https://github.com/galadran/we-have-a-problem.com/blob/e2786ff8b707b4951f0db1f83ccaf8ac9af2f7e6/impacted-intermediates.json) | `compromised.pem` | `f67b3abe9b4879f4e91874ed041beccd71257f1db41c588c724ccf12aa90c59e` |
|
||||
/// | [roots.json](https://github.com/galadran/we-have-a-problem.com/blob/e2786ff8b707b4951f0db1f83ccaf8ac9af2f7e6/roots.json) | `roots.pem` | `119f32cd81a6b5a4e3c4c2df5fe07daa90dc43cafa123a62f0394f1895d92380` |
|
||||
/// | [chains.zip](https://github.com/galadran/we-have-a-problem.com/blob/e2786ff8b707b4951f0db1f83ccaf8ac9af2f7e6/chains.zip) | The 12 TLS chain PEM files | `8a2edc81b6c51e3ad27d45ca1e441ec84c961e2dee0fc547f2a02c0f74315f95` |
|
||||
@NotNullByDefault
|
||||
class TLSSecurityTest {
|
||||
/// Loads an ephemeral client identity and fails closed for invalid key store settings.
|
||||
@Test
|
||||
void loadsConfiguredClientCredentials(@TempDir Path directory) throws Exception {
|
||||
SSLContext previousContext = SSLContext.getDefault();
|
||||
SSLSocketFactory previousFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
|
||||
Path storePath = directory.resolve("client.p12");
|
||||
Path output = directory.resolve("keytool.log");
|
||||
String executable = System.getProperty("os.name").startsWith("Windows") ? "keytool.exe" : "keytool";
|
||||
Process process = new ProcessBuilder(Path.of(System.getProperty("java.home"), "bin", executable).toString(),
|
||||
"-genkeypair", "-alias", "client", "-keyalg", "EC", "-groupname", "secp256r1",
|
||||
"-dname", "CN=TLS test client", "-validity", "1", "-storetype", "PKCS12",
|
||||
"-keystore", storePath.toString(), "-storepass", "test-password", "-keypass", "test-password")
|
||||
.redirectErrorStream(true).redirectOutput(output.toFile()).start();
|
||||
try {
|
||||
assertTrue(process.waitFor(30, TimeUnit.SECONDS), "Keytool timed out");
|
||||
assertEquals(0, process.exitValue(), new String(Files.readAllBytes(output),
|
||||
Charset.forName(System.getProperty("native.encoding", "UTF-8"))));
|
||||
} finally {
|
||||
process.destroyForcibly();
|
||||
}
|
||||
|
||||
String previousAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
|
||||
Map<String, @Nullable String> previous = new HashMap<>();
|
||||
for (String store : List.of("keyStore", "trustStore")) {
|
||||
for (String suffix : List.of("", "Type", "Provider", "Password")) {
|
||||
String property = "javax.net.ssl." + store + suffix;
|
||||
previous.put(property, System.getProperty(property));
|
||||
}
|
||||
}
|
||||
try {
|
||||
System.setProperty("javax.net.ssl.keyStore", storePath.toString());
|
||||
System.setProperty("javax.net.ssl.keyStoreType", "PKCS12");
|
||||
System.setProperty("javax.net.ssl.keyStoreProvider", "SUN");
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", "test-password");
|
||||
var keyManagers = TLSSecurity.createKeyManagers();
|
||||
SSLContext serverContext = SSLContext.getInstance("TLS");
|
||||
serverContext.init(keyManagers, null, null);
|
||||
X509KeyManager manager = (X509KeyManager) keyManagers[0];
|
||||
@Nullable String alias = manager.chooseClientAlias(new String[]{"EC"}, null, null);
|
||||
assertNotNull(alias);
|
||||
assertNotNull(manager.getPrivateKey(alias));
|
||||
assertEquals("CN=TLS test client", manager.getCertificateChain(alias)[0].getSubjectX500Principal().getName());
|
||||
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", "wrong-password");
|
||||
assertThrows(IOException.class, TLSSecurity::createKeyManagers);
|
||||
assertDoesNotThrow(TLSSecurity::install);
|
||||
assertNotSame(previousContext, SSLContext.getDefault());
|
||||
assertNotSame(previousFactory, HttpsURLConnection.getDefaultSSLSocketFactory());
|
||||
assertRejectsHandshake(serverContext);
|
||||
|
||||
System.setProperty("javax.net.ssl.keyStore", directory.resolve("missing.p12").toString());
|
||||
assertThrows(IOException.class, TLSSecurity::createKeyManagers);
|
||||
assertDoesNotThrow(TLSSecurity::install);
|
||||
assertRejectsHandshake(serverContext);
|
||||
System.setProperty("javax.net.ssl.keyStoreType", "PKCS11");
|
||||
assertThrows(GeneralSecurityException.class, TLSSecurity::createKeyManagers);
|
||||
|
||||
System.setProperty("javax.net.ssl.keyStoreType", "PKCS12");
|
||||
System.clearProperty("javax.net.ssl.keyStorePassword");
|
||||
for (String path : List.of("", "NONE")) {
|
||||
System.setProperty("javax.net.ssl.keyStore", path);
|
||||
X509KeyManager empty = (X509KeyManager) TLSSecurity.createKeyManagers()[0];
|
||||
assertNull(empty.chooseClientAlias(new String[]{"EC"}, null, null));
|
||||
}
|
||||
Security.setProperty("ssl.TrustManagerFactory.algorithm", "SunX509");
|
||||
assertDoesNotThrow(TLSSecurity::install);
|
||||
assertRejectsHandshake(serverContext);
|
||||
Security.setProperty("ssl.TrustManagerFactory.algorithm", previousAlgorithm);
|
||||
|
||||
Path emptyStore = directory.resolve("empty.p12");
|
||||
try (var outputStream = Files.newOutputStream(emptyStore)) {
|
||||
trustStore().store(outputStream, "test-password".toCharArray());
|
||||
}
|
||||
System.setProperty("javax.net.ssl.trustStore", emptyStore.toString());
|
||||
System.setProperty("javax.net.ssl.trustStoreType", KeyStore.getDefaultType());
|
||||
System.setProperty("javax.net.ssl.trustStoreProvider", "");
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", "test-password");
|
||||
assertDoesNotThrow(TLSSecurity::install);
|
||||
assertRejectsHandshake(serverContext);
|
||||
} finally {
|
||||
Security.setProperty("ssl.TrustManagerFactory.algorithm", previousAlgorithm);
|
||||
SSLContext.setDefault(previousContext);
|
||||
HttpsURLConnection.setDefaultSSLSocketFactory(previousFactory);
|
||||
previous.forEach((property, value) -> {
|
||||
if (value == null)
|
||||
System.clearProperty(property);
|
||||
else
|
||||
System.setProperty(property, value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Confirms that the installed default context refuses a real local TLS handshake.
|
||||
private static void assertRejectsHandshake(SSLContext serverContext) throws Exception {
|
||||
var executor = Executors.newSingleThreadExecutor();
|
||||
try (SSLServerSocket server = (SSLServerSocket) serverContext.getServerSocketFactory().createServerSocket(0)) {
|
||||
server.setSoTimeout(5000);
|
||||
Future<?> handshake = executor.submit(() -> {
|
||||
try (SSLSocket socket = (SSLSocket) server.accept()) {
|
||||
socket.setSoTimeout(5000);
|
||||
socket.startHandshake();
|
||||
} catch (IOException expected) {
|
||||
// The client aborts when the rejecting trust manager is invoked.
|
||||
}
|
||||
});
|
||||
try (SSLSocket client = (SSLSocket) SSLContext.getDefault().getSocketFactory()
|
||||
.createSocket("localhost", server.getLocalPort())) {
|
||||
client.setSoTimeout(5000);
|
||||
SSLException failure = assertThrows(SSLException.class, client::startHandshake);
|
||||
assertTrue(failure.getMessage().contains("TLS configuration failed"), failure.toString());
|
||||
}
|
||||
handshake.get(10, TimeUnit.SECONDS);
|
||||
} finally {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads an independent array of public certificates from a bundled PEM fixture.
|
||||
private static X509Certificate[] readCertificates(String name) throws Exception {
|
||||
try (InputStream input = Objects.requireNonNull(TLSSecurityTest.class.getResourceAsStream("tls/" + name))) {
|
||||
return CertificateFactory.getInstance("X.509").generateCertificates(input).toArray(X509Certificate[]::new);
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a trust store containing exactly the supplied certificates.
|
||||
private static KeyStore trustStore(X509Certificate... certificates) throws Exception {
|
||||
KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
store.load(null, null);
|
||||
for (int i = 0; i < certificates.length; i++)
|
||||
store.setCertificateEntry("certificate-" + i, certificates[i]);
|
||||
return store;
|
||||
}
|
||||
|
||||
/// Creates an extended trust manager at a fixed date within the fixtures' validity periods.
|
||||
private static X509ExtendedTrustManager trustManager(PKIXBuilderParameters parameters) throws Exception {
|
||||
assertEquals(5, parameters.getMaxPathLength());
|
||||
parameters.setDate(Date.from(Instant.parse("2026-10-22T00:00:00Z")));
|
||||
parameters.setRevocationEnabled(false);
|
||||
TrustManagerFactory factory = TrustManagerFactory.getInstance("PKIX");
|
||||
factory.init(new CertPathTrustManagerParameters(parameters));
|
||||
return (X509ExtendedTrustManager) factory.getTrustManagers()[0];
|
||||
}
|
||||
|
||||
/// Returns an initialized checker from the application's PKIX configuration.
|
||||
private static PKIXCertPathChecker newChecker() throws Exception {
|
||||
PKIXCertPathChecker checker = TLSSecurity.createParameters(trustStore(readCertificates("roots.pem")))
|
||||
.getCertPathCheckers().get(0);
|
||||
checker.init(false);
|
||||
return checker;
|
||||
}
|
||||
|
||||
/// Covers all eight keys and their cross-signed certificates, including code-signing CAs.
|
||||
@Test
|
||||
void blocksAllPublishedCertificates() throws Exception {
|
||||
X509Certificate[] certificates = readCertificates("compromised.pem");
|
||||
assertEquals(16, certificates.length);
|
||||
PKIXCertPathChecker checker = newChecker();
|
||||
Set<String> fingerprints = new HashSet<>();
|
||||
for (X509Certificate certificate : certificates) {
|
||||
fingerprints.add(HexFormat.of().withUpperCase().formatHex(
|
||||
MessageDigest.getInstance("SHA-256").digest(certificate.getPublicKey().getEncoded())));
|
||||
assertThrows(CertPathValidatorException.class, () -> checker.check(certificate),
|
||||
certificate.getSubjectX500Principal().toString());
|
||||
}
|
||||
assertEquals(Set.of(
|
||||
"EBED6475C2C2D80FCB199F3FE8DD6E6B3CE60B77EB15D4E42C3DEC0C6F5B5105",
|
||||
"43BFFA19D6EF651451F6841E019931E96F6F2EAEFF28AB4B6F1CE4576DE2717B",
|
||||
"E0254C7FCDC265E2F0F8A428DABB75AB4AE039B4872D62F5EE57F0877D5A8A41",
|
||||
"49DD353DC2B919A3770645117D0E60C16115E7D1BC259EEFE734B00EA434FE5F",
|
||||
"63ECEFEE431D49523BC1136C4E72D0206A477086CAF606CD5CF025686C109CF8",
|
||||
"5C0660A2B164910D8FF589C18E14552E9E0EC237DCEFE2CB736D41A4811F81A0",
|
||||
"EC4FFA1AD0AC88081314B7AAFFEF6A91F97CD57291BB5BA686C5415F5EB4F6B6",
|
||||
"92C063D67E8F14DB71D288E11266FFDAF69CBF115F7BAB14F3BC2F68D25E0E30"
|
||||
), fingerprints);
|
||||
}
|
||||
|
||||
/// Matches every certificate to the full-certificate fingerprints in Mozilla's disclosure.
|
||||
/// Source: [Mozilla disclosure](https://bugzilla.mozilla.org/attachment.cgi?id=9664786).
|
||||
@Test
|
||||
void matchesMozillaDisclosure() throws Exception {
|
||||
Set<String> fingerprints = new HashSet<>();
|
||||
for (X509Certificate certificate : readCertificates("compromised.pem")) {
|
||||
fingerprints.add(HexFormat.of().withUpperCase().formatHex(
|
||||
MessageDigest.getInstance("SHA-256").digest(certificate.getEncoded())));
|
||||
}
|
||||
assertEquals(Set.of(
|
||||
"E7568FE6E12331F4FD7DE61DA4C7559D0F8EB192A218D4741A3BA75184333838",
|
||||
"98B896625C0C230ADEEC3EB2CF6AAD5001ECF9156A68644E82374CE5833BC25B",
|
||||
"42C4ACCD12C4F59597925E1A08A7A90102608543809308D50EC427E118BA1493",
|
||||
"432EC25F0D28DE9006D97BF752F17BBB2CD41F669933FCB072D9A1148D0261F3",
|
||||
"CB6E15165AC802E9B3F993F16C25D41F4E81C21EBDE71AE1B012AC00BF9C7C3F",
|
||||
"0164D7CEAE5BB909C272AF151FF44B1177AE9B47D9424B94F1D9558C30934D19",
|
||||
"9E39DE2881273ACC225A149C72BA3E0F51424963669AB633EE1DC7DF5DB067B4",
|
||||
"4B2460A466F95FD40113BCDCCA2CEB29B3B093921DE45474DA7DF48874C630B1",
|
||||
"579480AEA166290BD3C643589E795FC0D0E54E86505D180F1DFF99EFD3199512",
|
||||
"5F1DED4DD590AF74631290CF073B122A2AAC6A1FA479778A0BD43579BD832699",
|
||||
"45A883F349AEDFD5F3A649B5498FFB121712BA8B448F3F2A91DBC5A766FCDDBE",
|
||||
"FD9D5C2DBBD4995AB69B8D50C439D9527B00529199C2FD01B2174EB58F07021F",
|
||||
"7485B51C0ED192878FA1D2F948FB529AE1842CC5086C4B67F7BE5B2039DE9489",
|
||||
"62B7F9868263F72F6911FC78CEAF72C1979158B7368BF648683BB2AA39B02E72",
|
||||
"A496E23AC0CAB8155FC9B117B97EAE368EFCBB9041AEC8804D5A308D2D01C2D8",
|
||||
"84112972B8E59D14560AE7E7119654474AAD119990CF3722BFC1E3B91615E1FD"
|
||||
), fingerprints);
|
||||
}
|
||||
|
||||
/// Rejects published TLS paths for both signing roots, including on JDKs that also block them.
|
||||
@Test
|
||||
void rejectsAllPublishedTlsChains() throws Exception {
|
||||
for (String type : List.of("dv", "ov", "ev")) {
|
||||
for (String generation : List.of("g3b", "g3c")) {
|
||||
for (String crossSign : List.of("", "-aaa")) {
|
||||
String name = type + "-" + generation + crossSign + "-emudhra.we-have-a-problem.com.pem";
|
||||
X509Certificate[] chain = readCertificates(name);
|
||||
PKIXBuilderParameters parameters = TLSSecurity.createParameters(trustStore(readCertificates("roots.pem")));
|
||||
X509ExtendedTrustManager protectedManager = trustManager(parameters);
|
||||
assertThrows(CertificateException.class, () -> protectedManager.checkServerTrusted(chain, "ECDHE_ECDSA"), name);
|
||||
assertThrows(CertificateException.class, () -> protectedManager.checkServerTrusted(chain, "ECDHE_ECDSA", (Socket) null), name);
|
||||
assertThrows(CertificateException.class, () -> protectedManager.checkServerTrusted(chain, "ECDHE_ECDSA", (SSLEngine) null), name);
|
||||
assertThrows(CertificateException.class, () -> protectedManager.checkClientTrusted(chain, "EC"), name);
|
||||
|
||||
// JDK blocklists may independently reject these compromised certificates.
|
||||
// Check HMCL's checker directly so JDK rejection cannot mask a missing key.
|
||||
assertThrows(CertPathValidatorException.class, () -> newChecker().check(chain[1]), name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes compromised certificates even when explicitly installed as trust anchors.
|
||||
@Test
|
||||
void filtersCompromisedTrustAnchors() throws Exception {
|
||||
X509Certificate[] compromised = readCertificates("compromised.pem");
|
||||
X509Certificate root = readCertificates("roots.pem")[2];
|
||||
KeyStore store = trustStore(compromised);
|
||||
store.setCertificateEntry("unaffected-root", root);
|
||||
PKIXBuilderParameters parameters = TLSSecurity.createParameters(store);
|
||||
assertEquals(1, parameters.getTrustAnchors().size());
|
||||
assertEquals(root, parameters.getTrustAnchors().iterator().next().getTrustedCert());
|
||||
assertDoesNotThrow(() -> trustManager(parameters).checkServerTrusted(new X509Certificate[]{root}, "RSA"));
|
||||
assertThrows(InvalidAlgorithmParameterException.class, () -> TLSSecurity.createParameters(trustStore(compromised)));
|
||||
}
|
||||
|
||||
/// Checks intermediates supplied by the path builder rather than by the peer.
|
||||
@Test
|
||||
void rejectsIntermediateFromCertStore() throws Exception {
|
||||
X509Certificate[] chain = readCertificates("dv-g3b-aaa-emudhra.we-have-a-problem.com.pem");
|
||||
PKIXBuilderParameters parameters = TLSSecurity.createParameters(trustStore(readCertificates("roots.pem")));
|
||||
parameters.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(List.of(chain))));
|
||||
X509Certificate[] leaf = {chain[0]};
|
||||
assertThrows(CertificateException.class, () -> trustManager(parameters).checkServerTrusted(leaf, "ECDHE_ECDSA"));
|
||||
}
|
||||
|
||||
/// Accepts an independent test chain with peer-supplied or locally stored intermediates.
|
||||
/// Also verifies that a missing intermediate still prevents path construction.
|
||||
@Test
|
||||
void acceptsUnblockedTlsChain() throws Exception {
|
||||
X509Certificate[] chain = readCertificates("test-chain.pem");
|
||||
assertEquals(3, chain.length);
|
||||
PKIXBuilderParameters parameters = TLSSecurity.createParameters(trustStore(chain[2]));
|
||||
X509ExtendedTrustManager manager = trustManager(parameters);
|
||||
assertDoesNotThrow(() -> manager.checkServerTrusted(chain, "ECDHE_ECDSA"));
|
||||
assertDoesNotThrow(() -> manager.checkServerTrusted(chain, "ECDHE_ECDSA", (Socket) null));
|
||||
assertDoesNotThrow(() -> manager.checkServerTrusted(chain, "ECDHE_ECDSA", (SSLEngine) null));
|
||||
assertDoesNotThrow(() -> manager.checkClientTrusted(chain, "EC"));
|
||||
|
||||
X509Certificate[] leaf = {chain[0]};
|
||||
assertThrows(CertificateException.class, () -> manager.checkServerTrusted(leaf, "ECDHE_ECDSA"));
|
||||
parameters.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(List.of(chain[1]))));
|
||||
assertDoesNotThrow(() -> trustManager(parameters).checkServerTrusted(leaf, "ECDHE_ECDSA"));
|
||||
}
|
||||
|
||||
/// Exercises concurrent checks on a shared instance and its shallow clones.
|
||||
@Test
|
||||
void supportsConcurrentChecksAndClones() throws Exception {
|
||||
X509Certificate[] blocked = readCertificates("compromised.pem");
|
||||
X509Certificate[] roots = readCertificates("roots.pem");
|
||||
PKIXCertPathChecker original = newChecker();
|
||||
original.check(roots[0]);
|
||||
var executor = Executors.newFixedThreadPool(4);
|
||||
try {
|
||||
List<Future<?>> tasks = new ArrayList<>();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
PKIXCertPathChecker checker = i % 2 == 0 ? original : (PKIXCertPathChecker) original.clone();
|
||||
tasks.add(executor.submit(() -> {
|
||||
for (int iteration = 0; iteration < 100; iteration++) {
|
||||
X509Certificate root = roots[iteration % roots.length];
|
||||
for (X509Certificate certificate : blocked) {
|
||||
assertThrows(CertPathValidatorException.class, () -> checker.check(certificate));
|
||||
assertDoesNotThrow(() -> checker.check(root));
|
||||
}
|
||||
assertDoesNotThrow(() -> checker.init(true));
|
||||
}
|
||||
}));
|
||||
}
|
||||
for (Future<?> task : tasks)
|
||||
task.get();
|
||||
assertDoesNotThrow(() -> original.check(roots[0]));
|
||||
assertThrows(CertPathValidatorException.class, () -> original.check(blocked[0]));
|
||||
} finally {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
/// Installs both process defaults and restores them after the assertion.
|
||||
@Test
|
||||
void installsProcessDefaults() throws Exception {
|
||||
SSLContext previousContext = SSLContext.getDefault();
|
||||
SSLSocketFactory previousFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
|
||||
try {
|
||||
TLSSecurity.install();
|
||||
assertNotSame(previousContext, SSLContext.getDefault());
|
||||
assertNotSame(previousFactory, HttpsURLConnection.getDefaultSSLSocketFactory());
|
||||
} finally {
|
||||
SSLContext.setDefault(previousContext);
|
||||
HttpsURLConnection.setDefaultSSLSocketFactory(previousFactory);
|
||||
}
|
||||
}
|
||||
|
||||
/// Keeps ordinary validation active for a leaf that does not contain a blocked key.
|
||||
@Test
|
||||
void rejectsUntrustedLeafWithoutIntermediate() throws Exception {
|
||||
X509Certificate[] chain = readCertificates("dv-g3b-aaa-emudhra.we-have-a-problem.com.pem");
|
||||
assertDoesNotThrow(() -> newChecker().check(chain[0]));
|
||||
X509ExtendedTrustManager manager = trustManager(TLSSecurity.createParameters(trustStore(readCertificates("roots.pem"))));
|
||||
assertThrows(CertificateException.class, () -> manager.checkServerTrusted(new X509Certificate[]{chain[0]}, "ECDHE_ECDSA"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDaDCCAu6gAwIBAgIQAKSu5njyM2K0wORgt3LyFDAKBggqhkjOPQQDAzBWMQsw
|
||||
CQYDVQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl
|
||||
ZDEgMB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwHhcNMjUwNDMwMTU1
|
||||
NDA4WhcNNDAwNDI4MTgzMDAwWjA/MQswCQYDVQQGEwJOTDETMBEGA1UECgwKZU11
|
||||
ZGhyYSBCVjEbMBkGA1UEAwwSRU0gRFYgVExTIENBIC0gRzNCMHYwEAYHKoZIzj0C
|
||||
AQYFK4EEACIDYgAE557ZwEqXc8RzXlERHahawr1eZKs3S4DhLLwgOOweTw47gS/J
|
||||
hZ4eZ6+wTi+LyX90hbplqpEe6KTkW2LVzAPVa+Z4s8OMtNyTfKJGDiOEU8iSk10p
|
||||
1E+R4VYGtjzO2rRoo4IBljCCAZIwHwYDVR0jBBgwFoAU4TqrIXzx401UrtRyt/2L
|
||||
WUIgkFQwHQYDVR0OBBYEFOIeL+ccGTkOVUtiwSAkVzUkiiUUMB0GA1UdJQQWMBQG
|
||||
CCsGAQUFBwMBBggrBgEFBQcDAjBdBgNVHSAEVjBUMDkGCysGAQQBg44hAQABMCow
|
||||
KAYIKwYBBQUHAgEWHGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDQYLKwYB
|
||||
BAGDjiEBAmQwCAYGZ4EMAQIBMHUGCCsGAQUFBwEBBGkwZzAkBggrBgEFBQcwAYYY
|
||||
aHR0cDovL29jc3AtYi5lbXNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVw
|
||||
b3NpdG9yeS5lbXNpZ24uY29tL2NlcnRzL0VNRFZUTFNDQUczQi5wN2MwNwYDVR0f
|
||||
BDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290dGxzY2FnMy5j
|
||||
cmwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0E
|
||||
AwMDaAAwZQIwLmsGXi6oFMJ2klkcEMXMug6gvxQWZWkewiDWVzQyl2L6z6DuQfPO
|
||||
Ibh1grvdULDCAjEAxZvLlxUavG69J6wnSjC8XBbqNIQsJ6CE2QdNOe84Ptxb6VG8
|
||||
gt2TlfJbHii+KNvK
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzjCCAragAwIBAgIRAKAk/fS6Zlx4XOSgIEg4WLcwDQYJKoZIhvcNAQELBQAw
|
||||
ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
|
||||
BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y
|
||||
ODEyMzEyMzU5NTlaMD8xCzAJBgNVBAYTAk5MMRMwEQYDVQQKDAplTXVkaHJhIEJW
|
||||
MRswGQYDVQQDDBJFTSBEViBUTFMgQ0EgLSBHM0IwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||
IgNiAATnntnASpdzxHNeUREdqFrCvV5kqzdLgOEsvCA47B5PDjuBL8mFnh5nr7BO
|
||||
L4vJf3SFumWqkR7opORbYtXMA9Vr5nizw4y03JN8okYOI4RTyJKTXSnUT5HhVga2
|
||||
PM7atGijggE1MIIBMTAfBgNVHSMEGDAWgBSgEQojPpbxB+zirynvgqV/0DCktDAd
|
||||
BgNVHQ4EFgQU4h4v5xwZOQ5VS2LBICRXNSSKJRQwDgYDVR0PAQH/BAQDAgEGMBIG
|
||||
A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
|
||||
MDEGA1UdIAQqMCgwCAYGZ4EMAQIBMA0GCysGAQQBg44hAQABMA0GCysGAQQBg44h
|
||||
AQJkMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0FB
|
||||
QUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEF
|
||||
BQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBCwUAA4IB
|
||||
AQBzQ1IoqpdotM6gFrR4Q3YrKifOLeaj5XXunldpVE/vZn2TxC2y0s8oYiLR5ICE
|
||||
wqVXSB7N6BM1Mp+Nif7Jbhb2IswAfSR4Iik8+B0iRmi7lYoNlycCgHjmsjOmZaxM
|
||||
QkPmjXAJbG5f4V41OsvJZmS7lI3r0IuvEDVX3GAZ13on6IImSs9ybNpHQ7NCqkMQ
|
||||
7wH4uAO1dtpFycgJSdHaCOhHjrox2BTYGLdpUrYwY3EOAjlZ8UGgsxs7mG+xquMl
|
||||
bN1LFURwfnkVMP3T/DBIZ/ys2+lNFyxK3FlArrV4/000uTnVB1lO8oGORYdXoT5V
|
||||
GUyRCEIDhF5TBUuKF4E3kf1h
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDZjCCAu2gAwIBAgIPbK2jZ/scEACIEg6iAtVoMAoGCCqGSM49BAMDMFYxCzAJ
|
||||
BgNVBAYTAklOMSUwIwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVk
|
||||
MSAwHgYDVQQDDBdlbVNpZ24gUm9vdCBUTFMgQ0EgLSBHMzAeFw0yNTA0MzAxNjAw
|
||||
MTJaFw00MDA0MjgxODMwMDBaMD8xCzAJBgNVBAYTAk5MMRMwEQYDVQQKDAplTXVk
|
||||
aHJhIEJWMRswGQYDVQQDDBJFTSBPViBUTFMgQ0EgLSBHM0IwdjAQBgcqhkjOPQIB
|
||||
BgUrgQQAIgNiAARziXqg6zz9yosSQC/68KvE4SErr43ud4rJ7i1tuD+sKAxeBWOK
|
||||
56Ks1xwyKo42HN659pxDArndjXPRzGyqsswK7k4hbOl53VGKSvS2JuVoTN1lgDft
|
||||
DIVOTMEvTPhkbQ2jggGWMIIBkjAfBgNVHSMEGDAWgBThOqshfPHjTVSu1HK3/YtZ
|
||||
QiCQVDAdBgNVHQ4EFgQUm9hlUa/n5yw3rQjuaRnZvzeErgowHQYDVR0lBBYwFAYI
|
||||
KwYBBQUHAwEGCCsGAQUFBwMCMF0GA1UdIARWMFQwOQYLKwYBBAGDjiEBAAEwKjAo
|
||||
BggrBgEFBQcCARYcaHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbTANBgsrBgEE
|
||||
AYOOIQECbjAIBgZngQwBAgIwdQYIKwYBBQUHAQEEaTBnMCQGCCsGAQUFBzABhhho
|
||||
dHRwOi8vb2NzcC1iLmVtc2lnbi5jb20wPwYIKwYBBQUHMAKGM2h0dHA6Ly9yZXBv
|
||||
c2l0b3J5LmVtc2lnbi5jb20vY2VydHMvRU1PVlRMU0NBRzNCLnA3YzA3BgNVHR8E
|
||||
MDAuMCygKqAohiZodHRwOi8vY3JsLmVtc2lnbi5jb20vP3Jvb3R0bHNjYWczLmNy
|
||||
bDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAKBggqhkjOPQQD
|
||||
AwNnADBkAjBAk+5BizdJhrED3wb7F2Gm7bWI60dlNd075nghT2TrR4NWKZtJZcbc
|
||||
28xxIY/NONICMF6mPdsjosaqmvEMWqKcBh4vsIvXSKnQj5o8zrFB4oKS+accjYTe
|
||||
8Dz3bQjauvy9uw==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzjCCAragAwIBAgIRAOlCI73pL5FOWrmsq/2sNxAwDQYJKoZIhvcNAQELBQAw
|
||||
ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
|
||||
BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y
|
||||
ODEyMzEyMzU5NTlaMD8xCzAJBgNVBAYTAk5MMRMwEQYDVQQKDAplTXVkaHJhIEJW
|
||||
MRswGQYDVQQDDBJFTSBPViBUTFMgQ0EgLSBHM0IwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||
IgNiAARziXqg6zz9yosSQC/68KvE4SErr43ud4rJ7i1tuD+sKAxeBWOK56Ks1xwy
|
||||
Ko42HN659pxDArndjXPRzGyqsswK7k4hbOl53VGKSvS2JuVoTN1lgDftDIVOTMEv
|
||||
TPhkbQ2jggE1MIIBMTAfBgNVHSMEGDAWgBSgEQojPpbxB+zirynvgqV/0DCktDAd
|
||||
BgNVHQ4EFgQUm9hlUa/n5yw3rQjuaRnZvzeErgowDgYDVR0PAQH/BAQDAgEGMBIG
|
||||
A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
|
||||
MDEGA1UdIAQqMCgwCAYGZ4EMAQICMA0GCysGAQQBg44hAQABMA0GCysGAQQBg44h
|
||||
AQJuMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0FB
|
||||
QUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEF
|
||||
BQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBCwUAA4IB
|
||||
AQALRoOSvkzRa+tN5Rje9W5GiXd/qsicd9N6Gnh0SWC7VyWH+HycIqAnFCMYZznj
|
||||
CeGusomFYHE42SHWr85JDIbgzy6y3dO4i9n9w0Bx7yrXWBbDzFS8VgA3+EY4Cg7y
|
||||
mtoC3RPOJVtaXXPZ+68X804g1Mj+3vCSHj2lNPVgco0OzSIOJ/KMg1R9MLBkiKzb
|
||||
bERAGZHejY0HBhYd76IINZN8z6bBNah2UdFYxsOisLynPQUkol6gRLum024/FzVl
|
||||
CCDfnUusrkr+mAfPAvD/Vb8bhpA7jYtepuU+2mIdShj5lLlvAwZphn+ypWycuo2x
|
||||
0k9mldz2dqI9nhCiY+TTIOOR
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDaDCCAu2gAwIBAgIQAMfEHv6S2CXwXq6EGkqCwTAKBggqhkjOPQQDAzBWMQsw
|
||||
CQYDVQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl
|
||||
ZDEgMB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwHhcNMjUwNDMwMTYw
|
||||
MzMxWhcNNDAwNDI4MTgzMDAwWjA/MQswCQYDVQQGEwJOTDETMBEGA1UECgwKZU11
|
||||
ZGhyYSBCVjEbMBkGA1UEAwwSRU0gRVYgVExTIENBIC0gRzNCMHYwEAYHKoZIzj0C
|
||||
AQYFK4EEACIDYgAEJduoWmTTl9oLEFLp6lRuSHvv6pydnWigxerc/u91ppu11vwB
|
||||
9Cv7ym60C81kwHZ+Nv/QXZeoE8WqDGyVMOec9fCGZGjxOAZCCqXIUYQLHiUlQOeQ
|
||||
5LZUw4Uu1krx6GV9o4IBlTCCAZEwHwYDVR0jBBgwFoAU4TqrIXzx401UrtRyt/2L
|
||||
WUIgkFQwHQYDVR0OBBYEFD7cINmJRpiHe4c/2IFHslwV2EcGMB0GA1UdJQQWMBQG
|
||||
CCsGAQUFBwMBBggrBgEFBQcDAjBcBgNVHSAEVTBTMDkGCysGAQQBg44hAQABMCow
|
||||
KAYIKwYBBQUHAgEWHGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDQYLKwYB
|
||||
BAGDjiEBAngwBwYFZ4EMAQEwdQYIKwYBBQUHAQEEaTBnMCQGCCsGAQUFBzABhhho
|
||||
dHRwOi8vb2NzcC1iLmVtc2lnbi5jb20wPwYIKwYBBQUHMAKGM2h0dHA6Ly9yZXBv
|
||||
c2l0b3J5LmVtc2lnbi5jb20vY2VydHMvRU1FVlRMU0NBRzNCLnA3YzA3BgNVHR8E
|
||||
MDAuMCygKqAohiZodHRwOi8vY3JsLmVtc2lnbi5jb20vP3Jvb3R0bHNjYWczLmNy
|
||||
bDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAKBggqhkjOPQQD
|
||||
AwNpADBmAjEA9Jq999oaOUKhFwopw33iCmt0OHIn3gjT43GfuOHR/YckXdwoNdDA
|
||||
lIEhz0NgRO3/AjEAvY/a3OMvktIZLYjn+WLKmp0QhrIMV+e6EpS7j4GVeOKouNp6
|
||||
MN/P+ByGeiRMghzB
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzTCCArWgAwIBAgIRANj4aF5PK9nYJAKoWhwetzEwDQYJKoZIhvcNAQELBQAw
|
||||
ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
|
||||
BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y
|
||||
ODEyMzEyMzU5NTlaMD8xCzAJBgNVBAYTAk5MMRMwEQYDVQQKDAplTXVkaHJhIEJW
|
||||
MRswGQYDVQQDDBJFTSBFViBUTFMgQ0EgLSBHM0IwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||
IgNiAAQl26haZNOX2gsQUunqVG5Ie+/qnJ2daKDF6tz+73Wmm7XW/AH0K/vKbrQL
|
||||
zWTAdn42/9Bdl6gTxaoMbJUw55z18IZkaPE4BkIKpchRhAseJSVA55DktlTDhS7W
|
||||
SvHoZX2jggE0MIIBMDAfBgNVHSMEGDAWgBSgEQojPpbxB+zirynvgqV/0DCktDAd
|
||||
BgNVHQ4EFgQUPtwg2YlGmId7hz/YgUeyXBXYRwYwDgYDVR0PAQH/BAQDAgEGMBIG
|
||||
A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
|
||||
MDAGA1UdIAQpMCcwBwYFZ4EMAQEwDQYLKwYBBAGDjiEBAAEwDQYLKwYBBAGDjiEB
|
||||
AngwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFB
|
||||
Q2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUF
|
||||
BzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQELBQADggEB
|
||||
AJNFuAFW9Gnw/8+kCO4KIlZdnM1Sh2u+8cK0EYAHhXpb5FlGvYkcARigo5oS7VoM
|
||||
7FOYlTueV6yICZKNAYawZPJPhNCGK+rp3cQBEz6d0n8OZEYZov9lb3ll8ORgSDuf
|
||||
FVIW6Gagg/UnOamLBTOR8eD5V73SCme/B2cHUDw+m5E8cc+0Ujzz3Cn3ex5JrhXk
|
||||
2T3s8PoVzEmhTgGboFfcWO1EYhPPrYwdxPYRQIXAXUXhLEOVNL9ASbZJLFI8E1IM
|
||||
m7NwxVN2s38jWpaCUaaF0tg7hfiy7oe13rzJfHsdqldJVTTUTPWVx8TC4ju0VLet
|
||||
bAlwhwFT6AP8uIVMGUHNw24=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDaDCCAu6gAwIBAgIPWIsuPCu4zaE+GhIJJUMhMAoGCCqGSM49BAMDMFYxCzAJ
|
||||
BgNVBAYTAklOMSUwIwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVk
|
||||
MSAwHgYDVQQDDBdlbVNpZ24gUm9vdCBUTFMgQ0EgLSBHMzAeFw0yNTA0MzAxNTM1
|
||||
MDlaFw00MDA0MjgxODMwMDBaMEAxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVk
|
||||
aHJhIEluYzEbMBkGA1UEAwwSRU0gRFYgVExTIENBIC0gRzNDMHYwEAYHKoZIzj0C
|
||||
AQYFK4EEACIDYgAEOnp4YsjETTJN3WkDOmeCwO/u/kBV5ooSP6A3G6O9kZRRTuFy
|
||||
pDPjYw8CvRiAN9nlFJ+bd9uUooHfmHsv6oiLdL3+j9pMEJnubKGVZv4+8OX2sJpV
|
||||
bMyLpQVh3xsqRB0Bo4IBljCCAZIwHwYDVR0jBBgwFoAU4TqrIXzx401UrtRyt/2L
|
||||
WUIgkFQwHQYDVR0OBBYEFK5+EFw8KZbBqEcnBVnx4D3U5QrzMB0GA1UdJQQWMBQG
|
||||
CCsGAQUFBwMBBggrBgEFBQcDAjBdBgNVHSAEVjBUMDkGCysGAQQBg44hAQABMCow
|
||||
KAYIKwYBBQUHAgEWHGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDQYLKwYB
|
||||
BAGDjiEBAmQwCAYGZ4EMAQIBMHUGCCsGAQUFBwEBBGkwZzAkBggrBgEFBQcwAYYY
|
||||
aHR0cDovL29jc3AtYy5lbXNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVw
|
||||
b3NpdG9yeS5lbXNpZ24uY29tL2NlcnRzL0VNRFZUTFNDQUczQy5wN2MwNwYDVR0f
|
||||
BDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290dGxzY2FnMy5j
|
||||
cmwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0E
|
||||
AwMDaAAwZQIxAMEN2HWMocbuWdy1d33pH6JpMvP/V/DIS5PtZbv1Q7TXEbxrcyxg
|
||||
yuddOJscYDJJlAIwB4sH1BpbScDzkdD4tJBSwrG/1Mx82vb8YaySehNuu1+1oXNd
|
||||
AEKwaJ0mVzScy2+6
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzzCCAregAwIBAgIRAJHzR/Glt9OZYrleDuTimdEwDQYJKoZIhvcNAQELBQAw
|
||||
ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
|
||||
BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y
|
||||
ODEyMzEyMzU5NTlaMEAxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVkaHJhIElu
|
||||
YzEbMBkGA1UEAwwSRU0gRFYgVExTIENBIC0gRzNDMHYwEAYHKoZIzj0CAQYFK4EE
|
||||
ACIDYgAEOnp4YsjETTJN3WkDOmeCwO/u/kBV5ooSP6A3G6O9kZRRTuFypDPjYw8C
|
||||
vRiAN9nlFJ+bd9uUooHfmHsv6oiLdL3+j9pMEJnubKGVZv4+8OX2sJpVbMyLpQVh
|
||||
3xsqRB0Bo4IBNTCCATEwHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQw
|
||||
HQYDVR0OBBYEFK5+EFw8KZbBqEcnBVnx4D3U5QrzMA4GA1UdDwEB/wQEAwIBBjAS
|
||||
BgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
|
||||
AjAxBgNVHSAEKjAoMAgGBmeBDAECATANBgsrBgEEAYOOIQEAATANBgsrBgEEAYOO
|
||||
IQECZDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9B
|
||||
QUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDA0BggrBgEFBQcBAQQoMCYwJAYIKwYB
|
||||
BQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG9w0BAQsFAAOC
|
||||
AQEAr7b6Mx11TIJgVzHt6EH5qGNWeiiqoMrp8E5/k8s+jvupx1n083pZ5XOzUzND
|
||||
wEUe0W4C4FIGUHNQ4XDwqOw0yK42xqm2dvCKgsko2UvDlT0haZVtTCeWMQOe5J/J
|
||||
ziaMfZwsZNfZ3aaI6XPtWDZtwY8AaNfyaeCpbtm5GcGnyNWk4jSNpbtpo6NjfXuP
|
||||
CFOm3rTjJ4zytP7Tso6iMm0L01cYFOlTmoPv4515R2r3SPnKDtijoB/D/vgxzojv
|
||||
DafgyG9afX8+laO9NbMMz4sqC1vZx3984i/sBNFiugm3Vvdq5OdJRjuGC1QILIs6
|
||||
dg/K3i8VQqRpbfaM9kIlMzZ3dQ==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDaDCCAu6gAwIBAgIPKWkmwiejoVJjkrGUgqUEMAoGCCqGSM49BAMDMFYxCzAJ
|
||||
BgNVBAYTAklOMSUwIwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVk
|
||||
MSAwHgYDVQQDDBdlbVNpZ24gUm9vdCBUTFMgQ0EgLSBHMzAeFw0yNTA0MzAxNTM3
|
||||
NDFaFw00MDA0MjgxODMwMDBaMEAxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVk
|
||||
aHJhIEluYzEbMBkGA1UEAwwSRU0gT1YgVExTIENBIC0gRzNDMHYwEAYHKoZIzj0C
|
||||
AQYFK4EEACIDYgAEE4JRzVKskpjByKrZdzId65fnCb0LTKCspV3IrVHc/J0ViaFZ
|
||||
fjpRIOHv1jHGPhg1ysrimGmmLhYx6KKBgatWYW3EXZGKvAnzqw5jz3kqpNztc4e+
|
||||
N7ulaVSfHAKycO1no4IBljCCAZIwHwYDVR0jBBgwFoAU4TqrIXzx401UrtRyt/2L
|
||||
WUIgkFQwHQYDVR0OBBYEFDTq6gzryQra+dDF4u2beMmHnhtsMB0GA1UdJQQWMBQG
|
||||
CCsGAQUFBwMBBggrBgEFBQcDAjBdBgNVHSAEVjBUMDkGCysGAQQBg44hAQABMCow
|
||||
KAYIKwYBBQUHAgEWHGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDQYLKwYB
|
||||
BAGDjiEBAm4wCAYGZ4EMAQICMHUGCCsGAQUFBwEBBGkwZzAkBggrBgEFBQcwAYYY
|
||||
aHR0cDovL29jc3AtYy5lbXNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVw
|
||||
b3NpdG9yeS5lbXNpZ24uY29tL2NlcnRzL0VNT1ZUTFNDQUczQy5wN2MwNwYDVR0f
|
||||
BDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290dGxzY2FnMy5j
|
||||
cmwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0E
|
||||
AwMDaAAwZQIxAMSZt2xsPeumRdeSMSQ3CbzIZeQ/q7GDCWfeE1Pq+mhgA6gRM44c
|
||||
NVF3xJ+wsxJk+wIwFJbaDMokD8Ei6eNOiA8yUMYvfzQyngmrhZQMdPCK1SewrR79
|
||||
4rSb9vGqxZzelQth
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzzCCAregAwIBAgIRAI1aSBGTnvRH13ERzdmEmukwDQYJKoZIhvcNAQELBQAw
|
||||
ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
|
||||
BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y
|
||||
ODEyMzEyMzU5NTlaMEAxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVkaHJhIElu
|
||||
YzEbMBkGA1UEAwwSRU0gT1YgVExTIENBIC0gRzNDMHYwEAYHKoZIzj0CAQYFK4EE
|
||||
ACIDYgAEE4JRzVKskpjByKrZdzId65fnCb0LTKCspV3IrVHc/J0ViaFZfjpRIOHv
|
||||
1jHGPhg1ysrimGmmLhYx6KKBgatWYW3EXZGKvAnzqw5jz3kqpNztc4e+N7ulaVSf
|
||||
HAKycO1no4IBNTCCATEwHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQw
|
||||
HQYDVR0OBBYEFDTq6gzryQra+dDF4u2beMmHnhtsMA4GA1UdDwEB/wQEAwIBBjAS
|
||||
BgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
|
||||
AjAxBgNVHSAEKjAoMAgGBmeBDAECAjANBgsrBgEEAYOOIQEAATANBgsrBgEEAYOO
|
||||
IQECbjBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9B
|
||||
QUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDA0BggrBgEFBQcBAQQoMCYwJAYIKwYB
|
||||
BQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG9w0BAQsFAAOC
|
||||
AQEAHlbhZpjhkPEAQVhPHRtBDaHqiisVD20pQ1TUeSx3l62Qe7NQ0b7vWQy7DN0i
|
||||
KOu7BGpP5EnIjvXbz9FhpfPJ4FhKEt3tQ51rqXz2oD8vRaryvExWPobDUz3j7HN6
|
||||
OzBsVMIe3PD7AAkpky0x8zMSi/ep5R9OMzybmNptbtppyuB5ytgq+h7lQdRIP+CQ
|
||||
TaJX3qTgfMqzntYre5GljmEB5hU2/rSn8zoSU2APkJidlZFpghtgbD4I2VRHZvCw
|
||||
FFr/SrFM9pwrgbJNZ1MtyTSVgI5NlNb0aMfLR+/r6jJt3ROeY2yh2QQ2dx+Yqgdb
|
||||
uL5ChFWOQU+ErSDnUq7yr+pjBQ==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDaDCCAu6gAwIBAgIQAIAJkopkEHYBrwX6LxnxCTAKBggqhkjOPQQDAzBWMQsw
|
||||
CQYDVQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl
|
||||
ZDEgMB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwHhcNMjUwNDMwMTUz
|
||||
OTMxWhcNNDAwNDI4MTgzMDAwWjBAMQswCQYDVQQGEwJVUzEUMBIGA1UECgwLZU11
|
||||
ZGhyYSBJbmMxGzAZBgNVBAMMEkVNIEVWIFRMUyBDQSAtIEczQzB2MBAGByqGSM49
|
||||
AgEGBSuBBAAiA2IABC1W6Cmm+4LGSkaX2WCvlR6QZ66R+SbTQwbaSKiq63hptqZ+
|
||||
bSdCbgRCqgOzbhmI3/Y2UU7hzwf1uks/8qBmRa59MdWGatMao9JG6wsxd+6Zy3Vm
|
||||
xRbOdICKZo+R8MR/yqOCAZUwggGRMB8GA1UdIwQYMBaAFOE6qyF88eNNVK7Ucrf9
|
||||
i1lCIJBUMB0GA1UdDgQWBBSmJV6AqU2385Z8G8eR35K87kvrXzAdBgNVHSUEFjAU
|
||||
BggrBgEFBQcDAQYIKwYBBQUHAwIwXAYDVR0gBFUwUzA5BgsrBgEEAYOOIQEAATAq
|
||||
MCgGCCsGAQUFBwIBFhxodHRwOi8vcmVwb3NpdG9yeS5lbXNpZ24uY29tMA0GCysG
|
||||
AQQBg44hAQJ4MAcGBWeBDAEBMHUGCCsGAQUFBwEBBGkwZzAkBggrBgEFBQcwAYYY
|
||||
aHR0cDovL29jc3AtYy5lbXNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVw
|
||||
b3NpdG9yeS5lbXNpZ24uY29tL2NlcnRzL0VNRVZUTFNDQUczQy5wN2MwNwYDVR0f
|
||||
BDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290dGxzY2FnMy5j
|
||||
cmwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0E
|
||||
AwMDaAAwZQIwBy1wTnzH4lXDTy2PD91ysLjvnlVN3JbJmc1BIMEU8TOLkYVHibMc
|
||||
9HeyonikFSfmAjEA6uE3QRxlrZTz/1/lXPTEMUhoauAtycagtlNFp79kjhvNa2/f
|
||||
Aal1GNxsOutLnr0Y
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzTCCArWgAwIBAgIQBfB4dS3s56G7hZXTAxZxcTANBgkqhkiG9w0BAQsFADB7
|
||||
MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD
|
||||
VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE
|
||||
AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTI1MDUxNDAwMDAwMFoXDTI4
|
||||
MTIzMTIzNTk1OVowQDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC2VNdWRocmEgSW5j
|
||||
MRswGQYDVQQDDBJFTSBFViBUTFMgQ0EgLSBHM0MwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||
IgNiAAQtVugppvuCxkpGl9lgr5UekGeukfkm00MG2kioqut4abamfm0nQm4EQqoD
|
||||
s24ZiN/2NlFO4c8H9bpLP/KgZkWufTHVhmrTGqPSRusLMXfumct1ZsUWznSAimaP
|
||||
kfDEf8qjggE0MIIBMDAfBgNVHSMEGDAWgBSgEQojPpbxB+zirynvgqV/0DCktDAd
|
||||
BgNVHQ4EFgQUpiVegKlNt/OWfBvHkd+SvO5L618wDgYDVR0PAQH/BAQDAgEGMBIG
|
||||
A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
|
||||
MDAGA1UdIAQpMCcwBwYFZ4EMAQEwDQYLKwYBBAGDjiEBAAEwDQYLKwYBBAGDjiEB
|
||||
AngwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFB
|
||||
Q2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUF
|
||||
BzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQELBQADggEB
|
||||
AIFfHiaMSjnrdZygRYV4ekRBmG7ybcFk4hGQWMMyMFi0cAEk2+dGfz5bl8IaIbLq
|
||||
z+uVJzhOgVEX7WqIfgbWfFsfPYHYjPHBmy7SlZ3BwA86IQ6qF/etpX2/qL9A5yRN
|
||||
N3nVowUpaqV6H3aCpkAh3wR2ZNkvAbLDvPz3J0l+sEWjWv+ZISOV4R+hXDTdPXV0
|
||||
1lKxnbwqEsdo/sE2QkCDKwFrZh/0uKjWspydJj+jSaDLTrGNx50Dgmn7VEcD1ZI5
|
||||
FXjknVUiO25YlmS1dtVZSBbyUKSjlrP/yDwPcZunoAsmCAAtCKsFLviLaymzvsKI
|
||||
eSSDGxpSySp2VCxompGDk8g=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDWzCCAuCgAwIBAgIPeBiTP4+FtGGaKzX7qD0KMAoGCCqGSM49BAMDMFUxCzAJ
|
||||
BgNVBAYTAklOMSUwIwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVk
|
||||
MR8wHQYDVQQDDBZlbVNpZ24gUm9vdCBDUyBDQSAtIEczMB4XDTI1MDQzMDE2MDgy
|
||||
N1oXDTQwMDQyODE4MzAwMFowPjELMAkGA1UEBhMCTkwxEzARBgNVBAoMCmVNdWRo
|
||||
cmEgQlYxGjAYBgNVBAMMEUVNIE9WIENTIENBIC0gRzNCMHYwEAYHKoZIzj0CAQYF
|
||||
K4EEACIDYgAEqrsPsGZqz88B+tUr/P1MK/LLPDSX3nOVDlgZth/mlQmHQcjEkUOb
|
||||
NKBDHK/+W/m0wcjorgAB+fTcitgsWfNjnpbNbD1NsM7+wmw9YlF7N3YTcnQ/jord
|
||||
d7EAZPKr5eBQo4IBizCCAYcwHwYDVR0jBBgwFoAUaHQD+IXKjVRQVx0W4MjeRQ2r
|
||||
zS0wHQYDVR0OBBYEFHn66OzP2w11jaRY1p+27dx8Sd7mMBMGA1UdJQQMMAoGCCsG
|
||||
AQUFBwMDMF4GA1UdIARXMFUwOQYLKwYBBAGDjiEBAAEwKjAoBggrBgEFBQcCARYc
|
||||
aHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbTAOBgwrBgEEAYOOIQECgUgwCAYG
|
||||
Z4EMAQQBMHQGCCsGAQUFBwEBBGgwZjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3At
|
||||
Yi5lbXNpZ24uY29tMD4GCCsGAQUFBzAChjJodHRwOi8vcmVwb3NpdG9yeS5lbXNp
|
||||
Z24uY29tL2NlcnRzL0VNT1ZDU0NBRzNCLnA3YzA2BgNVHR8ELzAtMCugKaAnhiVo
|
||||
dHRwOi8vY3JsLmVtc2lnbi5jb20vP3Jvb3Rjc2NhZzMuY3JsMA4GA1UdDwEB/wQE
|
||||
AwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMAoGCCqGSM49BAMDA2kAMGYCMQC/zr03
|
||||
YEELsNKqzUtH2dVFi4BaKFdyrr/6G1dIWp8KAVgsw2/u4lyyboUDaC9jDvICMQC9
|
||||
HTxku84tNX9dmHOtH7aC2Nu/JM0jTf6KjZAvovCJzzv1IFWSZpVLZibynMLXOrM=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDwzCCAqugAwIBAgIQQeMrc6wYXWzg10gLrPhOaTANBgkqhkiG9w0BAQsFADB7
|
||||
MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD
|
||||
VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE
|
||||
AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTI1MDUxNDAwMDAwMFoXDTI4
|
||||
MTIzMTIzNTk1OVowPjELMAkGA1UEBhMCTkwxEzARBgNVBAoMCmVNdWRocmEgQlYx
|
||||
GjAYBgNVBAMMEUVNIE9WIENTIENBIC0gRzNCMHYwEAYHKoZIzj0CAQYFK4EEACID
|
||||
YgAEqrsPsGZqz88B+tUr/P1MK/LLPDSX3nOVDlgZth/mlQmHQcjEkUObNKBDHK/+
|
||||
W/m0wcjorgAB+fTcitgsWfNjnpbNbD1NsM7+wmw9YlF7N3YTcnQ/jordd7EAZPKr
|
||||
5eBQo4IBLDCCASgwHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYD
|
||||
VR0OBBYEFHn66OzP2w11jaRY1p+27dx8Sd7mMA4GA1UdDwEB/wQEAwIBBjASBgNV
|
||||
HRMBAf8ECDAGAQH/AgEAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMDIGA1UdIAQrMCkw
|
||||
CAYGZ4EMAQQBMA0GCysGAQQBg44hAQABMA4GDCsGAQQBg44hAQKBSDBDBgNVHR8E
|
||||
PDA6MDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9BQUFDZXJ0aWZpY2F0
|
||||
ZVNlcnZpY2VzLmNybDA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6
|
||||
Ly9vY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAIh0OzWsdBYhY
|
||||
7+yxPGyosRL+uUSN6/Mrd30fBuougs5FVWQ3cRgLhImnUUgXADY9buugWhMmfT3c
|
||||
yU5T1MFXZXClCILm5mfg+oqm/ikUWJl3Ayf2GvYf2O++6vzqvkn418nI5Lpdt0O7
|
||||
Li524YS/3CZHDA+zZQ7rrHpZXgQ9Hmp8AUQ4FneaGjNanFoYxiZxwCyW5z4u7AoK
|
||||
0bmOVqKtcDFoVNz5++G9TYDJdhWOXmKKgzqRo1MK0PjazNT7MGHqknP93SqMVJ1g
|
||||
mX/qNWxYteAjlvFanQxBNX/+TRxDjuFsEHO/7BvqSpIIck0UEEfizhN8zNpWLbcl
|
||||
EqVgl0g39A==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDWzCCAuGgAwIBAgIPVEt/kiwxriIKSpIpFvbmMAoGCCqGSM49BAMDMFUxCzAJ
|
||||
BgNVBAYTAklOMSUwIwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVk
|
||||
MR8wHQYDVQQDDBZlbVNpZ24gUm9vdCBDUyBDQSAtIEczMB4XDTI1MDQzMDE2Mzg0
|
||||
MFoXDTQwMDQyODE4MzAwMFowPzELMAkGA1UEBhMCVVMxFDASBgNVBAoMC2VNdWRo
|
||||
cmEgSW5jMRowGAYDVQQDDBFFTSBPViBDUyBDQSAtIEczQzB2MBAGByqGSM49AgEG
|
||||
BSuBBAAiA2IABLnw4GNHAqacpqrdRDz9YsQbm6SyCSRpfXl052kaEm+5avs+NML9
|
||||
O8X0gwYfJl3CpYQ2NHMjUQQ2so8E84YjYwZ5gOJQPgJTLWVGI1pxZaJZoe9Jb3Qb
|
||||
X97neHII5Lj676OCAYswggGHMB8GA1UdIwQYMBaAFGh0A/iFyo1UUFcdFuDI3kUN
|
||||
q80tMB0GA1UdDgQWBBTub3MpjW+/p7Mbw5U09hVWRHhmtzATBgNVHSUEDDAKBggr
|
||||
BgEFBQcDAzBeBgNVHSAEVzBVMDkGCysGAQQBg44hAQABMCowKAYIKwYBBQUHAgEW
|
||||
HGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDgYMKwYBBAGDjiEBAoFIMAgG
|
||||
BmeBDAEEATB0BggrBgEFBQcBAQRoMGYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3Nw
|
||||
LWMuZW1zaWduLmNvbTA+BggrBgEFBQcwAoYyaHR0cDovL3JlcG9zaXRvcnkuZW1z
|
||||
aWduLmNvbS9jZXJ0cy9FTU9WQ1NDQUczQy5wN2MwNgYDVR0fBC8wLTAroCmgJ4Yl
|
||||
aHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290Y3NjYWczLmNybDAOBgNVHQ8BAf8E
|
||||
BAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAKBggqhkjOPQQDAwNoADBlAjEA8yPg
|
||||
8sntOhLwV2QXJ5LEaKSpo/fnE3uOgTadK73fDhyjiqr0Io9GVuQTYqBPbQ2TAjBg
|
||||
euApmBS486+iei03lRrp139dPw6k28jjGNvWsAU7A1N86J1zPJVDEyVH+nMAWC0=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDxTCCAq2gAwIBAgIRANal72E7OQ8y75zfNJ93KEEwDQYJKoZIhvcNAQELBQAw
|
||||
ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
|
||||
BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y
|
||||
ODEyMzEyMzU5NTlaMD8xCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVkaHJhIElu
|
||||
YzEaMBgGA1UEAwwRRU0gT1YgQ1MgQ0EgLSBHM0MwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||
IgNiAAS58OBjRwKmnKaq3UQ8/WLEG5uksgkkaX15dOdpGhJvuWr7PjTC/TvF9IMG
|
||||
HyZdwqWENjRzI1EENrKPBPOGI2MGeYDiUD4CUy1lRiNacWWiWaHvSW90G1/e53hy
|
||||
COS4+u+jggEsMIIBKDAfBgNVHSMEGDAWgBSgEQojPpbxB+zirynvgqV/0DCktDAd
|
||||
BgNVHQ4EFgQU7m9zKY1vv6ezG8OVNPYVVkR4ZrcwDgYDVR0PAQH/BAQDAgEGMBIG
|
||||
A1UdEwEB/wQIMAYBAf8CAQAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwMgYDVR0gBCsw
|
||||
KTAIBgZngQwBBAEwDQYLKwYBBAGDjiEBAAEwDgYMKwYBBAGDjiEBAoFIMEMGA1Ud
|
||||
HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0FBQUNlcnRpZmlj
|
||||
YXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0
|
||||
cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQBgGbZ+NDkn
|
||||
2Fr5dCVs/51+6cT5iEPP1stglX4TI2UaNh9LmPdltxXw3crV8gK5L8i+d+ScGibF
|
||||
y0obtBHNTldXDgLnAHIzY3n+pceyKj7O2ae9CYnyuu5RBifeI3XYKowWE+oXnVGF
|
||||
Oy0CvCJ/JsdP6aYgoCa9hGnAYicYQQL6MOMQZ5PuhcSM8t4giIzrpgrEkUP93GYE
|
||||
emDG5Z8BYwKs80lLeQNBnwg/VHJ3iEF/o84RXYwrlrKqJ+33C094o6LNikMJCqOK
|
||||
/WYHZSR8Xyhf8GXFAj5pZuI6Bcpug2V/E7i42j8OBhc25aNYDqK+B0qmb90sjNUB
|
||||
g3tJeSIb/599
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,38 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICQDCCAcagAwIBAgIUU3mgjls/8ZJ8upmukdlSQR1IF/UwCgYIKoZIzj0EAwIw
|
||||
PzELMAkGA1UEBhMCTkwxEzARBgNVBAoMCmVNdWRocmEgQlYxGzAZBgNVBAMMEkVN
|
||||
IERWIFRMUyBDQSAtIEczQjAeFw0yNjEwMjAwMjIyNDFaFw0yNzA1MDMwMjI3NDFa
|
||||
MDMxMTAvBgNVBAMMKGR2LWczYi1hYWEtZW11ZGhyYS53ZS1oYXZlLWEtcHJvYmxl
|
||||
bS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATK3sfcOxxtM2Vdq9Qbw/GR
|
||||
NsHlJcnqINEInXO3fkWhw/FYT80hkqGk0jSN/EKib3YZJsr5GyfkYcpEiA2rwMgm
|
||||
o4GrMIGoMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoG
|
||||
CCsGAQUFBwMBMDMGA1UdEQQsMCqCKGR2LWczYi1hYWEtZW11ZGhyYS53ZS1oYXZl
|
||||
LWEtcHJvYmxlbS5jb20wHQYDVR0OBBYEFDggfJFz1eeToKmzFBSYkMCvpAu0MB8G
|
||||
A1UdIwQYMBaAFOIeL+ccGTkOVUtiwSAkVzUkiiUUMAoGCCqGSM49BAMCA2gAMGUC
|
||||
MDqDDOt7K313QIH0rhn/ZKnzFu8+/9W8OlTuFtDfVCTRDfRmqdyfsCfLsEk7xqHq
|
||||
uQIxAJqyLERABmU98U46OCuT5bp5h0ytuSEIGM7orTpKci5S1u1B/AOmwv162U7R
|
||||
TcVPHA==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzjCCAragAwIBAgIRAKAk/fS6Zlx4XOSgIEg4WLcwDQYJKoZIhvcNAQELBQAw
|
||||
ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
|
||||
BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y
|
||||
ODEyMzEyMzU5NTlaMD8xCzAJBgNVBAYTAk5MMRMwEQYDVQQKDAplTXVkaHJhIEJW
|
||||
MRswGQYDVQQDDBJFTSBEViBUTFMgQ0EgLSBHM0IwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||
IgNiAATnntnASpdzxHNeUREdqFrCvV5kqzdLgOEsvCA47B5PDjuBL8mFnh5nr7BO
|
||||
L4vJf3SFumWqkR7opORbYtXMA9Vr5nizw4y03JN8okYOI4RTyJKTXSnUT5HhVga2
|
||||
PM7atGijggE1MIIBMTAfBgNVHSMEGDAWgBSgEQojPpbxB+zirynvgqV/0DCktDAd
|
||||
BgNVHQ4EFgQU4h4v5xwZOQ5VS2LBICRXNSSKJRQwDgYDVR0PAQH/BAQDAgEGMBIG
|
||||
A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
|
||||
MDEGA1UdIAQqMCgwCAYGZ4EMAQIBMA0GCysGAQQBg44hAQABMA0GCysGAQQBg44h
|
||||
AQJkMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0FB
|
||||
QUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEF
|
||||
BQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBCwUAA4IB
|
||||
AQBzQ1IoqpdotM6gFrR4Q3YrKifOLeaj5XXunldpVE/vZn2TxC2y0s8oYiLR5ICE
|
||||
wqVXSB7N6BM1Mp+Nif7Jbhb2IswAfSR4Iik8+B0iRmi7lYoNlycCgHjmsjOmZaxM
|
||||
QkPmjXAJbG5f4V41OsvJZmS7lI3r0IuvEDVX3GAZ13on6IImSs9ybNpHQ7NCqkMQ
|
||||
7wH4uAO1dtpFycgJSdHaCOhHjrox2BTYGLdpUrYwY3EOAjlZ8UGgsxs7mG+xquMl
|
||||
bN1LFURwfnkVMP3T/DBIZ/ys2+lNFyxK3FlArrV4/000uTnVB1lO8oGORYdXoT5V
|
||||
GUyRCEIDhF5TBUuKF4E3kf1h
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,56 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICOTCCAb6gAwIBAgIUY1R/PHrtXShV0uhLJ5EyjsHvMi0wCgYIKoZIzj0EAwIw
|
||||
PzELMAkGA1UEBhMCTkwxEzARBgNVBAoMCmVNdWRocmEgQlYxGzAZBgNVBAMMEkVN
|
||||
IERWIFRMUyBDQSAtIEczQjAeFw0yNjEwMjAwMDU5MzZaFw0yNzA1MDMwMTA0MzZa
|
||||
MC8xLTArBgNVBAMMJGR2LWczYi1lbXVkaHJhLndlLWhhdmUtYS1wcm9ibGVtLmNv
|
||||
bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFpE24ACBFHQLzpg+7D767thJ3+Q
|
||||
NIoJJugUMT/OftEz6G6uDdxlS+9VfXYBDglHjJirjEPFNglOGJoQNQuiz7Gjgacw
|
||||
gaQwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYB
|
||||
BQUHAwEwLwYDVR0RBCgwJoIkZHYtZzNiLWVtdWRocmEud2UtaGF2ZS1hLXByb2Js
|
||||
ZW0uY29tMB0GA1UdDgQWBBT4r5v4etYNuG21BqBqs273OkcHiDAfBgNVHSMEGDAW
|
||||
gBTiHi/nHBk5DlVLYsEgJFc1JIolFDAKBggqhkjOPQQDAgNpADBmAjEAlxNF+/8l
|
||||
APpgibawACy15yn5bcvTksz2Zw0R9z9nUK9e3MSSk+hXagg7LVb7V4NtAjEA4qyR
|
||||
laFPkaokrnTjqQieq8CgDQ6tIz3FjBvj3D7vkhKXHaml6zD2zT5ih2Y+lD2h
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDaDCCAu6gAwIBAgIQAKSu5njyM2K0wORgt3LyFDAKBggqhkjOPQQDAzBWMQsw
|
||||
CQYDVQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl
|
||||
ZDEgMB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwHhcNMjUwNDMwMTU1
|
||||
NDA4WhcNNDAwNDI4MTgzMDAwWjA/MQswCQYDVQQGEwJOTDETMBEGA1UECgwKZU11
|
||||
ZGhyYSBCVjEbMBkGA1UEAwwSRU0gRFYgVExTIENBIC0gRzNCMHYwEAYHKoZIzj0C
|
||||
AQYFK4EEACIDYgAE557ZwEqXc8RzXlERHahawr1eZKs3S4DhLLwgOOweTw47gS/J
|
||||
hZ4eZ6+wTi+LyX90hbplqpEe6KTkW2LVzAPVa+Z4s8OMtNyTfKJGDiOEU8iSk10p
|
||||
1E+R4VYGtjzO2rRoo4IBljCCAZIwHwYDVR0jBBgwFoAU4TqrIXzx401UrtRyt/2L
|
||||
WUIgkFQwHQYDVR0OBBYEFOIeL+ccGTkOVUtiwSAkVzUkiiUUMB0GA1UdJQQWMBQG
|
||||
CCsGAQUFBwMBBggrBgEFBQcDAjBdBgNVHSAEVjBUMDkGCysGAQQBg44hAQABMCow
|
||||
KAYIKwYBBQUHAgEWHGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDQYLKwYB
|
||||
BAGDjiEBAmQwCAYGZ4EMAQIBMHUGCCsGAQUFBwEBBGkwZzAkBggrBgEFBQcwAYYY
|
||||
aHR0cDovL29jc3AtYi5lbXNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVw
|
||||
b3NpdG9yeS5lbXNpZ24uY29tL2NlcnRzL0VNRFZUTFNDQUczQi5wN2MwNwYDVR0f
|
||||
BDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290dGxzY2FnMy5j
|
||||
cmwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0E
|
||||
AwMDaAAwZQIwLmsGXi6oFMJ2klkcEMXMug6gvxQWZWkewiDWVzQyl2L6z6DuQfPO
|
||||
Ibh1grvdULDCAjEAxZvLlxUavG69J6wnSjC8XBbqNIQsJ6CE2QdNOe84Ptxb6VG8
|
||||
gt2TlfJbHii+KNvK
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDdDCCAvmgAwIBAgIQAJYaPvsTWVdu0EAFtGhKWzAKBggqhkjOPQQDAzBrMQsw
|
||||
CQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhy
|
||||
YSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290
|
||||
IENBIC0gRzMwHhcNMjQwNzExMTU0MzM2WhcNMzkwNzA5MTgzMDAwWjBWMQswCQYD
|
||||
VQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg
|
||||
MB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwdjAQBgcqhkjOPQIBBgUr
|
||||
gQQAIgNiAARfD9w/q1cWxL6D9bMGH6CQ4u+gAV6GN9s+PwQx9Bl4hC+0iU8QOk8m
|
||||
WO/RwXQy10DCSxT8vSIJClHKp+Ao4qJKdYeV0SwZ7LMQziyLfxIUOY6Grja/sQFw
|
||||
LKYf8EdDXAyjggF1MIIBcTAfBgNVHSMEGDAWgBR8XQKEE9TMipuBzhccLikenEhj
|
||||
QjAdBgNVHQ4EFgQU4TqrIXzx401UrtRyt/2LWUIgkFQwHQYDVR0lBBYwFAYIKwYB
|
||||
BQUHAwEGCCsGAQUFBwMCMD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIB
|
||||
FhxodHRwOi8vcmVwb3NpdG9yeS5lbXNpZ24uY29tMHgGCCsGAQUFBwEBBGwwajAi
|
||||
BggrBgEFBQcwAYYWaHR0cDovL29jc3AuZW1zaWduLmNvbTBEBggrBgEFBQcwAoY4
|
||||
aHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbS9jZXJ0cy9lbVNpZ25FQ0NSb290
|
||||
Q0FHMy5jcnQwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5lbXNpZ24uY29t
|
||||
P1Jvb3RDQUczLmNybDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIB
|
||||
ATAKBggqhkjOPQQDAwNpADBmAjEAt9CIpRGp46QHQYDUBnLKw+vvK0/h9vmilEAg
|
||||
TroUl/anH4x2rXHqiCtBTBfgKDegAjEA8WpG1LTZdAIL0FSYqxXeREaeTSxEofM0
|
||||
1jJH3jGvCXC79LU9UPJ2o3TU+UIj2gMV
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,38 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICQjCCAcegAwIBAgIUMH3z62Z6agEULBZqrfhpw9/ZpCAwCgYIKoZIzj0EAwIw
|
||||
QDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC2VNdWRocmEgSW5jMRswGQYDVQQDDBJF
|
||||
TSBEViBUTFMgQ0EgLSBHM0MwHhcNMjYxMDIwMDIyMjQxWhcNMjcwNTAzMDIyNzQx
|
||||
WjAzMTEwLwYDVQQDDChkdi1nM2MtYWFhLWVtdWRocmEud2UtaGF2ZS1hLXByb2Js
|
||||
ZW0uY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAMPpHsQqEqIerrj/durO
|
||||
EN7gkUVJSVgIPN6+VJ2sEGXDyqXAN1fnNzGuASP/4WEr0RIhht3cYvuUYp2SYofr
|
||||
w6OBqzCBqDAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAK
|
||||
BggrBgEFBQcDATAzBgNVHREELDAqgihkdi1nM2MtYWFhLWVtdWRocmEud2UtaGF2
|
||||
ZS1hLXByb2JsZW0uY29tMB0GA1UdDgQWBBT8CNQz4OLVnIwrQYZBF+++9jTCvzAf
|
||||
BgNVHSMEGDAWgBSufhBcPCmWwahHJwVZ8eA91OUK8zAKBggqhkjOPQQDAgNpADBm
|
||||
AjEAp3ZLdfBvKcZxgR2p8LZaxWzP6uB7zU/EZ9BA49Ds2P5ByYkRodwK5qcwt4J/
|
||||
VL/vAjEAnnxrcrDX7SzoJxuho604GBh6eyBLJLCdmcy+qb+LA3u5oxP/OjAzCA4x
|
||||
v1kw6Tm4
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzzCCAregAwIBAgIRAJHzR/Glt9OZYrleDuTimdEwDQYJKoZIhvcNAQELBQAw
|
||||
ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
|
||||
BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y
|
||||
ODEyMzEyMzU5NTlaMEAxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVkaHJhIElu
|
||||
YzEbMBkGA1UEAwwSRU0gRFYgVExTIENBIC0gRzNDMHYwEAYHKoZIzj0CAQYFK4EE
|
||||
ACIDYgAEOnp4YsjETTJN3WkDOmeCwO/u/kBV5ooSP6A3G6O9kZRRTuFypDPjYw8C
|
||||
vRiAN9nlFJ+bd9uUooHfmHsv6oiLdL3+j9pMEJnubKGVZv4+8OX2sJpVbMyLpQVh
|
||||
3xsqRB0Bo4IBNTCCATEwHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQw
|
||||
HQYDVR0OBBYEFK5+EFw8KZbBqEcnBVnx4D3U5QrzMA4GA1UdDwEB/wQEAwIBBjAS
|
||||
BgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
|
||||
AjAxBgNVHSAEKjAoMAgGBmeBDAECATANBgsrBgEEAYOOIQEAATANBgsrBgEEAYOO
|
||||
IQECZDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9B
|
||||
QUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDA0BggrBgEFBQcBAQQoMCYwJAYIKwYB
|
||||
BQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG9w0BAQsFAAOC
|
||||
AQEAr7b6Mx11TIJgVzHt6EH5qGNWeiiqoMrp8E5/k8s+jvupx1n083pZ5XOzUzND
|
||||
wEUe0W4C4FIGUHNQ4XDwqOw0yK42xqm2dvCKgsko2UvDlT0haZVtTCeWMQOe5J/J
|
||||
ziaMfZwsZNfZ3aaI6XPtWDZtwY8AaNfyaeCpbtm5GcGnyNWk4jSNpbtpo6NjfXuP
|
||||
CFOm3rTjJ4zytP7Tso6iMm0L01cYFOlTmoPv4515R2r3SPnKDtijoB/D/vgxzojv
|
||||
DafgyG9afX8+laO9NbMMz4sqC1vZx3984i/sBNFiugm3Vvdq5OdJRjuGC1QILIs6
|
||||
dg/K3i8VQqRpbfaM9kIlMzZ3dQ==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,56 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICODCCAb+gAwIBAgIUbWvtwSNK/Qro223vAqyz323+VBAwCgYIKoZIzj0EAwIw
|
||||
QDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC2VNdWRocmEgSW5jMRswGQYDVQQDDBJF
|
||||
TSBEViBUTFMgQ0EgLSBHM0MwHhcNMjYxMDIwMDA1OTM2WhcNMjcwNTAzMDEwNDM2
|
||||
WjAvMS0wKwYDVQQDDCRkdi1nM2MtZW11ZGhyYS53ZS1oYXZlLWEtcHJvYmxlbS5j
|
||||
b20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAScdBQpNUOOCcag6YEVFccNRSFe
|
||||
UIl02iH53vDfoWPJFS005WzqF3CPDclB/kygP2gWF4pg/r5gPYdnI3/6Vdzro4Gn
|
||||
MIGkMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsG
|
||||
AQUFBwMBMC8GA1UdEQQoMCaCJGR2LWczYy1lbXVkaHJhLndlLWhhdmUtYS1wcm9i
|
||||
bGVtLmNvbTAdBgNVHQ4EFgQUBj2yGO/TzDOE/u/6QeUvcW8y1iMwHwYDVR0jBBgw
|
||||
FoAUrn4QXDwplsGoRycFWfHgPdTlCvMwCgYIKoZIzj0EAwIDZwAwZAIwBQOOQqG9
|
||||
ccdzM/7XIlXLtota1esALaLmgEN/ZweosBIvk4xabHcERZInWVIwO45NAjBos8Ue
|
||||
NwDe1X3ahdK2lkxqBEoBMtUHJN18TnJKs2nK1gmXpMbmY5oKnAQQgCEZPA4=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDaDCCAu6gAwIBAgIPWIsuPCu4zaE+GhIJJUMhMAoGCCqGSM49BAMDMFYxCzAJ
|
||||
BgNVBAYTAklOMSUwIwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVk
|
||||
MSAwHgYDVQQDDBdlbVNpZ24gUm9vdCBUTFMgQ0EgLSBHMzAeFw0yNTA0MzAxNTM1
|
||||
MDlaFw00MDA0MjgxODMwMDBaMEAxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVk
|
||||
aHJhIEluYzEbMBkGA1UEAwwSRU0gRFYgVExTIENBIC0gRzNDMHYwEAYHKoZIzj0C
|
||||
AQYFK4EEACIDYgAEOnp4YsjETTJN3WkDOmeCwO/u/kBV5ooSP6A3G6O9kZRRTuFy
|
||||
pDPjYw8CvRiAN9nlFJ+bd9uUooHfmHsv6oiLdL3+j9pMEJnubKGVZv4+8OX2sJpV
|
||||
bMyLpQVh3xsqRB0Bo4IBljCCAZIwHwYDVR0jBBgwFoAU4TqrIXzx401UrtRyt/2L
|
||||
WUIgkFQwHQYDVR0OBBYEFK5+EFw8KZbBqEcnBVnx4D3U5QrzMB0GA1UdJQQWMBQG
|
||||
CCsGAQUFBwMBBggrBgEFBQcDAjBdBgNVHSAEVjBUMDkGCysGAQQBg44hAQABMCow
|
||||
KAYIKwYBBQUHAgEWHGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDQYLKwYB
|
||||
BAGDjiEBAmQwCAYGZ4EMAQIBMHUGCCsGAQUFBwEBBGkwZzAkBggrBgEFBQcwAYYY
|
||||
aHR0cDovL29jc3AtYy5lbXNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVw
|
||||
b3NpdG9yeS5lbXNpZ24uY29tL2NlcnRzL0VNRFZUTFNDQUczQy5wN2MwNwYDVR0f
|
||||
BDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290dGxzY2FnMy5j
|
||||
cmwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0E
|
||||
AwMDaAAwZQIxAMEN2HWMocbuWdy1d33pH6JpMvP/V/DIS5PtZbv1Q7TXEbxrcyxg
|
||||
yuddOJscYDJJlAIwB4sH1BpbScDzkdD4tJBSwrG/1Mx82vb8YaySehNuu1+1oXNd
|
||||
AEKwaJ0mVzScy2+6
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDdDCCAvmgAwIBAgIQAJYaPvsTWVdu0EAFtGhKWzAKBggqhkjOPQQDAzBrMQsw
|
||||
CQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhy
|
||||
YSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290
|
||||
IENBIC0gRzMwHhcNMjQwNzExMTU0MzM2WhcNMzkwNzA5MTgzMDAwWjBWMQswCQYD
|
||||
VQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg
|
||||
MB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwdjAQBgcqhkjOPQIBBgUr
|
||||
gQQAIgNiAARfD9w/q1cWxL6D9bMGH6CQ4u+gAV6GN9s+PwQx9Bl4hC+0iU8QOk8m
|
||||
WO/RwXQy10DCSxT8vSIJClHKp+Ao4qJKdYeV0SwZ7LMQziyLfxIUOY6Grja/sQFw
|
||||
LKYf8EdDXAyjggF1MIIBcTAfBgNVHSMEGDAWgBR8XQKEE9TMipuBzhccLikenEhj
|
||||
QjAdBgNVHQ4EFgQU4TqrIXzx401UrtRyt/2LWUIgkFQwHQYDVR0lBBYwFAYIKwYB
|
||||
BQUHAwEGCCsGAQUFBwMCMD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIB
|
||||
FhxodHRwOi8vcmVwb3NpdG9yeS5lbXNpZ24uY29tMHgGCCsGAQUFBwEBBGwwajAi
|
||||
BggrBgEFBQcwAYYWaHR0cDovL29jc3AuZW1zaWduLmNvbTBEBggrBgEFBQcwAoY4
|
||||
aHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbS9jZXJ0cy9lbVNpZ25FQ0NSb290
|
||||
Q0FHMy5jcnQwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5lbXNpZ24uY29t
|
||||
P1Jvb3RDQUczLmNybDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIB
|
||||
ATAKBggqhkjOPQQDAwNpADBmAjEAt9CIpRGp46QHQYDUBnLKw+vvK0/h9vmilEAg
|
||||
TroUl/anH4x2rXHqiCtBTBfgKDegAjEA8WpG1LTZdAIL0FSYqxXeREaeTSxEofM0
|
||||
1jJH3jGvCXC79LU9UPJ2o3TU+UIj2gMV
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,38 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICQDCCAcagAwIBAgIUUYu3O8bpN1GPvCIHqWpT/FYUNDowCgYIKoZIzj0EAwIw
|
||||
PzELMAkGA1UEBhMCTkwxEzARBgNVBAoMCmVNdWRocmEgQlYxGzAZBgNVBAMMEkVN
|
||||
IEVWIFRMUyBDQSAtIEczQjAeFw0yNjEwMjAwMjIyNDFaFw0yNzA1MDMwMjI3NDFa
|
||||
MDMxMTAvBgNVBAMMKGV2LWczYi1hYWEtZW11ZGhyYS53ZS1oYXZlLWEtcHJvYmxl
|
||||
bS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASjDLpnVdmvXM6R3UB2o42a
|
||||
1GnrYfxRXo7UII36mBl9mIS7p9MkAryhPdxitFHkgTrrbypVtzpydNtJIGGGXUwE
|
||||
o4GrMIGoMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoG
|
||||
CCsGAQUFBwMBMDMGA1UdEQQsMCqCKGV2LWczYi1hYWEtZW11ZGhyYS53ZS1oYXZl
|
||||
LWEtcHJvYmxlbS5jb20wHQYDVR0OBBYEFEVBY1ecTN9bLMDl4ifdnN2WZL+WMB8G
|
||||
A1UdIwQYMBaAFD7cINmJRpiHe4c/2IFHslwV2EcGMAoGCCqGSM49BAMCA2gAMGUC
|
||||
MQCyvr9gktgmtsuTuFleFV+YExNNKLf3GZieuUprQcERrzh798oYKavTqZeYf9Kz
|
||||
RhECMCVs1B7KPh7/lgHRly14Z8RJA8ApR7e2cRREW5uPKFOSDlZ/RIezxsfEzsQ4
|
||||
lAODeA==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzTCCArWgAwIBAgIRANj4aF5PK9nYJAKoWhwetzEwDQYJKoZIhvcNAQELBQAw
|
||||
ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
|
||||
BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y
|
||||
ODEyMzEyMzU5NTlaMD8xCzAJBgNVBAYTAk5MMRMwEQYDVQQKDAplTXVkaHJhIEJW
|
||||
MRswGQYDVQQDDBJFTSBFViBUTFMgQ0EgLSBHM0IwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||
IgNiAAQl26haZNOX2gsQUunqVG5Ie+/qnJ2daKDF6tz+73Wmm7XW/AH0K/vKbrQL
|
||||
zWTAdn42/9Bdl6gTxaoMbJUw55z18IZkaPE4BkIKpchRhAseJSVA55DktlTDhS7W
|
||||
SvHoZX2jggE0MIIBMDAfBgNVHSMEGDAWgBSgEQojPpbxB+zirynvgqV/0DCktDAd
|
||||
BgNVHQ4EFgQUPtwg2YlGmId7hz/YgUeyXBXYRwYwDgYDVR0PAQH/BAQDAgEGMBIG
|
||||
A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
|
||||
MDAGA1UdIAQpMCcwBwYFZ4EMAQEwDQYLKwYBBAGDjiEBAAEwDQYLKwYBBAGDjiEB
|
||||
AngwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFB
|
||||
Q2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUF
|
||||
BzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQELBQADggEB
|
||||
AJNFuAFW9Gnw/8+kCO4KIlZdnM1Sh2u+8cK0EYAHhXpb5FlGvYkcARigo5oS7VoM
|
||||
7FOYlTueV6yICZKNAYawZPJPhNCGK+rp3cQBEz6d0n8OZEYZov9lb3ll8ORgSDuf
|
||||
FVIW6Gagg/UnOamLBTOR8eD5V73SCme/B2cHUDw+m5E8cc+0Ujzz3Cn3ex5JrhXk
|
||||
2T3s8PoVzEmhTgGboFfcWO1EYhPPrYwdxPYRQIXAXUXhLEOVNL9ASbZJLFI8E1IM
|
||||
m7NwxVN2s38jWpaCUaaF0tg7hfiy7oe13rzJfHsdqldJVTTUTPWVx8TC4ju0VLet
|
||||
bAlwhwFT6AP8uIVMGUHNw24=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,56 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICNzCCAb6gAwIBAgIUPfthO0PYGAYKs9lQ4gp1IOkOr2MwCgYIKoZIzj0EAwIw
|
||||
PzELMAkGA1UEBhMCTkwxEzARBgNVBAoMCmVNdWRocmEgQlYxGzAZBgNVBAMMEkVN
|
||||
IEVWIFRMUyBDQSAtIEczQjAeFw0yNjEwMjAwMDU5MzZaFw0yNzA1MDMwMTA0MzZa
|
||||
MC8xLTArBgNVBAMMJGV2LWczYi1lbXVkaHJhLndlLWhhdmUtYS1wcm9ibGVtLmNv
|
||||
bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPZPwlA3io/AX/pMwu0dNl9WMxrr
|
||||
C9j/xMLHgG72cWVzoX62REmvp9zyXQ04kHP9/sW6KnCcm5ZZ5i4YoJbDPASjgacw
|
||||
gaQwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYB
|
||||
BQUHAwEwLwYDVR0RBCgwJoIkZXYtZzNiLWVtdWRocmEud2UtaGF2ZS1hLXByb2Js
|
||||
ZW0uY29tMB0GA1UdDgQWBBSvX4QR4ERW5uNUaMq0yYzXCZf0ZjAfBgNVHSMEGDAW
|
||||
gBQ+3CDZiUaYh3uHP9iBR7JcFdhHBjAKBggqhkjOPQQDAgNnADBkAjACy/uJR01M
|
||||
/O9IEZyRHX1LSU28bmg542K0IX9jDRTrZXUqkDWfiZIL7xfnrRrBpHoCMFbBIaOa
|
||||
WwWtFtDg9ZqQvktsWS950u5qfW1sChgWNya/E3IMKFgtoHm2xrZsmfwLSQ==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDaDCCAu2gAwIBAgIQAMfEHv6S2CXwXq6EGkqCwTAKBggqhkjOPQQDAzBWMQsw
|
||||
CQYDVQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl
|
||||
ZDEgMB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwHhcNMjUwNDMwMTYw
|
||||
MzMxWhcNNDAwNDI4MTgzMDAwWjA/MQswCQYDVQQGEwJOTDETMBEGA1UECgwKZU11
|
||||
ZGhyYSBCVjEbMBkGA1UEAwwSRU0gRVYgVExTIENBIC0gRzNCMHYwEAYHKoZIzj0C
|
||||
AQYFK4EEACIDYgAEJduoWmTTl9oLEFLp6lRuSHvv6pydnWigxerc/u91ppu11vwB
|
||||
9Cv7ym60C81kwHZ+Nv/QXZeoE8WqDGyVMOec9fCGZGjxOAZCCqXIUYQLHiUlQOeQ
|
||||
5LZUw4Uu1krx6GV9o4IBlTCCAZEwHwYDVR0jBBgwFoAU4TqrIXzx401UrtRyt/2L
|
||||
WUIgkFQwHQYDVR0OBBYEFD7cINmJRpiHe4c/2IFHslwV2EcGMB0GA1UdJQQWMBQG
|
||||
CCsGAQUFBwMBBggrBgEFBQcDAjBcBgNVHSAEVTBTMDkGCysGAQQBg44hAQABMCow
|
||||
KAYIKwYBBQUHAgEWHGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDQYLKwYB
|
||||
BAGDjiEBAngwBwYFZ4EMAQEwdQYIKwYBBQUHAQEEaTBnMCQGCCsGAQUFBzABhhho
|
||||
dHRwOi8vb2NzcC1iLmVtc2lnbi5jb20wPwYIKwYBBQUHMAKGM2h0dHA6Ly9yZXBv
|
||||
c2l0b3J5LmVtc2lnbi5jb20vY2VydHMvRU1FVlRMU0NBRzNCLnA3YzA3BgNVHR8E
|
||||
MDAuMCygKqAohiZodHRwOi8vY3JsLmVtc2lnbi5jb20vP3Jvb3R0bHNjYWczLmNy
|
||||
bDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAKBggqhkjOPQQD
|
||||
AwNpADBmAjEA9Jq999oaOUKhFwopw33iCmt0OHIn3gjT43GfuOHR/YckXdwoNdDA
|
||||
lIEhz0NgRO3/AjEAvY/a3OMvktIZLYjn+WLKmp0QhrIMV+e6EpS7j4GVeOKouNp6
|
||||
MN/P+ByGeiRMghzB
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDdDCCAvmgAwIBAgIQAJYaPvsTWVdu0EAFtGhKWzAKBggqhkjOPQQDAzBrMQsw
|
||||
CQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhy
|
||||
YSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290
|
||||
IENBIC0gRzMwHhcNMjQwNzExMTU0MzM2WhcNMzkwNzA5MTgzMDAwWjBWMQswCQYD
|
||||
VQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg
|
||||
MB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwdjAQBgcqhkjOPQIBBgUr
|
||||
gQQAIgNiAARfD9w/q1cWxL6D9bMGH6CQ4u+gAV6GN9s+PwQx9Bl4hC+0iU8QOk8m
|
||||
WO/RwXQy10DCSxT8vSIJClHKp+Ao4qJKdYeV0SwZ7LMQziyLfxIUOY6Grja/sQFw
|
||||
LKYf8EdDXAyjggF1MIIBcTAfBgNVHSMEGDAWgBR8XQKEE9TMipuBzhccLikenEhj
|
||||
QjAdBgNVHQ4EFgQU4TqrIXzx401UrtRyt/2LWUIgkFQwHQYDVR0lBBYwFAYIKwYB
|
||||
BQUHAwEGCCsGAQUFBwMCMD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIB
|
||||
FhxodHRwOi8vcmVwb3NpdG9yeS5lbXNpZ24uY29tMHgGCCsGAQUFBwEBBGwwajAi
|
||||
BggrBgEFBQcwAYYWaHR0cDovL29jc3AuZW1zaWduLmNvbTBEBggrBgEFBQcwAoY4
|
||||
aHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbS9jZXJ0cy9lbVNpZ25FQ0NSb290
|
||||
Q0FHMy5jcnQwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5lbXNpZ24uY29t
|
||||
P1Jvb3RDQUczLmNybDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIB
|
||||
ATAKBggqhkjOPQQDAwNpADBmAjEAt9CIpRGp46QHQYDUBnLKw+vvK0/h9vmilEAg
|
||||
TroUl/anH4x2rXHqiCtBTBfgKDegAjEA8WpG1LTZdAIL0FSYqxXeREaeTSxEofM0
|
||||
1jJH3jGvCXC79LU9UPJ2o3TU+UIj2gMV
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,38 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICQDCCAcegAwIBAgIUTMUrGzSU+0BySFdrN3ALZq9Spv8wCgYIKoZIzj0EAwIw
|
||||
QDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC2VNdWRocmEgSW5jMRswGQYDVQQDDBJF
|
||||
TSBFViBUTFMgQ0EgLSBHM0MwHhcNMjYxMDIwMDIyMjQyWhcNMjcwNTAzMDIyNzQy
|
||||
WjAzMTEwLwYDVQQDDChldi1nM2MtYWFhLWVtdWRocmEud2UtaGF2ZS1hLXByb2Js
|
||||
ZW0uY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEglClDbotLFFeY9SpryT4
|
||||
k958tvriY2DDnaPsmjQaecRhB6+L/8hLw6FMt+HT8SLvjIZnFY+AH92cBjedcNST
|
||||
AKOBqzCBqDAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAK
|
||||
BggrBgEFBQcDATAzBgNVHREELDAqgihldi1nM2MtYWFhLWVtdWRocmEud2UtaGF2
|
||||
ZS1hLXByb2JsZW0uY29tMB0GA1UdDgQWBBSVtGbn6xUT1iosUeaGA15Nq640aTAf
|
||||
BgNVHSMEGDAWgBSmJV6AqU2385Z8G8eR35K87kvrXzAKBggqhkjOPQQDAgNnADBk
|
||||
AjAg7b5bIK9X50QEX0U6G8fQh12BnFQ079Iluzd0Y2TJWCjtVXZi3nOciM8RzNKM
|
||||
tZECMCBf0JOyMt8RNS93Wj8kYbWkCtoKoUrqx7G3JTfGReKci+DdTvkF8/3F6doi
|
||||
9KB22Q==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzTCCArWgAwIBAgIQBfB4dS3s56G7hZXTAxZxcTANBgkqhkiG9w0BAQsFADB7
|
||||
MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD
|
||||
VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE
|
||||
AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTI1MDUxNDAwMDAwMFoXDTI4
|
||||
MTIzMTIzNTk1OVowQDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC2VNdWRocmEgSW5j
|
||||
MRswGQYDVQQDDBJFTSBFViBUTFMgQ0EgLSBHM0MwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||
IgNiAAQtVugppvuCxkpGl9lgr5UekGeukfkm00MG2kioqut4abamfm0nQm4EQqoD
|
||||
s24ZiN/2NlFO4c8H9bpLP/KgZkWufTHVhmrTGqPSRusLMXfumct1ZsUWznSAimaP
|
||||
kfDEf8qjggE0MIIBMDAfBgNVHSMEGDAWgBSgEQojPpbxB+zirynvgqV/0DCktDAd
|
||||
BgNVHQ4EFgQUpiVegKlNt/OWfBvHkd+SvO5L618wDgYDVR0PAQH/BAQDAgEGMBIG
|
||||
A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
|
||||
MDAGA1UdIAQpMCcwBwYFZ4EMAQEwDQYLKwYBBAGDjiEBAAEwDQYLKwYBBAGDjiEB
|
||||
AngwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFB
|
||||
Q2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUF
|
||||
BzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQELBQADggEB
|
||||
AIFfHiaMSjnrdZygRYV4ekRBmG7ybcFk4hGQWMMyMFi0cAEk2+dGfz5bl8IaIbLq
|
||||
z+uVJzhOgVEX7WqIfgbWfFsfPYHYjPHBmy7SlZ3BwA86IQ6qF/etpX2/qL9A5yRN
|
||||
N3nVowUpaqV6H3aCpkAh3wR2ZNkvAbLDvPz3J0l+sEWjWv+ZISOV4R+hXDTdPXV0
|
||||
1lKxnbwqEsdo/sE2QkCDKwFrZh/0uKjWspydJj+jSaDLTrGNx50Dgmn7VEcD1ZI5
|
||||
FXjknVUiO25YlmS1dtVZSBbyUKSjlrP/yDwPcZunoAsmCAAtCKsFLviLaymzvsKI
|
||||
eSSDGxpSySp2VCxompGDk8g=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,56 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICOTCCAb+gAwIBAgIUOcw5N4XGLiB/3GvB05bJuGoZPR0wCgYIKoZIzj0EAwIw
|
||||
QDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC2VNdWRocmEgSW5jMRswGQYDVQQDDBJF
|
||||
TSBFViBUTFMgQ0EgLSBHM0MwHhcNMjYxMDIwMDA1OTM3WhcNMjcwNTAzMDEwNDM3
|
||||
WjAvMS0wKwYDVQQDDCRldi1nM2MtZW11ZGhyYS53ZS1oYXZlLWEtcHJvYmxlbS5j
|
||||
b20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQo7U02MAUO8EEMomXwIl+kSQzm
|
||||
b+nvXKIZ/hC4rZ1GpXchv4SzTrV1SB/Qg+pD6OBEXbBBnPmQNkePNQTlujqRo4Gn
|
||||
MIGkMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsG
|
||||
AQUFBwMBMC8GA1UdEQQoMCaCJGV2LWczYy1lbXVkaHJhLndlLWhhdmUtYS1wcm9i
|
||||
bGVtLmNvbTAdBgNVHQ4EFgQUqxe+7qS+MxNyioJZhQsL5MEEw6swHwYDVR0jBBgw
|
||||
FoAUpiVegKlNt/OWfBvHkd+SvO5L618wCgYIKoZIzj0EAwIDaAAwZQIwC/CxSIov
|
||||
wcar3sV98B8cL+QuR2kkJdLdXsFKzg/HDSJ1MOjqTGm924LB6r9nl0f7AjEAzcwr
|
||||
WQOYY9eycqIz+LG2daYS6LOctj+CjoAk/CLugWPxGxp4Aerfau2P1z1h5jAA
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDaDCCAu6gAwIBAgIQAIAJkopkEHYBrwX6LxnxCTAKBggqhkjOPQQDAzBWMQsw
|
||||
CQYDVQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl
|
||||
ZDEgMB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwHhcNMjUwNDMwMTUz
|
||||
OTMxWhcNNDAwNDI4MTgzMDAwWjBAMQswCQYDVQQGEwJVUzEUMBIGA1UECgwLZU11
|
||||
ZGhyYSBJbmMxGzAZBgNVBAMMEkVNIEVWIFRMUyBDQSAtIEczQzB2MBAGByqGSM49
|
||||
AgEGBSuBBAAiA2IABC1W6Cmm+4LGSkaX2WCvlR6QZ66R+SbTQwbaSKiq63hptqZ+
|
||||
bSdCbgRCqgOzbhmI3/Y2UU7hzwf1uks/8qBmRa59MdWGatMao9JG6wsxd+6Zy3Vm
|
||||
xRbOdICKZo+R8MR/yqOCAZUwggGRMB8GA1UdIwQYMBaAFOE6qyF88eNNVK7Ucrf9
|
||||
i1lCIJBUMB0GA1UdDgQWBBSmJV6AqU2385Z8G8eR35K87kvrXzAdBgNVHSUEFjAU
|
||||
BggrBgEFBQcDAQYIKwYBBQUHAwIwXAYDVR0gBFUwUzA5BgsrBgEEAYOOIQEAATAq
|
||||
MCgGCCsGAQUFBwIBFhxodHRwOi8vcmVwb3NpdG9yeS5lbXNpZ24uY29tMA0GCysG
|
||||
AQQBg44hAQJ4MAcGBWeBDAEBMHUGCCsGAQUFBwEBBGkwZzAkBggrBgEFBQcwAYYY
|
||||
aHR0cDovL29jc3AtYy5lbXNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVw
|
||||
b3NpdG9yeS5lbXNpZ24uY29tL2NlcnRzL0VNRVZUTFNDQUczQy5wN2MwNwYDVR0f
|
||||
BDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290dGxzY2FnMy5j
|
||||
cmwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0E
|
||||
AwMDaAAwZQIwBy1wTnzH4lXDTy2PD91ysLjvnlVN3JbJmc1BIMEU8TOLkYVHibMc
|
||||
9HeyonikFSfmAjEA6uE3QRxlrZTz/1/lXPTEMUhoauAtycagtlNFp79kjhvNa2/f
|
||||
Aal1GNxsOutLnr0Y
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDdDCCAvmgAwIBAgIQAJYaPvsTWVdu0EAFtGhKWzAKBggqhkjOPQQDAzBrMQsw
|
||||
CQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhy
|
||||
YSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290
|
||||
IENBIC0gRzMwHhcNMjQwNzExMTU0MzM2WhcNMzkwNzA5MTgzMDAwWjBWMQswCQYD
|
||||
VQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg
|
||||
MB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwdjAQBgcqhkjOPQIBBgUr
|
||||
gQQAIgNiAARfD9w/q1cWxL6D9bMGH6CQ4u+gAV6GN9s+PwQx9Bl4hC+0iU8QOk8m
|
||||
WO/RwXQy10DCSxT8vSIJClHKp+Ao4qJKdYeV0SwZ7LMQziyLfxIUOY6Grja/sQFw
|
||||
LKYf8EdDXAyjggF1MIIBcTAfBgNVHSMEGDAWgBR8XQKEE9TMipuBzhccLikenEhj
|
||||
QjAdBgNVHQ4EFgQU4TqrIXzx401UrtRyt/2LWUIgkFQwHQYDVR0lBBYwFAYIKwYB
|
||||
BQUHAwEGCCsGAQUFBwMCMD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIB
|
||||
FhxodHRwOi8vcmVwb3NpdG9yeS5lbXNpZ24uY29tMHgGCCsGAQUFBwEBBGwwajAi
|
||||
BggrBgEFBQcwAYYWaHR0cDovL29jc3AuZW1zaWduLmNvbTBEBggrBgEFBQcwAoY4
|
||||
aHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbS9jZXJ0cy9lbVNpZ25FQ0NSb290
|
||||
Q0FHMy5jcnQwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5lbXNpZ24uY29t
|
||||
P1Jvb3RDQUczLmNybDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIB
|
||||
ATAKBggqhkjOPQQDAwNpADBmAjEAt9CIpRGp46QHQYDUBnLKw+vvK0/h9vmilEAg
|
||||
TroUl/anH4x2rXHqiCtBTBfgKDegAjEA8WpG1LTZdAIL0FSYqxXeREaeTSxEofM0
|
||||
1jJH3jGvCXC79LU9UPJ2o3TU+UIj2gMV
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,38 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICPzCCAcagAwIBAgIUH0gWfBRnOiSjcduti/cukjDGAjIwCgYIKoZIzj0EAwIw
|
||||
PzELMAkGA1UEBhMCTkwxEzARBgNVBAoMCmVNdWRocmEgQlYxGzAZBgNVBAMMEkVN
|
||||
IE9WIFRMUyBDQSAtIEczQjAeFw0yNjEwMjAwMjIyNDJaFw0yNzA1MDMwMjI3NDJa
|
||||
MDMxMTAvBgNVBAMMKG92LWczYi1hYWEtZW11ZGhyYS53ZS1oYXZlLWEtcHJvYmxl
|
||||
bS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ3EfjVE+6fUm9gr7UoUv6N
|
||||
YPttIdILR9OMZnAdTj+RWT1Z4fp7dpC5mat1gbIo4J6EDelcOQQz5/oGeB3gFJKw
|
||||
o4GrMIGoMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoG
|
||||
CCsGAQUFBwMBMDMGA1UdEQQsMCqCKG92LWczYi1hYWEtZW11ZGhyYS53ZS1oYXZl
|
||||
LWEtcHJvYmxlbS5jb20wHQYDVR0OBBYEFKzQPp6gTBOWb0TBxZbkL5zsQn3oMB8G
|
||||
A1UdIwQYMBaAFJvYZVGv5+csN60I7mkZ2b83hK4KMAoGCCqGSM49BAMCA2cAMGQC
|
||||
MARKrRRBEWY2Ix1d80DGUvC7bWENP1Q9pf6BOGF4+84UiwV5+nAuht/+hdpAanWB
|
||||
sQIwJF2dW8tpPN6lTm1kDerzG6yJAJOz4drotaTthMF2+efuv5W2FL7Dr8qZSd1z
|
||||
201o
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzjCCAragAwIBAgIRAOlCI73pL5FOWrmsq/2sNxAwDQYJKoZIhvcNAQELBQAw
|
||||
ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
|
||||
BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y
|
||||
ODEyMzEyMzU5NTlaMD8xCzAJBgNVBAYTAk5MMRMwEQYDVQQKDAplTXVkaHJhIEJW
|
||||
MRswGQYDVQQDDBJFTSBPViBUTFMgQ0EgLSBHM0IwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||
IgNiAARziXqg6zz9yosSQC/68KvE4SErr43ud4rJ7i1tuD+sKAxeBWOK56Ks1xwy
|
||||
Ko42HN659pxDArndjXPRzGyqsswK7k4hbOl53VGKSvS2JuVoTN1lgDftDIVOTMEv
|
||||
TPhkbQ2jggE1MIIBMTAfBgNVHSMEGDAWgBSgEQojPpbxB+zirynvgqV/0DCktDAd
|
||||
BgNVHQ4EFgQUm9hlUa/n5yw3rQjuaRnZvzeErgowDgYDVR0PAQH/BAQDAgEGMBIG
|
||||
A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
|
||||
MDEGA1UdIAQqMCgwCAYGZ4EMAQICMA0GCysGAQQBg44hAQABMA0GCysGAQQBg44h
|
||||
AQJuMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0FB
|
||||
QUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEF
|
||||
BQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBCwUAA4IB
|
||||
AQALRoOSvkzRa+tN5Rje9W5GiXd/qsicd9N6Gnh0SWC7VyWH+HycIqAnFCMYZznj
|
||||
CeGusomFYHE42SHWr85JDIbgzy6y3dO4i9n9w0Bx7yrXWBbDzFS8VgA3+EY4Cg7y
|
||||
mtoC3RPOJVtaXXPZ+68X804g1Mj+3vCSHj2lNPVgco0OzSIOJ/KMg1R9MLBkiKzb
|
||||
bERAGZHejY0HBhYd76IINZN8z6bBNah2UdFYxsOisLynPQUkol6gRLum024/FzVl
|
||||
CCDfnUusrkr+mAfPAvD/Vb8bhpA7jYtepuU+2mIdShj5lLlvAwZphn+ypWycuo2x
|
||||
0k9mldz2dqI9nhCiY+TTIOOR
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,56 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICODCCAb6gAwIBAgIUPXjC97k7buQHZ7p5ehDU8QNyrR4wCgYIKoZIzj0EAwIw
|
||||
PzELMAkGA1UEBhMCTkwxEzARBgNVBAoMCmVNdWRocmEgQlYxGzAZBgNVBAMMEkVN
|
||||
IE9WIFRMUyBDQSAtIEczQjAeFw0yNjEwMjAwMDU5MzdaFw0yNzA1MDMwMTA0Mzda
|
||||
MC8xLTArBgNVBAMMJG92LWczYi1lbXVkaHJhLndlLWhhdmUtYS1wcm9ibGVtLmNv
|
||||
bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA1haD+95Albk0UN6YtDCPpqevTv
|
||||
YUmEhsIVpb/hnhsbBlH0yLthSE2JLH14ywiQJXttoc/pDWC9eoICW6CJXL2jgacw
|
||||
gaQwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYB
|
||||
BQUHAwEwLwYDVR0RBCgwJoIkb3YtZzNiLWVtdWRocmEud2UtaGF2ZS1hLXByb2Js
|
||||
ZW0uY29tMB0GA1UdDgQWBBQUhzc4s202Hn62G0srmgguYdY2MTAfBgNVHSMEGDAW
|
||||
gBSb2GVRr+fnLDetCO5pGdm/N4SuCjAKBggqhkjOPQQDAgNoADBlAjEAoJ6d4zzA
|
||||
xE349yGHHrwKZpVCC/HBFutcOpOfzQoe8ikbmaaBTdtKai5TEllwt3dBAjBeHGiX
|
||||
VheDhJfewbei99cw1MfYB5tPwdfuY5pe1MJz0Tg9zJ4JgxBvF6LTT0OOXZ4=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDZjCCAu2gAwIBAgIPbK2jZ/scEACIEg6iAtVoMAoGCCqGSM49BAMDMFYxCzAJ
|
||||
BgNVBAYTAklOMSUwIwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVk
|
||||
MSAwHgYDVQQDDBdlbVNpZ24gUm9vdCBUTFMgQ0EgLSBHMzAeFw0yNTA0MzAxNjAw
|
||||
MTJaFw00MDA0MjgxODMwMDBaMD8xCzAJBgNVBAYTAk5MMRMwEQYDVQQKDAplTXVk
|
||||
aHJhIEJWMRswGQYDVQQDDBJFTSBPViBUTFMgQ0EgLSBHM0IwdjAQBgcqhkjOPQIB
|
||||
BgUrgQQAIgNiAARziXqg6zz9yosSQC/68KvE4SErr43ud4rJ7i1tuD+sKAxeBWOK
|
||||
56Ks1xwyKo42HN659pxDArndjXPRzGyqsswK7k4hbOl53VGKSvS2JuVoTN1lgDft
|
||||
DIVOTMEvTPhkbQ2jggGWMIIBkjAfBgNVHSMEGDAWgBThOqshfPHjTVSu1HK3/YtZ
|
||||
QiCQVDAdBgNVHQ4EFgQUm9hlUa/n5yw3rQjuaRnZvzeErgowHQYDVR0lBBYwFAYI
|
||||
KwYBBQUHAwEGCCsGAQUFBwMCMF0GA1UdIARWMFQwOQYLKwYBBAGDjiEBAAEwKjAo
|
||||
BggrBgEFBQcCARYcaHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbTANBgsrBgEE
|
||||
AYOOIQECbjAIBgZngQwBAgIwdQYIKwYBBQUHAQEEaTBnMCQGCCsGAQUFBzABhhho
|
||||
dHRwOi8vb2NzcC1iLmVtc2lnbi5jb20wPwYIKwYBBQUHMAKGM2h0dHA6Ly9yZXBv
|
||||
c2l0b3J5LmVtc2lnbi5jb20vY2VydHMvRU1PVlRMU0NBRzNCLnA3YzA3BgNVHR8E
|
||||
MDAuMCygKqAohiZodHRwOi8vY3JsLmVtc2lnbi5jb20vP3Jvb3R0bHNjYWczLmNy
|
||||
bDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAKBggqhkjOPQQD
|
||||
AwNnADBkAjBAk+5BizdJhrED3wb7F2Gm7bWI60dlNd075nghT2TrR4NWKZtJZcbc
|
||||
28xxIY/NONICMF6mPdsjosaqmvEMWqKcBh4vsIvXSKnQj5o8zrFB4oKS+accjYTe
|
||||
8Dz3bQjauvy9uw==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDdDCCAvmgAwIBAgIQAJYaPvsTWVdu0EAFtGhKWzAKBggqhkjOPQQDAzBrMQsw
|
||||
CQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhy
|
||||
YSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290
|
||||
IENBIC0gRzMwHhcNMjQwNzExMTU0MzM2WhcNMzkwNzA5MTgzMDAwWjBWMQswCQYD
|
||||
VQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg
|
||||
MB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwdjAQBgcqhkjOPQIBBgUr
|
||||
gQQAIgNiAARfD9w/q1cWxL6D9bMGH6CQ4u+gAV6GN9s+PwQx9Bl4hC+0iU8QOk8m
|
||||
WO/RwXQy10DCSxT8vSIJClHKp+Ao4qJKdYeV0SwZ7LMQziyLfxIUOY6Grja/sQFw
|
||||
LKYf8EdDXAyjggF1MIIBcTAfBgNVHSMEGDAWgBR8XQKEE9TMipuBzhccLikenEhj
|
||||
QjAdBgNVHQ4EFgQU4TqrIXzx401UrtRyt/2LWUIgkFQwHQYDVR0lBBYwFAYIKwYB
|
||||
BQUHAwEGCCsGAQUFBwMCMD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIB
|
||||
FhxodHRwOi8vcmVwb3NpdG9yeS5lbXNpZ24uY29tMHgGCCsGAQUFBwEBBGwwajAi
|
||||
BggrBgEFBQcwAYYWaHR0cDovL29jc3AuZW1zaWduLmNvbTBEBggrBgEFBQcwAoY4
|
||||
aHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbS9jZXJ0cy9lbVNpZ25FQ0NSb290
|
||||
Q0FHMy5jcnQwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5lbXNpZ24uY29t
|
||||
P1Jvb3RDQUczLmNybDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIB
|
||||
ATAKBggqhkjOPQQDAwNpADBmAjEAt9CIpRGp46QHQYDUBnLKw+vvK0/h9vmilEAg
|
||||
TroUl/anH4x2rXHqiCtBTBfgKDegAjEA8WpG1LTZdAIL0FSYqxXeREaeTSxEofM0
|
||||
1jJH3jGvCXC79LU9UPJ2o3TU+UIj2gMV
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,38 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICQTCCAcegAwIBAgIULuWnWW6kFV6QZxp2ls8r18NCjXkwCgYIKoZIzj0EAwIw
|
||||
QDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC2VNdWRocmEgSW5jMRswGQYDVQQDDBJF
|
||||
TSBPViBUTFMgQ0EgLSBHM0MwHhcNMjYxMDIwMDIyMjQyWhcNMjcwNTAzMDIyNzQy
|
||||
WjAzMTEwLwYDVQQDDChvdi1nM2MtYWFhLWVtdWRocmEud2UtaGF2ZS1hLXByb2Js
|
||||
ZW0uY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEb7+5g2YHNYwH2eQERrZc
|
||||
TmVuf/RmvzsGGMSwXmZke6vn35Ut5HME7UOOU3Muy+BSfjfpo6fjL5KOs3Ndz67d
|
||||
OKOBqzCBqDAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAK
|
||||
BggrBgEFBQcDATAzBgNVHREELDAqgihvdi1nM2MtYWFhLWVtdWRocmEud2UtaGF2
|
||||
ZS1hLXByb2JsZW0uY29tMB0GA1UdDgQWBBQ7oBzq3jpTuvntbIkzMV12Ps3O0TAf
|
||||
BgNVHSMEGDAWgBQ06uoM68kK2vnQxeLtm3jJh54bbDAKBggqhkjOPQQDAgNoADBl
|
||||
AjEA/Y7AZ4M2MYbN5Od9tREaUfFjZKoOjczfIKPjKNMjtGxTdL9WUXvAT4QDgktn
|
||||
/IJdAjAbORTkAXD5hggyG6K7p0MBz3lFZJb2l/LndZlrttJWBLP8QGrc44hrZZtK
|
||||
Iq0d1dE=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzzCCAregAwIBAgIRAI1aSBGTnvRH13ERzdmEmukwDQYJKoZIhvcNAQELBQAw
|
||||
ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
|
||||
BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y
|
||||
ODEyMzEyMzU5NTlaMEAxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVkaHJhIElu
|
||||
YzEbMBkGA1UEAwwSRU0gT1YgVExTIENBIC0gRzNDMHYwEAYHKoZIzj0CAQYFK4EE
|
||||
ACIDYgAEE4JRzVKskpjByKrZdzId65fnCb0LTKCspV3IrVHc/J0ViaFZfjpRIOHv
|
||||
1jHGPhg1ysrimGmmLhYx6KKBgatWYW3EXZGKvAnzqw5jz3kqpNztc4e+N7ulaVSf
|
||||
HAKycO1no4IBNTCCATEwHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQw
|
||||
HQYDVR0OBBYEFDTq6gzryQra+dDF4u2beMmHnhtsMA4GA1UdDwEB/wQEAwIBBjAS
|
||||
BgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
|
||||
AjAxBgNVHSAEKjAoMAgGBmeBDAECAjANBgsrBgEEAYOOIQEAATANBgsrBgEEAYOO
|
||||
IQECbjBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9B
|
||||
QUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDA0BggrBgEFBQcBAQQoMCYwJAYIKwYB
|
||||
BQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG9w0BAQsFAAOC
|
||||
AQEAHlbhZpjhkPEAQVhPHRtBDaHqiisVD20pQ1TUeSx3l62Qe7NQ0b7vWQy7DN0i
|
||||
KOu7BGpP5EnIjvXbz9FhpfPJ4FhKEt3tQ51rqXz2oD8vRaryvExWPobDUz3j7HN6
|
||||
OzBsVMIe3PD7AAkpky0x8zMSi/ep5R9OMzybmNptbtppyuB5ytgq+h7lQdRIP+CQ
|
||||
TaJX3qTgfMqzntYre5GljmEB5hU2/rSn8zoSU2APkJidlZFpghtgbD4I2VRHZvCw
|
||||
FFr/SrFM9pwrgbJNZ1MtyTSVgI5NlNb0aMfLR+/r6jJt3ROeY2yh2QQ2dx+Yqgdb
|
||||
uL5ChFWOQU+ErSDnUq7yr+pjBQ==
|
||||
-----END CERTIFICATE-----
|
||||