package net.minecraft.server.packs; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Sets; import com.mojang.logging.LogUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.repository.Pack; import net.minecraft.server.packs.resources.IoSupplier; import org.apache.commons.io.IOUtils; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; public class FilePackResources extends AbstractPackResources { static final Logger LOGGER = LogUtils.getLogger(); private final FilePackResources.SharedZipFileAccess zipFileAccess; private final String prefix; FilePackResources(PackLocationInfo location, FilePackResources.SharedZipFileAccess zipFileAccess, String prefix) { super(location); this.zipFileAccess = zipFileAccess; this.prefix = prefix; } private static String getPathFromLocation(PackType packType, ResourceLocation location) { return String.format(Locale.ROOT, "%s/%s/%s", packType.getDirectory(), location.getNamespace(), location.getPath()); } @Nullable @Override public IoSupplier getRootResource(String... elements) { return this.getResource(String.join("/", elements)); } @Override public IoSupplier getResource(PackType packType, ResourceLocation location) { return this.getResource(getPathFromLocation(packType, location)); } private String addPrefix(String resourcePath) { return this.prefix.isEmpty() ? resourcePath : this.prefix + "/" + resourcePath; } @Nullable private IoSupplier getResource(String resourcePath) { ZipFile zipFile = this.zipFileAccess.getOrCreateZipFile(); if (zipFile == null) { return null; } else { ZipEntry zipEntry = zipFile.getEntry(this.addPrefix(resourcePath)); return zipEntry == null ? null : IoSupplier.create(zipFile, zipEntry); } } @Override public Set getNamespaces(PackType type) { ZipFile zipFile = this.zipFileAccess.getOrCreateZipFile(); if (zipFile == null) { return Set.of(); } else { Enumeration enumeration = zipFile.entries(); Set set = Sets.newHashSet(); String string = this.addPrefix(type.getDirectory() + "/"); while (enumeration.hasMoreElements()) { ZipEntry zipEntry = (ZipEntry)enumeration.nextElement(); String string2 = zipEntry.getName(); String string3 = extractNamespace(string, string2); if (!string3.isEmpty()) { if (ResourceLocation.isValidNamespace(string3)) { set.add(string3); } else { LOGGER.warn("Non [a-z0-9_.-] character in namespace {} in pack {}, ignoring", string3, this.zipFileAccess.file); } } } return set; } } @VisibleForTesting public static String extractNamespace(String directory, String name) { if (!name.startsWith(directory)) { return ""; } else { int i = directory.length(); int j = name.indexOf(47, i); return j == -1 ? name.substring(i) : name.substring(i, j); } } @Override public void close() { this.zipFileAccess.close(); } @Override public void listResources(PackType packType, String namespace, String path, PackResources.ResourceOutput resourceOutput) { ZipFile zipFile = this.zipFileAccess.getOrCreateZipFile(); if (zipFile != null) { Enumeration enumeration = zipFile.entries(); String string = this.addPrefix(packType.getDirectory() + "/" + namespace + "/"); String string2 = string + path + "/"; while (enumeration.hasMoreElements()) { ZipEntry zipEntry = (ZipEntry)enumeration.nextElement(); if (!zipEntry.isDirectory()) { String string3 = zipEntry.getName(); if (string3.startsWith(string2)) { String string4 = string3.substring(string.length()); ResourceLocation resourceLocation = ResourceLocation.tryBuild(namespace, string4); if (resourceLocation != null) { resourceOutput.accept(resourceLocation, IoSupplier.create(zipFile, zipEntry)); } else { LOGGER.warn("Invalid path in datapack: {}:{}, ignoring", namespace, string4); } } } } } } public static class FileResourcesSupplier implements Pack.ResourcesSupplier { private final File content; public FileResourcesSupplier(Path content) { this(content.toFile()); } public FileResourcesSupplier(File content) { this.content = content; } @Override public PackResources openPrimary(PackLocationInfo location) { FilePackResources.SharedZipFileAccess sharedZipFileAccess = new FilePackResources.SharedZipFileAccess(this.content); return new FilePackResources(location, sharedZipFileAccess, ""); } @Override public PackResources openFull(PackLocationInfo location, Pack.Metadata metadata) { FilePackResources.SharedZipFileAccess sharedZipFileAccess = new FilePackResources.SharedZipFileAccess(this.content); PackResources packResources = new FilePackResources(location, sharedZipFileAccess, ""); List list = metadata.overlays(); if (list.isEmpty()) { return packResources; } else { List list2 = new ArrayList(list.size()); for (String string : list) { list2.add(new FilePackResources(location, sharedZipFileAccess, string)); } return new CompositePackResources(packResources, list2); } } } static class SharedZipFileAccess implements AutoCloseable { final File file; @Nullable private ZipFile zipFile; private boolean failedToLoad; SharedZipFileAccess(File file) { this.file = file; } @Nullable ZipFile getOrCreateZipFile() { if (this.failedToLoad) { return null; } else { if (this.zipFile == null) { try { this.zipFile = new ZipFile(this.file); } catch (IOException var2) { FilePackResources.LOGGER.error("Failed to open pack {}", this.file, var2); this.failedToLoad = true; return null; } } return this.zipFile; } } public void close() { if (this.zipFile != null) { IOUtils.closeQuietly(this.zipFile); this.zipFile = null; } } protected void finalize() throws Throwable { this.close(); super.finalize(); } } }