package net.minecraft.client.renderer.texture; import com.mojang.blaze3d.platform.NativeImage; import com.mojang.logging.LogUtils; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.net.HttpURLConnection; import java.net.URI; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.OpenOption; import java.nio.file.Path; import java.util.concurrent.CompletableFuture; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.FileUtil; import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.ARGB; import org.slf4j.Logger; @Environment(EnvType.CLIENT) public class SkinTextureDownloader { private static final Logger LOGGER = LogUtils.getLogger(); private static final int SKIN_WIDTH = 64; private static final int SKIN_HEIGHT = 64; private static final int LEGACY_SKIN_HEIGHT = 32; public static CompletableFuture downloadAndRegisterSkin(ResourceLocation textureLocation, Path path, String url, boolean isLegacySkin) { return CompletableFuture.supplyAsync(() -> { NativeImage nativeImage; try { nativeImage = downloadSkin(path, url); } catch (IOException var5) { throw new UncheckedIOException(var5); } return isLegacySkin ? processLegacySkin(nativeImage, url) : nativeImage; }, Util.nonCriticalIoPool().forName("downloadTexture")).thenCompose(nativeImage -> registerTextureInManager(textureLocation, nativeImage)); } private static NativeImage downloadSkin(Path path, String url) throws IOException { if (Files.isRegularFile(path, new LinkOption[0])) { LOGGER.debug("Loading HTTP texture from local cache ({})", path); InputStream inputStream = Files.newInputStream(path); NativeImage var17; try { var17 = NativeImage.read(inputStream); } catch (Throwable var14) { if (inputStream != null) { try { inputStream.close(); } catch (Throwable var12) { var14.addSuppressed(var12); } } throw var14; } if (inputStream != null) { inputStream.close(); } return var17; } else { HttpURLConnection httpURLConnection = null; LOGGER.debug("Downloading HTTP texture from {} to {}", url, path); URI uRI = URI.create(url); NativeImage iOException; try { httpURLConnection = (HttpURLConnection)uRI.toURL().openConnection(Minecraft.getInstance().getProxy()); httpURLConnection.setDoInput(true); httpURLConnection.setDoOutput(false); httpURLConnection.connect(); int i = httpURLConnection.getResponseCode(); if (i / 100 != 2) { throw new IOException("Failed to open " + uRI + ", HTTP error code: " + i); } byte[] bs = httpURLConnection.getInputStream().readAllBytes(); try { FileUtil.createDirectoriesSafe(path.getParent()); Files.write(path, bs, new OpenOption[0]); } catch (IOException var13) { LOGGER.warn("Failed to cache texture {} in {}", url, path); } iOException = NativeImage.read(bs); } finally { if (httpURLConnection != null) { httpURLConnection.disconnect(); } } return iOException; } } private static CompletableFuture registerTextureInManager(ResourceLocation location, NativeImage image) { Minecraft minecraft = Minecraft.getInstance(); return CompletableFuture.supplyAsync(() -> { minecraft.getTextureManager().register(location, new DynamicTexture(location::toString, image)); return location; }, minecraft); } private static NativeImage processLegacySkin(NativeImage image, String url) { int i = image.getHeight(); int j = image.getWidth(); if (j == 64 && (i == 32 || i == 64)) { boolean bl = i == 32; if (bl) { NativeImage nativeImage = new NativeImage(64, 64, true); nativeImage.copyFrom(image); image.close(); image = nativeImage; nativeImage.fillRect(0, 32, 64, 32, 0); nativeImage.copyRect(4, 16, 16, 32, 4, 4, true, false); nativeImage.copyRect(8, 16, 16, 32, 4, 4, true, false); nativeImage.copyRect(0, 20, 24, 32, 4, 12, true, false); nativeImage.copyRect(4, 20, 16, 32, 4, 12, true, false); nativeImage.copyRect(8, 20, 8, 32, 4, 12, true, false); nativeImage.copyRect(12, 20, 16, 32, 4, 12, true, false); nativeImage.copyRect(44, 16, -8, 32, 4, 4, true, false); nativeImage.copyRect(48, 16, -8, 32, 4, 4, true, false); nativeImage.copyRect(40, 20, 0, 32, 4, 12, true, false); nativeImage.copyRect(44, 20, -8, 32, 4, 12, true, false); nativeImage.copyRect(48, 20, -16, 32, 4, 12, true, false); nativeImage.copyRect(52, 20, -8, 32, 4, 12, true, false); } setNoAlpha(image, 0, 0, 32, 16); if (bl) { doNotchTransparencyHack(image, 32, 0, 64, 32); } setNoAlpha(image, 0, 16, 64, 32); setNoAlpha(image, 16, 48, 48, 64); return image; } else { image.close(); throw new IllegalStateException("Discarding incorrectly sized (" + j + "x" + i + ") skin texture from " + url); } } private static void doNotchTransparencyHack(NativeImage image, int minX, int minY, int maxX, int maxY) { for (int i = minX; i < maxX; i++) { for (int j = minY; j < maxY; j++) { int k = image.getPixel(i, j); if (ARGB.alpha(k) < 128) { return; } } } for (int i = minX; i < maxX; i++) { for (int jx = minY; jx < maxY; jx++) { image.setPixel(i, jx, image.getPixel(i, jx) & 16777215); } } } private static void setNoAlpha(NativeImage image, int minX, int minY, int maxX, int maxY) { for (int i = minX; i < maxX; i++) { for (int j = minY; j < maxY; j++) { image.setPixel(i, j, ARGB.opaque(image.getPixel(i, j))); } } } }