package de.geolykt.starplane.remapping; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.tukaani.xz.XZInputStream; import net.fabricmc.mappingio.MappingFlag; import net.fabricmc.mappingio.MappingReader; import net.fabricmc.mappingio.MappingUtil; import net.fabricmc.mappingio.MappingVisitor; import net.fabricmc.mappingio.adapter.ForwardingMappingVisitor; import net.fabricmc.mappingio.format.MappingFormat; import net.fabricmc.mappingio.format.enigma.EnigmaFileReader; import net.fabricmc.mappingio.tree.MappingTree; import net.fabricmc.mappingio.tree.MemoryMappingTree; import kala.compress.archivers.tar.TarArchiveEntry; import kala.compress.archivers.tar.TarArchiveInputStream; public class MIOContainerFormat { private final static class ArchiveVisitor extends ForwardingMappingVisitor implements AutoCloseable { @NotNull private static MappingVisitor optionallyClone(@NotNull MappingVisitor visitor) { if (visitor.getFlags().contains(MappingFlag.NEEDS_ELEMENT_UNIQUENESS) || visitor.getFlags().contains(MappingFlag.NEEDS_MULTIPLE_PASSES)) { return new MemoryMappingTree(); } return visitor; } private boolean clonedVisitor; @NotNull private MappingVisitor originalVisitor; @Nullable private Boolean visitContent = null; public ArchiveVisitor(@NotNull MappingVisitor visitor) throws IOException { super(ArchiveVisitor.optionallyClone(visitor)); this.originalVisitor = visitor; this.clonedVisitor = visitor != this.next; if (this.visitHeader()) { this.visitNamespaces(MappingUtil.NS_SOURCE_FALLBACK, Collections.singletonList(MappingUtil.NS_TARGET_FALLBACK)); } } @Override public void close() throws IOException { if (super.visitEnd()) { if (this.clonedVisitor) { // visitEnd = true (final pass); cloned instance. ((MappingTree) this.next).accept(this.originalVisitor); } } else if (this.clonedVisitor) { // visitEnd = false (not the final pass); cloned instance. ((MappingTree) this.next).accept(this.originalVisitor); } else { // visitEnd = false; no clone. throw new IllegalStateException("super.visitEnd() returned true without NEEDS_MULTIPLE_PASSES having been declared."); } } @Override public boolean visitContent() throws IOException { Boolean visitContent = this.visitContent; if (visitContent == null) { return this.visitContent = super.visitContent(); } return visitContent; } @Override public boolean visitEnd() throws IOException { return true; } @Override public boolean visitHeader() throws IOException { return false; } } public static enum MappingContainer { PLAIN { @Override public void read(@NotNull MappingFormat format, @NotNull Path path, @NotNull MappingVisitor visitor) throws IOException { MappingReader.read(path, format, visitor); } }, TAR_XZ { @Override public void read(@NotNull MappingFormat format, @NotNull Path path, @NotNull MappingVisitor visitor) throws IOException { if (format != MappingFormat.ENIGMA_DIR) { throw new IllegalArgumentException("The TAR_XZ format is only supported for the ENIGMA_DIR format. Instead, the " + format + " is being used."); } try (ArchiveVisitor archiveVisitor = new ArchiveVisitor(visitor); InputStream rawIn = Files.newInputStream(path); XZInputStream decompressedIn = new XZInputStream(rawIn); TarArchiveInputStream tarIn = new TarArchiveInputStream(decompressedIn); InputStreamReader reader = new InputStreamReader(tarIn); BufferedReader bufferedReader = new BufferedReader(reader)) { TarArchiveEntry tarEntry; while ((tarEntry = tarIn.getNextEntry()) != null) { if (!tarEntry.getName().endsWith("." + MappingFormat.ENIGMA_FILE.fileExt)) { continue; // Skip non-*.mapping files. } EnigmaFileReader.read(bufferedReader, visitor); } } } }, XZ { @Override public void read(@NotNull MappingFormat format, @NotNull Path path, @NotNull MappingVisitor visitor) throws IOException { if (format == MappingFormat.ENIGMA_DIR) { throw new IllegalArgumentException("The XZ container format is not applicable to the " + format + " mapping format."); } try (InputStream rawIn = Files.newInputStream(path); XZInputStream decompressedIn = new XZInputStream(rawIn); InputStreamReader reader = new InputStreamReader(decompressedIn); BufferedReader bufferedReader = new BufferedReader(reader)) { MappingReader.read(bufferedReader, format, visitor); } } }; public abstract void read(@NotNull MappingFormat format, @NotNull Path path, @NotNull MappingVisitor visitor) throws IOException; } @NotNull public final MappingContainer containerFormat; @NotNull public final MappingFormat coreFormat; public MIOContainerFormat(@NotNull MappingFormat coreFormat, @NotNull MappingContainer containerFormat) { this.coreFormat = coreFormat; this.containerFormat = containerFormat; } @Override public boolean equals(Object obj) { if (obj instanceof MIOContainerFormat) { MIOContainerFormat other = (MIOContainerFormat) obj; return this.containerFormat == other.containerFormat && this.coreFormat == other.coreFormat; } else { return false; } } @Override public int hashCode() { return this.containerFormat.hashCode() ^ this.coreFormat.hashCode(); } public void read(@NotNull Path path, @NotNull MappingVisitor visitor) throws IOException { this.containerFormat.read(this.coreFormat, path, visitor); } @Override public String toString() { return "MIOContainerFormat[core = " + this.coreFormat + ", container = " + this.containerFormat + "]"; } }