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¶