diff --git a/.gitignore b/.gitignore index 85cf43e8..520b20ee 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,9 @@ /.classpath /.project /.idea/ -/.gradle \ No newline at end of file +/.gradle +mapping-io-extras/.classpath +mapping-io-extras/.project +mapping-io-extras/.settings/org.eclipse.buildship.core.prefs +mapping-io-extras/.settings/org.eclipse.jdt.core.prefs +mapping-io-extras/bin/ \ No newline at end of file diff --git a/src/main/java/net/fabricmc/mappingio/MappingReader.java b/src/main/java/net/fabricmc/mappingio/MappingReader.java index bc6cbbae..05b5cd69 100644 --- a/src/main/java/net/fabricmc/mappingio/MappingReader.java +++ b/src/main/java/net/fabricmc/mappingio/MappingReader.java @@ -37,6 +37,7 @@ import net.fabricmc.mappingio.format.enigma.EnigmaFileReader; import net.fabricmc.mappingio.format.intellij.MigrationMapFileReader; import net.fabricmc.mappingio.format.jobf.JobfFileReader; +import net.fabricmc.mappingio.format.pdme.PDMEFileReader; import net.fabricmc.mappingio.format.proguard.ProGuardFileReader; import net.fabricmc.mappingio.format.simple.RecafSimpleFileReader; import net.fabricmc.mappingio.format.srg.JamFileReader; @@ -137,6 +138,8 @@ private static MappingFormat detectFormat(Reader reader, @Nullable String fileEx return MappingFormat.PROGUARD_FILE; } else if (headerStr.contains("\n\t")) { return MappingFormat.TSRG_FILE; + } else if (headerStr.contains("\u00B6")) { + return MappingFormat.PDME_FILE; } if (fileExt != null) { @@ -323,6 +326,9 @@ public static void read(Reader reader, MappingFormat format, MappingVisitor visi case RECAF_SIMPLE_FILE: RecafSimpleFileReader.read(reader, visitor); break; + case PDME_FILE: + PDMEFileReader.read(reader, visitor); + break; case JOBF_FILE: JobfFileReader.read(reader, visitor); break; diff --git a/src/main/java/net/fabricmc/mappingio/MappingWriter.java b/src/main/java/net/fabricmc/mappingio/MappingWriter.java index bb98875f..ecc2dc0e 100644 --- a/src/main/java/net/fabricmc/mappingio/MappingWriter.java +++ b/src/main/java/net/fabricmc/mappingio/MappingWriter.java @@ -29,6 +29,7 @@ import net.fabricmc.mappingio.format.enigma.EnigmaFileWriter; import net.fabricmc.mappingio.format.intellij.MigrationMapFileWriter; import net.fabricmc.mappingio.format.jobf.JobfFileWriter; +import net.fabricmc.mappingio.format.pdme.PDMEFileWriter; import net.fabricmc.mappingio.format.proguard.ProGuardFileWriter; import net.fabricmc.mappingio.format.simple.RecafSimpleFileWriter; import net.fabricmc.mappingio.format.srg.CsrgFileWriter; @@ -68,6 +69,7 @@ static MappingWriter create(Writer writer, MappingFormat format) throws IOExcept case PROGUARD_FILE: return new ProGuardFileWriter(writer); case INTELLIJ_MIGRATION_MAP_FILE: return new MigrationMapFileWriter(writer); case RECAF_SIMPLE_FILE: return new RecafSimpleFileWriter(writer); + case PDME_FILE: return new PDMEFileWriter(writer); case JOBF_FILE: return new JobfFileWriter(writer); default: return null; } diff --git a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java index b281ce36..04fa9a87 100644 --- a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java +++ b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java @@ -253,6 +253,38 @@ public enum MappingFormat { .withSrcDescs(FeaturePresence.REQUIRED)) .withFileComments(true)), + /** + * The {@code Paragraph Delimited Mapping Extended} mapping format, as specified here. + * @implNote This implementation does not support the Include/Incluir or AccessFlag/BanderaDeAcceso types. + */ + PDME_FILE("Paragraph Delimited Mapping Extended", "pdme", true, + FeatureSetBuilder.create() + .withNamespaces(false) + .withElementMetadata(MetadataSupport.ARBITRARY) + .withClasses(c -> c + .withSrcNames(FeaturePresence.REQUIRED) + .withDstNames(FeaturePresence.REQUIRED) + .withRepackaging(true)) + .withFields(f -> f + .withSrcNames(FeaturePresence.REQUIRED) + .withSrcDescs(FeaturePresence.REQUIRED) + .withDstNames(FeaturePresence.REQUIRED)) + .withMethods(m -> m + .withSrcNames(FeaturePresence.REQUIRED) + .withDstNames(FeaturePresence.REQUIRED) + .withSrcDescs(FeaturePresence.REQUIRED)) + .withArgs(a -> a + .withLvIndices(FeaturePresence.REQUIRED) + .withSrcNames(FeaturePresence.OPTIONAL) + .withDstNames(FeaturePresence.REQUIRED)) + .withVars(v -> v + .withLvIndices(FeaturePresence.OPTIONAL) + .withLvtRowIndices(FeaturePresence.OPTIONAL) + .withStartOpIndices(FeaturePresence.OPTIONAL) + .withSrcNames(FeaturePresence.OPTIONAL) + .withDstNames(FeaturePresence.REQUIRED)) + .withFileComments(true)), + /** * The {@code JOBF} mapping format, as implemented here. * diff --git a/src/main/java/net/fabricmc/mappingio/format/pdme/PDMEFileReader.java b/src/main/java/net/fabricmc/mappingio/format/pdme/PDMEFileReader.java new file mode 100644 index 00000000..02b3e098 --- /dev/null +++ b/src/main/java/net/fabricmc/mappingio/format/pdme/PDMEFileReader.java @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2023 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingio.format.pdme; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import net.fabricmc.mappingio.MappedElementKind; +import net.fabricmc.mappingio.MappingFlag; +import net.fabricmc.mappingio.MappingUtil; +import net.fabricmc.mappingio.MappingVisitor; +import net.fabricmc.mappingio.format.MappingFormat; +import net.fabricmc.mappingio.tree.MemoryMappingTree; + +/** + * {@linkplain MappingFormat#PDME_FILE Paragraph Delimited Mappings Extended + * file} reader. + * + *

+ * Crashes if a second visit pass is requested without + * {@link MappingFlag#NEEDS_MULTIPLE_PASSES} having been passed beforehand. + */ +public final class PDMEFileReader { + private static final char DELIM = '\u00B6'; + private static final Pattern LOCAL_META_PATTERN = Pattern.compile("^-?\\d+:-?\\d+:-?\\d+:-?\\d+$"); + + private PDMEFileReader() { + } + + public static void read(Reader in, MappingVisitor visitor) throws IOException { + Set flags = visitor.getFlags(); + + MappingVisitor downstream = visitor; + MemoryMappingTree buffering = null; + + if (flags.contains(MappingFlag.NEEDS_ELEMENT_UNIQUENESS)) { + buffering = new MemoryMappingTree(); + visitor = buffering; + } + + BufferedReader br = new BufferedReader(in); + String header = br.readLine(); + + if (header == null) { + return; + } + + List lines = new ArrayList(); + + for (String l; (l = br.readLine()) != null;) { + lines.add(l); + } + + boolean multi = flags.contains(MappingFlag.NEEDS_MULTIPLE_PASSES); + + while (true) { + boolean h = visitor.visitHeader(); + + if (h) { + visitor.visitNamespaces(MappingUtil.NS_SOURCE_FALLBACK, + Collections.singletonList(MappingUtil.NS_TARGET_FALLBACK)); + } + + if (visitor.visitContent()) { + parseContent(lines, visitor); + } + + if (visitor.visitEnd()) { + break; + } + + if (!multi) { + throw new IllegalStateException("Repeated visitation requested without NEEDS_MULTIPLE_PASSES"); + } + } + + if (buffering != null) { + buffering.accept(downstream); + } + } + + private abstract static class Row { + abstract void emit(MappingVisitor v) throws IOException; + } + + private static final class ClassRow extends Row { + final String original; + final String mappedCol; + final String comment; + String finalMapped; + boolean resolved; + + ClassRow(String o, String m, String c) { + this.original = o; + this.mappedCol = m; + this.comment = c; + } + + void resolve(Map byOrig) { + if (resolved) { + return; + } + + resolved = true; + + if (original == null) { + return; + } + + int lastDollar = original.lastIndexOf('$'); + + if (lastDollar < 0) { + finalMapped = (mappedCol != null ? mappedCol : original); + return; + } + + String outerOrig = original.substring(0, lastDollar); + String tailOrig = original.substring(lastDollar + 1); + + ClassRow outer = byOrig.get(outerOrig); + + if (outer != null) { + outer.resolve(byOrig); + } + + String outerFinal = (outer != null && outer.finalMapped != null) ? outer.finalMapped : outerOrig; + + if (mappedCol == null) { + finalMapped = outerFinal + '$' + tailOrig; + } else { + if (isSimpleTail(mappedCol)) { + finalMapped = outerFinal + '$' + mappedCol; + } else { + finalMapped = mappedCol; + } + } + } + + private static boolean isSimpleTail(String s) { + return s.indexOf('$') < 0 && s.indexOf('.') < 0; + } + + @Override + void emit(MappingVisitor v) throws IOException { + if (original == null) { + return; + } + + if (finalMapped == null) { + finalMapped = original; + } + + String slashOrig = original.replace('.', '/'); + String slashMapped = original.equals(finalMapped) ? null : finalMapped.replace('.', '/'); + + if (v.visitClass(slashOrig)) { + if (slashMapped != null) { + v.visitDstName(MappedElementKind.CLASS, 0, slashMapped); + } + + if (v.visitElementContent(MappedElementKind.CLASS) && comment != null) { + v.visitComment(MappedElementKind.CLASS, comment); + } + } + } + } + + private static final class FieldRow extends Row { + final String original; + final String mapped; + final String comment; + + FieldRow(String o, String m, String c) { + this.original = o; + this.mapped = m; + this.comment = c; + } + + @Override + void emit(MappingVisitor v) throws IOException { + if (original == null) { + return; + } + + int colon = original.lastIndexOf(':'); + + if (colon < 0) { + return; + } + + String desc = original.substring(colon + 1); + String ownerAndName = original.substring(0, colon); + int lastDot = ownerAndName.lastIndexOf('.'); + + if (lastDot < 0) { + return; + } + + String clsDotted = ownerAndName.substring(0, lastDot); + String fieldName = ownerAndName.substring(lastDot + 1); + String slashClass = clsDotted.replace('.', '/'); + + if (v.visitClass(slashClass)) { + v.visitElementContent(MappedElementKind.CLASS); + } + + if (v.visitField(fieldName, desc)) { + if (mapped != null) { + v.visitDstName(MappedElementKind.FIELD, 0, mapped); + } + + if (v.visitElementContent(MappedElementKind.FIELD) && comment != null) { + v.visitComment(MappedElementKind.FIELD, comment); + } + } + } + } + + private static final class MethodRow extends Row { + final String original; + final String mapped; + final String comment; + + MethodRow(String o, String m, String c) { + this.original = o; + this.mapped = m; + this.comment = c; + } + + @Override + void emit(MappingVisitor v) throws IOException { + if (original == null) { + return; + } + + int paren = original.indexOf('('); + + if (paren < 0) { + return; + } + + int lastDot = original.lastIndexOf('.', paren); + + if (lastDot < 0) { + return; + } + + String clsDotted = original.substring(0, lastDot); + String methodName = original.substring(lastDot + 1, paren); + String methodDesc = original.substring(paren); + String slashClass = clsDotted.replace('.', '/'); + + if (v.visitClass(slashClass)) { + v.visitElementContent(MappedElementKind.CLASS); + } + + if (v.visitMethod(methodName, methodDesc)) { + if (mapped != null) { + v.visitDstName(MappedElementKind.METHOD, 0, mapped); + } + + if (v.visitElementContent(MappedElementKind.METHOD) && comment != null) { + v.visitComment(MappedElementKind.METHOD, comment); + } + } + } + } + + private static final class ParamRow extends Row { + final String def; + final String posStr; + final String mappedName; + final String comment; + final String sourceName; + final String localMeta; + + ParamRow(String d, String p, String mapped, String c, String sourceName, String localMeta) { + this.def = d; + this.posStr = p; + this.mappedName = mapped; + this.comment = c; + this.sourceName = sourceName; + this.localMeta = localMeta; + } + + @Override + void emit(MappingVisitor v) throws IOException { + if (def == null || posStr == null || mappedName == null) { + return; + } + + int paren = def.indexOf('('); + + if (paren < 0) { + return; + } + + int lastDot = def.lastIndexOf('.', paren); + + if (lastDot < 0) { + return; + } + + String clsDotted = def.substring(0, lastDot); + String methodName = def.substring(lastDot + 1, paren); + String methodDesc = def.substring(paren); + + int pos; + + try { + pos = Integer.parseInt(posStr); + } catch (NumberFormatException e) { + return; + } + + if (pos <= 0) { + return; + } + + String slashClass = clsDotted.replace('.', '/'); + + if (v.visitClass(slashClass)) { + v.visitElementContent(MappedElementKind.CLASS); + } + + if (v.visitMethod(methodName, methodDesc)) { + v.visitElementContent(MappedElementKind.METHOD); + } + + int paramCount = countParams(methodDesc); + + if (pos <= paramCount) { + int zeroIdx = pos - 1; + String src = (sourceName != null ? sourceName : mappedName); + + if (v.visitMethodArg(zeroIdx, -1, src)) { + boolean needsDst = (mappedName != null) && (sourceName == null || !mappedName.equals(sourceName)); + + if (needsDst) { + v.visitDstName(MappedElementKind.METHOD_ARG, 0, mappedName); + } + + if (v.visitElementContent(MappedElementKind.METHOD_ARG) && comment != null) { + v.visitComment(MappedElementKind.METHOD_ARG, comment); + } + } + } else { + int lvtRowIndex = 0; + int lvIndex = 0; + int start = 0; + int end = -1; + + if (localMeta != null) { + String[] parts = localMeta.split(":"); + + if (parts.length == 4) { + try { + lvtRowIndex = Integer.parseInt(parts[0]); + lvIndex = Integer.parseInt(parts[1]); + start = Integer.parseInt(parts[2]); + end = Integer.parseInt(parts[3]); + } catch (NumberFormatException e) { + return; + } + } else { + return; + } + } else { + return; + } + + String src = (sourceName != null ? sourceName : mappedName); + + if (v.visitMethodVar(lvtRowIndex, lvIndex, start, end, src)) { + boolean needsDst = (mappedName != null) && (sourceName == null || !mappedName.equals(sourceName)); + + if (needsDst) { + v.visitDstName(MappedElementKind.METHOD_VAR, 0, mappedName); + } + + if (v.visitElementContent(MappedElementKind.METHOD_VAR) && comment != null) { + v.visitComment(MappedElementKind.METHOD_VAR, comment); + } + } + } + } + } + + private static void parseContent(List lines, MappingVisitor v) throws IOException { + Map classRowsByOrig = new LinkedHashMap(); + List otherRows = new ArrayList(); + + for (int i = 0; i < lines.size(); i++) { + String raw = lines.get(i); + + if (raw == null) { + continue; + } + + String trimmed = raw.trim(); + + if (trimmed.isEmpty() || trimmed.startsWith("#")) { + continue; + } + + String[] cols = splitToSix(trimmed); + String tipo = cols[0]; + String originalCol = normaliseEmpty(cols[1]); + String nuevo = normaliseEmpty(cols[2]); + String def = normaliseEmpty(cols[3]); + String pos = normaliseEmpty(cols[4]); + String desc = normaliseEmpty(cols[5]); + + if ("Class".equals(tipo)) { + classRowsByOrig.put(originalCol, new ClassRow(originalCol, nuevo, desc)); + } else if ("Var".equals(tipo)) { + otherRows.add(new FieldRow(originalCol, nuevo, desc)); + } else if ("Def".equals(tipo)) { + otherRows.add(new MethodRow(originalCol, nuevo, desc)); + } else if ("Param".equals(tipo)) { + String sourceName = null; + + String localMeta = null; + + if (originalCol != null) { + if (LOCAL_META_PATTERN.matcher(originalCol).matches()) { + localMeta = originalCol; + } else { + sourceName = originalCol; + } + } + + otherRows.add(new ParamRow(def, pos, nuevo, desc, sourceName, localMeta)); + } + } + + for (ClassRow cr : classRowsByOrig.values()) { + cr.resolve(classRowsByOrig); + } + + for (ClassRow cr : classRowsByOrig.values()) { + cr.emit(v); + } + + for (Row r : otherRows) { + r.emit(v); + } + } + + private static String[] splitToSix(String line) { + String[] arr = line.split(Pattern.quote(String.valueOf(DELIM)), -1); + + if (arr.length == 6) { + return arr; + } + + if (arr.length < 6) { + String[] six = new String[6]; + + System.arraycopy(arr, 0, six, 0, arr.length); + + for (int i = arr.length; i < 6; i++) { + six[i] = ""; + } + + return six; + } + + StringBuilder desc = new StringBuilder(arr[5]); + + for (int i = 6; i < arr.length; i++) { + if (desc.length() > 0) { + desc.append(DELIM); + } + + desc.append(arr[i]); + } + + String[] six = new String[6]; + System.arraycopy(arr, 0, six, 0, 5); + six[5] = desc.toString(); + return six; + } + + private static String normaliseEmpty(String s) { + return (s == null || s.isEmpty() || "nil".equals(s)) ? null : s; + } + + private static int countParams(String desc) { + if (desc == null || desc.length() == 0) { + return 0; + } + + int count = 0; + int i = 1; + + while (i < desc.length()) { + char c = desc.charAt(i); + + if (c == ')') { + break; + } + + if (c == 'L') { + int semi = desc.indexOf(';', i); + + if (semi < 0) { + break; + } + + i = semi + 1; + count++; + } else if (c == '[') { + i++; + + while (i < desc.length() && desc.charAt(i) == '[') { + i++; + } + + if (i < desc.length() && desc.charAt(i) == 'L') { + int semi = desc.indexOf(';', i); + + if (semi < 0) { + break; + } + + i = semi + 1; + } else { + i++; + } + + count++; + } else { + i++; + count++; + } + } + + return count; + } +} diff --git a/src/main/java/net/fabricmc/mappingio/format/pdme/PDMEFileWriter.java b/src/main/java/net/fabricmc/mappingio/format/pdme/PDMEFileWriter.java new file mode 100644 index 00000000..18fe7db9 --- /dev/null +++ b/src/main/java/net/fabricmc/mappingio/format/pdme/PDMEFileWriter.java @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2023 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingio.format.pdme; + +import java.io.IOException; +import java.io.Writer; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.mappingio.MappedElementKind; +import net.fabricmc.mappingio.MappingFlag; +import net.fabricmc.mappingio.MappingWriter; +import net.fabricmc.mappingio.format.MappingFormat; + +/** + * {@linkplain MappingFormat#PDME_FILE Paragraph Delimited Mappings Extended + * file} writer. + */ +public final class PDMEFileWriter implements MappingWriter { + private static final char DELIM = '\u00B6'; + + private final Writer out; + + private String currentClassSlash; + private String currentMethodName; + private String currentMethodDesc; + + private int currentMethodParamCount; + private int nextLocalPos1; + + private String dstClassFullSlash; + private String dstMemberName; + private String dstParamName; + + private String fieldSrcName; + private String fieldSrcDesc; + + private String paramSrcName; + private int paramOrLocalPos1; + private boolean stagingParam; + private StringBuilder stagedRow; + private MappedElementKind stagedKind; + + public PDMEFileWriter(Writer out) { + this.out = out; + } + + @Override + public Set getFlags() { + return EnumSet.of(MappingFlag.NEEDS_HEADER_METADATA, MappingFlag.NEEDS_SRC_FIELD_DESC, + MappingFlag.NEEDS_SRC_METHOD_DESC); + } + + @Override + public void visitNamespaces(String srcNamespace, List dstNamespaces) throws IOException { + out.write("tipo" + DELIM + "original" + DELIM + "nuevo" + DELIM + "def" + DELIM + "pos" + DELIM + "desc\n"); + } + + @Override + public boolean visitClass(String srcName) throws IOException { + flushStaged(); + currentClassSlash = srcName; + currentMethodName = null; + currentMethodDesc = null; + currentMethodParamCount = 0; + nextLocalPos1 = 0; + dstClassFullSlash = null; + dstMemberName = null; + dstParamName = null; + fieldSrcName = null; + fieldSrcDesc = null; + paramSrcName = null; + stagingParam = false; + return true; + } + + @Override + public boolean visitField(String srcName, String srcDesc) throws IOException { + flushStaged(); + + if (srcDesc == null) { + return false; + } + + fieldSrcName = srcName; + fieldSrcDesc = srcDesc; + dstMemberName = null; + return true; + } + + @Override + public boolean visitMethod(String srcName, String srcDesc) throws IOException { + flushStaged(); + + if (srcDesc == null) { + return false; + } + + currentMethodName = srcName; + currentMethodDesc = srcDesc; + currentMethodParamCount = countParams(srcDesc); + nextLocalPos1 = currentMethodParamCount + 1; + dstMemberName = null; + dstParamName = null; + paramSrcName = null; + stagingParam = false; + return true; + } + + @Override + public boolean visitMethodArg(int argPosition, int lvIndex, @Nullable String srcName) throws IOException { + flushStaged(); + paramSrcName = srcName; + dstParamName = null; + paramOrLocalPos1 = argPosition + 1; + stagingParam = true; + return true; + } + + @Override + public boolean visitMethodVar(int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, @Nullable String srcName) + throws IOException { + flushStaged(); + paramSrcName = srcName; + dstParamName = null; + + if (nextLocalPos1 == 0) { + nextLocalPos1 = currentMethodParamCount + 1; + } + + paramOrLocalPos1 = nextLocalPos1++; + stagingParam = true; + return true; + } + + @Override + public void visitDstName(MappedElementKind kind, int namespace, String name) { + if (namespace != 0 || name == null) { + return; + } + + switch (kind) { + case CLASS: + dstClassFullSlash = name; + break; + case FIELD: + case METHOD: + dstMemberName = name; + break; + case METHOD_ARG: + case METHOD_VAR: + dstParamName = name; + break; + default: + break; + } + } + + @Override + public boolean visitElementContent(MappedElementKind kind) throws IOException { + switch (kind) { + case CLASS: + stageClass(); + return true; + case FIELD: + stageField(); + return true; + case METHOD: + stageMethod(); + return true; + case METHOD_ARG: + case METHOD_VAR: + return stageParam(); + default: + return false; + } + } + + private void stageClass() { + String origFull = currentClassSlash.replace('/', '.'); + String mappedFull = (dstClassFullSlash != null ? dstClassFullSlash : currentClassSlash).replace('/', '.'); + + stagedRow = begin("Class").append(origFull).append(DELIM).append(mappedFull).append(DELIM).append("nil") + .append(DELIM).append("nil").append(DELIM); + stagedKind = MappedElementKind.CLASS; + dstClassFullSlash = null; + } + + private void stageField() { + String cls = currentClassSlash.replace('/', '.'); + String mapped = (dstMemberName != null ? dstMemberName : fieldSrcName); + + stagedRow = begin("Var").append(cls).append('.').append(fieldSrcName).append(':').append(fieldSrcDesc) + .append(DELIM).append(mapped).append(DELIM).append("nil").append(DELIM).append("nil").append(DELIM); + stagedKind = MappedElementKind.FIELD; + dstMemberName = null; + } + + private void stageMethod() { + String cls = currentClassSlash.replace('/', '.'); + String mapped = (dstMemberName != null ? dstMemberName : currentMethodName); + + stagedRow = begin("Def").append(cls).append('.').append(currentMethodName).append(currentMethodDesc) + .append(DELIM).append(mapped).append(DELIM).append("nil").append(DELIM).append("nil").append(DELIM); + stagedKind = MappedElementKind.METHOD; + dstMemberName = null; + } + + private boolean stageParam() { + if (!stagingParam) { + return false; + } + + boolean hasSrc = paramSrcName != null && paramSrcName.length() > 0; + boolean hasDst = dstParamName != null && dstParamName.length() > 0; + + if (!hasSrc && !hasDst) { + dstParamName = null; + paramSrcName = null; + return false; + } + + String originalCol = hasSrc ? paramSrcName : "nil"; + String chosen = hasDst ? dstParamName : paramSrcName; + + String clsDotted = currentClassSlash.replace('/', '.'); + String def = clsDotted + '.' + currentMethodName + currentMethodDesc; + + stagedRow = begin("Param").append(originalCol).append(DELIM).append(chosen).append(DELIM).append(def) + .append(DELIM).append(paramOrLocalPos1).append(DELIM); + + stagedKind = MappedElementKind.METHOD_ARG; + dstParamName = null; + paramSrcName = null; + stagingParam = false; + return true; + } + + private StringBuilder begin(String tipo) { + return new StringBuilder().append(tipo).append(DELIM); + } + + @Override + public void visitComment(MappedElementKind kind, String comment) throws IOException { + if (stagedRow == null) { + return; + } + + if (stagedKind == kind || (stagedKind == MappedElementKind.METHOD_ARG + && (kind == MappedElementKind.METHOD_ARG || kind == MappedElementKind.METHOD_VAR))) { + if (comment != null && !comment.isEmpty()) { + String esc = comment.replaceAll("\\.+$", "").replace("\r\n", "\\n").replace("\n", "\\n").replace("\r", + "\\n"); + stagedRow.append(esc); + } + + out.write(stagedRow.toString()); + out.write('\n'); + stagedRow = null; + stagedKind = null; + } + } + + private void flushStaged() throws IOException { + if (stagedRow != null) { + out.write(stagedRow.toString()); + out.write('\n'); + stagedRow = null; + stagedKind = null; + } + + stagingParam = false; + } + + @Override + public void close() throws IOException { + flushStaged(); + out.close(); + } + + private static int countParams(String desc) { + if (desc == null || desc.length() == 0) { + return 0; + } + + int count = 0; + int i = 1; + + while (i < desc.length()) { + char c = desc.charAt(i); + + if (c == ')') { + break; + } + + if (c == 'L') { + int semi = desc.indexOf(';', i); + + if (semi < 0) { + break; + } + + i = semi + 1; + count++; + } else if (c == '[') { + i++; + + while (i < desc.length() && desc.charAt(i) == '[') { + i++; + } + + if (i < desc.length() && desc.charAt(i) == 'L') { + int semi = desc.indexOf(';', i); + + if (semi < 0) { + break; + } + + i = semi + 1; + } else { + i++; + } + + count++; + } else { + i++; + count++; + } + } + + return count; + } +} diff --git a/src/test/java/net/fabricmc/mappingio/test/TestUtil.java b/src/test/java/net/fabricmc/mappingio/test/TestUtil.java index 321d5aa7..54736c78 100644 --- a/src/test/java/net/fabricmc/mappingio/test/TestUtil.java +++ b/src/test/java/net/fabricmc/mappingio/test/TestUtil.java @@ -82,6 +82,8 @@ public static String getFileName(MappingFormat format) { return "migration-map.xml"; case RECAF_SIMPLE_FILE: return "recaf-simple.txt"; + case PDME_FILE: + return "pdme.pdme"; case JOBF_FILE: return "jobf.jobf"; default: @@ -111,6 +113,7 @@ public static org.cadixdev.lorenz.io.MappingFormat toLorenzFormat(MappingFormat case PROGUARD_FILE: case INTELLIJ_MIGRATION_MAP_FILE: case RECAF_SIMPLE_FILE: + case PDME_FILE: case JOBF_FILE: return null; default: @@ -142,6 +145,7 @@ public static IMappingFile.Format toSrgUtilsFormat(MappingFormat format) { case JAM_FILE: case INTELLIJ_MIGRATION_MAP_FILE: case RECAF_SIMPLE_FILE: + case PDME_FILE: case JOBF_FILE: return null; default: diff --git a/src/test/java/net/fabricmc/mappingio/test/tests/reading/EmptyContentReadTest.java b/src/test/java/net/fabricmc/mappingio/test/tests/reading/EmptyContentReadTest.java index d67899d8..ed96b507 100644 --- a/src/test/java/net/fabricmc/mappingio/test/tests/reading/EmptyContentReadTest.java +++ b/src/test/java/net/fabricmc/mappingio/test/tests/reading/EmptyContentReadTest.java @@ -29,6 +29,7 @@ import net.fabricmc.mappingio.format.enigma.EnigmaFileReader; import net.fabricmc.mappingio.format.intellij.MigrationMapFileReader; import net.fabricmc.mappingio.format.jobf.JobfFileReader; +import net.fabricmc.mappingio.format.pdme.PDMEFileReader; import net.fabricmc.mappingio.format.proguard.ProGuardFileReader; import net.fabricmc.mappingio.format.simple.RecafSimpleFileReader; import net.fabricmc.mappingio.format.srg.JamFileReader; @@ -138,6 +139,11 @@ public void emptyRecafSimpleFile() throws Exception { RecafSimpleFileReader.read(new StringReader(""), target); } + @Test + public void emptyPDMEFile() throws Exception { + PDMEFileReader.read(new StringReader("tipo¶original¶nuevo¶def¶pos¶desc"), target); + } + @Test public void emptyJobfFile() throws Exception { JobfFileReader.read(new StringReader(""), target); diff --git a/src/test/resources/detection/pdme.pdme b/src/test/resources/detection/pdme.pdme new file mode 100644 index 00000000..9f94c6fd --- /dev/null +++ b/src/test/resources/detection/pdme.pdme @@ -0,0 +1 @@ +type¶original¶new¶method¶position¶description \ No newline at end of file diff --git a/src/test/resources/outer-class-name-propagation/propagated-except-remapped-dst/pdme.pdme b/src/test/resources/outer-class-name-propagation/propagated-except-remapped-dst/pdme.pdme new file mode 100644 index 00000000..6e982a71 --- /dev/null +++ b/src/test/resources/outer-class-name-propagation/propagated-except-remapped-dst/pdme.pdme @@ -0,0 +1,7 @@ +tipo¶original¶nuevo¶def¶pos¶desc +Class¶class_1¶class1Ns0Rename¶nil¶nil¶ +Var¶class_1.field_1:Lclass_1;¶field_1¶nil¶nil¶ +Class¶class_1$class_2¶class1Ns0Rename$class_2¶nil¶nil¶ +Var¶class_1$class_2.field_2:Lclass_1$class_2;¶field_2¶nil¶nil¶ +Class¶class_1$class_2$class_3¶class1Ns0Rename$class_2$class_3¶nil¶nil¶ +Var¶class_1$class_2$class_3.field_2:Lclass_1$class_2$class_3;¶field_2¶nil¶nil¶ diff --git a/src/test/resources/outer-class-name-propagation/propagated/pdme.pdme b/src/test/resources/outer-class-name-propagation/propagated/pdme.pdme new file mode 100644 index 00000000..6e982a71 --- /dev/null +++ b/src/test/resources/outer-class-name-propagation/propagated/pdme.pdme @@ -0,0 +1,7 @@ +tipo¶original¶nuevo¶def¶pos¶desc +Class¶class_1¶class1Ns0Rename¶nil¶nil¶ +Var¶class_1.field_1:Lclass_1;¶field_1¶nil¶nil¶ +Class¶class_1$class_2¶class1Ns0Rename$class_2¶nil¶nil¶ +Var¶class_1$class_2.field_2:Lclass_1$class_2;¶field_2¶nil¶nil¶ +Class¶class_1$class_2$class_3¶class1Ns0Rename$class_2$class_3¶nil¶nil¶ +Var¶class_1$class_2$class_3.field_2:Lclass_1$class_2$class_3;¶field_2¶nil¶nil¶ diff --git a/src/test/resources/outer-class-name-propagation/unpropagated/pdme.pdme b/src/test/resources/outer-class-name-propagation/unpropagated/pdme.pdme new file mode 100644 index 00000000..301d02d5 --- /dev/null +++ b/src/test/resources/outer-class-name-propagation/unpropagated/pdme.pdme @@ -0,0 +1,7 @@ +tipo¶original¶nuevo¶def¶pos¶desc +Class¶class_1¶class1Ns0Rename¶nil¶nil¶ +Var¶class_1.field_1:Lclass_1;¶field_1¶nil¶nil¶ +Class¶class_1$class_2¶class_1$class_2¶nil¶nil¶ +Var¶class_1$class_2.field_2:Lclass_1$class_2;¶field_2¶nil¶nil¶ +Class¶class_1$class_2$class_3¶class_1$class_2$class_3¶nil¶nil¶ +Var¶class_1$class_2$class_3.field_2:Lclass_1$class_2$class_3;¶field_2¶nil¶nil¶ diff --git a/src/test/resources/reading/holes/pdme.pdme b/src/test/resources/reading/holes/pdme.pdme new file mode 100644 index 00000000..44afec18 --- /dev/null +++ b/src/test/resources/reading/holes/pdme.pdme @@ -0,0 +1,47 @@ +tipo¶original¶nuevo¶def¶pos¶desc +Class¶class_1¶class_1¶nil¶nil¶ +Class¶package_2.class_2¶package_2.class_2¶nil¶nil¶ +Class¶class_3¶package_3.class3Ns0Rename¶nil¶nil¶ +Class¶package_4.class_4¶package_4.class_4¶nil¶nil¶ +Class¶class_5¶class_5¶nil¶nil¶This is a comment +Class¶package_6.class_6¶package_7.class6Ns0Rename¶nil¶nil¶This is a comment +Class¶class_7¶class_7¶nil¶nil¶This is a comment +Class¶package_8.class_8¶package_8.class_8¶nil¶nil¶ +Class¶class_9¶class_9¶nil¶nil¶ +Class¶class_9$class_10¶class_9$class10Ns0Rename¶nil¶nil¶ +Class¶class_11$class_12¶class_11$class_12¶nil¶nil¶ +Class¶class_13$class_14¶class_13$class_14¶nil¶nil¶This is a comment +Class¶class_15$class_16¶class_15$class16Ns0Rename¶nil¶nil¶This is a comment +Class¶class_17$class_18¶class_17$class_18¶nil¶nil¶This is a comment +Class¶class_19¶class_19¶nil¶nil¶ +Class¶package_20.class_20¶package_20.class_20¶nil¶nil¶ +Class¶package_20.class_20$class_21$class_22¶package_20.class_20$class_21$class22Ns0Rename¶nil¶nil¶ +Class¶class_23$class_24$class_25¶class_23$class_24$class_25¶nil¶nil¶ +Class¶package_26.class_26$class_27$class_28¶package_26.class_26$class_27$class_28¶nil¶nil¶This is a comment +Class¶class_29$class_30$class_31¶class_29$class_30$class31Ns0Rename¶nil¶nil¶This is a comment +Class¶package_32.class_32$class_33$class_34¶package_32.class_32$class_33$class_34¶nil¶nil¶This is a comment +Class¶class_35¶class_35¶nil¶nil¶ +Var¶class_35.field_1:I¶field_1¶nil¶nil¶ +Var¶class_35.field_2:Lcls;¶field2Ns0Rename¶nil¶nil¶ +Var¶class_35.field_3:Lpkg/cls;¶field_3¶nil¶nil¶ +Var¶class_35.field_4:[I¶field_4¶nil¶nil¶This is a comment +Var¶class_35.field_6:I¶field_6¶nil¶nil¶This is a comment +Def¶class_35.method_1()I¶method_1¶nil¶nil¶ +Def¶class_35.method_2(I)V¶method2Ns0Rename¶nil¶nil¶ +Def¶class_35.method_3(Lcls;)Lcls;¶method_3¶nil¶nil¶ +Def¶class_35.method_4(ILcls;)Lpkg/cls;¶method_4¶nil¶nil¶This is a comment +Def¶class_35.method_5(Lcls;[I)[[B¶method5Ns0Rename¶nil¶nil¶This is a comment +Def¶class_35.method_7()I¶method_7¶nil¶nil¶ +Param¶param_1¶param_1¶class_35.method_7()I¶1¶ +Param¶param_2¶param_2¶class_35.method_7()I¶3¶ +Param¶param_3¶param3Ns0Rename¶class_35.method_7()I¶5¶ +Param¶param_4¶param_4¶class_35.method_7()I¶7¶This is a comment +Param¶param_5¶param5Ns0Rename¶class_35.method_7()I¶9¶This is a comment +Param¶param_6¶param_6¶class_35.method_7()I¶11¶This is a comment +Def¶class_35.method_8(I)V¶method_8¶nil¶nil¶ +Param¶var_1¶var_1¶class_35.method_8(I)V¶2¶ +Param¶var_2¶var_2¶class_35.method_8(I)V¶3¶ +Param¶var_3¶var3Ns0Rename¶class_35.method_8(I)V¶4¶ +Param¶var_4¶var_4¶class_35.method_8(I)V¶5¶This is a comment +Param¶var_5¶var5Ns0Rename¶class_35.method_8(I)V¶6¶This is a comment +Param¶var_6¶var_6¶class_35.method_8(I)V¶7¶This is a comment diff --git a/src/test/resources/reading/repeated-elements/enigma.mappings b/src/test/resources/reading/repeated-elements/enigma.mappings index 2df119c8..0f0ec6ec 100644 --- a/src/test/resources/reading/repeated-elements/enigma.mappings +++ b/src/test/resources/reading/repeated-elements/enigma.mappings @@ -1,4 +1,3 @@ -CLASS class_1 class1Ns0Rename0 CLASS class_1 class1Ns0Rename FIELD field_1 field1Ns0Rename0 I FIELD field_1 field1Ns0Rename I @@ -6,13 +5,8 @@ CLASS class_1 class1Ns0Rename METHOD method_1 method1Ns0Rename ()I ARG 1 param1Ns0Rename0 ARG 1 param1Ns0Rename - CLASS class_2 class2Ns0Rename0 - CLASS class_2 class2Ns0Rename1 - COMMENT This is a comment. - CLASS class_2 class2Ns0Rename2 CLASS class_2 class2Ns0Rename COMMENT This is a comment FIELD field_2 field2Ns0Rename0 Lcls; FIELD field_2 field2Ns0Rename Lcls; -CLASS class_3 package_3/class3Ns0Rename0 CLASS class_3 package_3/class3Ns0Rename diff --git a/src/test/resources/reading/repeated-elements/pdme.pdme b/src/test/resources/reading/repeated-elements/pdme.pdme new file mode 100644 index 00000000..4bd6f300 --- /dev/null +++ b/src/test/resources/reading/repeated-elements/pdme.pdme @@ -0,0 +1,17 @@ +tipo¶original¶nuevo¶def¶pos¶desc +Class¶class_1¶class1Ns0Rename0¶nil¶nil¶ +Class¶class_1¶class1Ns0Rename¶nil¶nil¶ +Var¶class_1.field_1:I¶field1Ns0Rename0¶nil¶nil¶ +Var¶class_1.field_1:I¶field1Ns0Rename¶nil¶nil¶ +Def¶class_1.method_1()I¶method1Ns0Rename0¶nil¶nil¶ +Def¶class_1.method_1()I¶method1Ns0Rename¶nil¶nil¶ +Param¶param_1¶param1Ns0Rename0¶class_1.method_1()I¶1¶ +Param¶param_1¶param1Ns0Rename¶class_1.method_1()I¶1¶ +Param¶var_1¶var1Ns0Rename0¶class_1.method_1()I¶1¶ +Param¶var_1¶var1Ns0Rename¶class_1.method_1()I¶2¶ +Class¶class_1$class_2¶class1Ns0Rename$class2Ns0Rename0¶nil¶nil¶ +Class¶class_1$class_2¶class1Ns0Rename$class2Ns0Rename¶nil¶nil¶This is a comment +Var¶class_1$class_2.field_2:Lcls;¶field2Ns0Rename0¶nil¶nil¶ +Var¶class_1$class_2.field_2:Lcls;¶field2Ns0Rename¶nil¶nil¶ +Class¶class_3¶package_3.class3Ns0Rename0¶nil¶nil¶ +Class¶class_3¶package_3.class3Ns0Rename¶nil¶nil¶ diff --git a/src/test/resources/reading/repeated-elements/srg.srg b/src/test/resources/reading/repeated-elements/srg.srg index 753ad3f2..e0661c02 100644 --- a/src/test/resources/reading/repeated-elements/srg.srg +++ b/src/test/resources/reading/repeated-elements/srg.srg @@ -1,5 +1,5 @@ CL: class_1 class1Ns0Rename0 -CL: class_1 class1Ns0Rename1 +CL: class_1 class1Ns0Rename FD: class_1/field_1 class1Ns0Rename/field1Ns0Rename0 FD: class_1/field_1 class1Ns0Rename/field1Ns0Rename MD: class_1/method_1 ()I class1Ns0Rename/method1Ns0Rename0 ()I diff --git a/src/test/resources/reading/valid/pdme.pdme b/src/test/resources/reading/valid/pdme.pdme new file mode 100644 index 00000000..91645287 --- /dev/null +++ b/src/test/resources/reading/valid/pdme.pdme @@ -0,0 +1,9 @@ +tipo¶original¶nuevo¶def¶pos¶desc +Class¶class_1¶class1Ns0Rename¶nil¶nil¶ +Var¶class_1.field_1:I¶field1Ns0Rename¶nil¶nil¶ +Def¶class_1.method_1()I¶method1Ns0Rename¶nil¶nil¶ +Param¶param_1¶param1Ns0Rename¶class_1.method_1()I¶1¶ +Param¶var_1¶var1Ns0Rename¶class_1.method_1()I¶1¶ +Class¶class_1$class_2¶class1Ns0Rename$class2Ns0Rename¶nil¶nil¶This is a comment +Var¶class_1$class_2.field_2:Lcls;¶field2Ns0Rename¶nil¶nil¶ +Class¶class_3¶package_3.class3Ns0Rename¶nil¶nil¶