diff options
| author | 2019-01-24 14:48:32 +0200 | |
|---|---|---|
| committer | 2019-01-24 13:48:32 +0100 | |
| commit | 00fcd0550fcdda621c2e4662f6ddd55ce673b931 (patch) | |
| tree | 6f9e4c24dbcc6d118fceec56adf7bf9d747a485c /src/main/java/cuchaz/enigma/mapping | |
| parent | mark as 0.13.0-SNAPSHOT for preliminary development (diff) | |
| download | enigma-fork-00fcd0550fcdda621c2e4662f6ddd55ce673b931.tar.gz enigma-fork-00fcd0550fcdda621c2e4662f6ddd55ce673b931.tar.xz enigma-fork-00fcd0550fcdda621c2e4662f6ddd55ce673b931.zip | |
[WIP] Mapping rework (#91)
* Move packages
* Mapping & entry refactor: first pass
* Fix deobf -> obf tree remapping
* Resolve various issues
* Give all entries the potential for parents and treat inner classes as children
* Deobf UI tree elements
* Tests pass
* Sort mapping output
* Fix delta tracking
* Index separation and first pass for #97
* Keep track of remapped jar index
* Fix child entries not being remapped
* Drop non-root entries
* Track dropped mappings
* Fix enigma mapping ordering
* EntryTreeNode interface
* Small tweaks
* Naive full index remap on rename
* Entries can resolve to more than one root entry
* Support alternative resolution strategies
* Bridge method resolution
* Tests pass
* Fix mappings being used where there are none
* Fix methods with different descriptors being considered unique. closes #89
Diffstat (limited to 'src/main/java/cuchaz/enigma/mapping')
33 files changed, 0 insertions, 4255 deletions
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java deleted file mode 100644 index 9c193ef..0000000 --- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java +++ /dev/null | |||
| @@ -1,627 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.collect.Maps; | ||
| 15 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 16 | import cuchaz.enigma.mapping.entry.FieldEntry; | ||
| 17 | import cuchaz.enigma.mapping.entry.MethodEntry; | ||
| 18 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 19 | |||
| 20 | import java.util.ArrayList; | ||
| 21 | import java.util.Map; | ||
| 22 | |||
| 23 | // FIXME: Enigma doesn't support inner classes of inner class????! | ||
| 24 | public class ClassMapping implements Comparable<ClassMapping> { | ||
| 25 | |||
| 26 | private String obfFullName; | ||
| 27 | private String obfSimpleName; | ||
| 28 | private String deobfName; | ||
| 29 | private String deobfFullName; | ||
| 30 | private String previousDeobfName; | ||
| 31 | private Map<String, ClassMapping> innerClassesByObfSimple; | ||
| 32 | private Map<String, ClassMapping> innerClassesByObfFull; | ||
| 33 | private Map<String, ClassMapping> innerClassesByDeobf; | ||
| 34 | private Map<String, FieldMapping> fieldsByObf; | ||
| 35 | private Map<String, FieldMapping> fieldsByDeobf; | ||
| 36 | private Map<String, MethodMapping> methodsByObf; | ||
| 37 | private Map<String, MethodMapping> methodsByDeobf; | ||
| 38 | private boolean isDirty; | ||
| 39 | private Mappings.EntryModifier modifier; | ||
| 40 | |||
| 41 | public ClassMapping(String obfFullName) { | ||
| 42 | this(obfFullName, null, Mappings.EntryModifier.UNCHANGED); | ||
| 43 | } | ||
| 44 | |||
| 45 | public ClassMapping(String obfFullName, String deobfName) { | ||
| 46 | this(obfFullName, deobfName, Mappings.EntryModifier.UNCHANGED); | ||
| 47 | } | ||
| 48 | |||
| 49 | public ClassMapping(String obfFullName, String deobfName, Mappings.EntryModifier modifier) { | ||
| 50 | this.obfFullName = obfFullName; | ||
| 51 | ClassEntry classEntry = new ClassEntry(obfFullName); | ||
| 52 | obfSimpleName = classEntry.isInnerClass() ? classEntry.getInnermostClassName() : classEntry.getSimpleName(); | ||
| 53 | previousDeobfName = null; | ||
| 54 | this.deobfName = NameValidator.validateClassName(deobfName, false); | ||
| 55 | innerClassesByObfSimple = Maps.newHashMap(); | ||
| 56 | innerClassesByObfFull = Maps.newHashMap(); | ||
| 57 | innerClassesByDeobf = Maps.newHashMap(); | ||
| 58 | fieldsByObf = Maps.newHashMap(); | ||
| 59 | fieldsByDeobf = Maps.newHashMap(); | ||
| 60 | methodsByObf = Maps.newHashMap(); | ||
| 61 | methodsByDeobf = Maps.newHashMap(); | ||
| 62 | isDirty = true; | ||
| 63 | this.modifier = modifier; | ||
| 64 | } | ||
| 65 | |||
| 66 | public static boolean isSimpleClassName(String name) { | ||
| 67 | return name.indexOf('/') < 0 && name.indexOf('$') < 0; | ||
| 68 | } | ||
| 69 | |||
| 70 | public String getObfFullName() { | ||
| 71 | return obfFullName; | ||
| 72 | } | ||
| 73 | |||
| 74 | public String getObfSimpleName() { | ||
| 75 | return obfSimpleName; | ||
| 76 | } | ||
| 77 | |||
| 78 | public String getPreviousDeobfName() { | ||
| 79 | return previousDeobfName; | ||
| 80 | } | ||
| 81 | |||
| 82 | public String getDeobfName() { | ||
| 83 | return deobfName; | ||
| 84 | } | ||
| 85 | |||
| 86 | public String getTranslatedName(TranslationDirection direction) { | ||
| 87 | return direction.choose(deobfName, obfFullName); | ||
| 88 | } | ||
| 89 | |||
| 90 | //// INNER CLASSES //////// | ||
| 91 | |||
| 92 | public void setDeobfName(String val) { | ||
| 93 | previousDeobfName = deobfName; | ||
| 94 | deobfName = NameValidator.validateClassName(val, false); | ||
| 95 | this.isDirty = true; | ||
| 96 | } | ||
| 97 | |||
| 98 | public Iterable<ClassMapping> innerClasses() { | ||
| 99 | assert (innerClassesByObfSimple.size() >= innerClassesByDeobf.size()); | ||
| 100 | return innerClassesByObfSimple.values(); | ||
| 101 | } | ||
| 102 | |||
| 103 | public void addInnerClassMapping(ClassMapping classMapping) throws MappingConflict { | ||
| 104 | // FIXME: dirty hack, that can get into issues, but it's a temp fix! | ||
| 105 | if (this.innerClassesByObfFull.containsKey(classMapping.getObfSimpleName())) { | ||
| 106 | throw new MappingConflict("classes", classMapping.getObfSimpleName(), this.innerClassesByObfSimple.get(classMapping.getObfSimpleName()).getObfSimpleName()); | ||
| 107 | } | ||
| 108 | innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping); | ||
| 109 | innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping); | ||
| 110 | |||
| 111 | if (classMapping.getDeobfName() != null) { | ||
| 112 | if (this.innerClassesByDeobf.containsKey(classMapping.getDeobfName())) { | ||
| 113 | throw new MappingConflict("classes", classMapping.getDeobfName(), this.innerClassesByDeobf.get(classMapping.getDeobfName()).getDeobfName()); | ||
| 114 | } | ||
| 115 | innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping); | ||
| 116 | } | ||
| 117 | this.isDirty = true; | ||
| 118 | } | ||
| 119 | |||
| 120 | public void removeInnerClassMapping(ClassMapping classMapping) { | ||
| 121 | innerClassesByObfFull.remove(classMapping.getObfFullName()); | ||
| 122 | boolean obfWasRemoved = innerClassesByObfSimple.remove(classMapping.getObfSimpleName()) != null; | ||
| 123 | assert (obfWasRemoved); | ||
| 124 | if (classMapping.getDeobfName() != null) { | ||
| 125 | boolean deobfWasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; | ||
| 126 | assert (deobfWasRemoved); | ||
| 127 | } | ||
| 128 | this.isDirty = true; | ||
| 129 | } | ||
| 130 | |||
| 131 | public ClassMapping getOrCreateInnerClass(ClassEntry obfInnerClass) { | ||
| 132 | ClassMapping classMapping = innerClassesByObfSimple.get(obfInnerClass.getInnermostClassName()); | ||
| 133 | if (classMapping == null) { | ||
| 134 | classMapping = new ClassMapping(obfInnerClass.getName()); | ||
| 135 | innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping); | ||
| 136 | boolean wasAdded = innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null; | ||
| 137 | assert (wasAdded); | ||
| 138 | this.isDirty = true; | ||
| 139 | } | ||
| 140 | return classMapping; | ||
| 141 | } | ||
| 142 | |||
| 143 | public ClassMapping getInnerClassByObfSimple(String obfSimpleName) { | ||
| 144 | assert (isSimpleClassName(obfSimpleName)); | ||
| 145 | return innerClassesByObfSimple.get(obfSimpleName); | ||
| 146 | } | ||
| 147 | |||
| 148 | public ClassMapping getInnerClassByDeobf(String deobfName) { | ||
| 149 | assert (isSimpleClassName(deobfName)); | ||
| 150 | return innerClassesByDeobf.get(deobfName); | ||
| 151 | } | ||
| 152 | |||
| 153 | public ClassMapping getInnerClassByDeobfThenObfSimple(String name) { | ||
| 154 | ClassMapping classMapping = getInnerClassByDeobf(name); | ||
| 155 | if (classMapping == null) { | ||
| 156 | classMapping = getInnerClassByObfSimple(name); | ||
| 157 | } | ||
| 158 | return classMapping; | ||
| 159 | } | ||
| 160 | |||
| 161 | public String getDeobfInnerClassName(String obfSimpleName) { | ||
| 162 | assert (isSimpleClassName(obfSimpleName)); | ||
| 163 | ClassMapping classMapping = innerClassesByObfSimple.get(obfSimpleName); | ||
| 164 | if (classMapping != null) { | ||
| 165 | return classMapping.getDeobfName(); | ||
| 166 | } | ||
| 167 | return null; | ||
| 168 | } | ||
| 169 | |||
| 170 | public void setInnerClassName(ClassEntry obfInnerClass, String deobfName) { | ||
| 171 | ClassMapping classMapping = getOrCreateInnerClass(obfInnerClass); | ||
| 172 | if (classMapping.getDeobfName() != null) { | ||
| 173 | boolean wasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; | ||
| 174 | assert (wasRemoved); | ||
| 175 | } | ||
| 176 | classMapping.setDeobfName(deobfName); | ||
| 177 | if (deobfName != null) { | ||
| 178 | assert (isSimpleClassName(deobfName)); | ||
| 179 | boolean wasAdded = innerClassesByDeobf.put(deobfName, classMapping) == null; | ||
| 180 | assert (wasAdded); | ||
| 181 | } | ||
| 182 | this.isDirty = true; | ||
| 183 | } | ||
| 184 | |||
| 185 | public boolean hasInnerClassByObfSimple(String obfSimpleName) { | ||
| 186 | return innerClassesByObfSimple.containsKey(obfSimpleName); | ||
| 187 | } | ||
| 188 | |||
| 189 | //// FIELDS //////// | ||
| 190 | |||
| 191 | public boolean hasInnerClassByDeobf(String deobfName) { | ||
| 192 | return innerClassesByDeobf.containsKey(deobfName); | ||
| 193 | } | ||
| 194 | |||
| 195 | public Iterable<FieldMapping> fields() { | ||
| 196 | assert (fieldsByObf.size() == fieldsByDeobf.size()); | ||
| 197 | return fieldsByObf.values(); | ||
| 198 | } | ||
| 199 | |||
| 200 | public boolean containsObfField(String obfName, TypeDescriptor obfDesc) { | ||
| 201 | return fieldsByObf.containsKey(getFieldKey(obfName, obfDesc)); | ||
| 202 | } | ||
| 203 | |||
| 204 | public boolean containsDeobfField(String deobfName, TypeDescriptor deobfDesc) { | ||
| 205 | return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfDesc)); | ||
| 206 | } | ||
| 207 | |||
| 208 | public void addFieldMapping(FieldMapping fieldMapping) { | ||
| 209 | String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc()); | ||
| 210 | if (fieldsByObf.containsKey(obfKey)) { | ||
| 211 | throw new Error("Already have mapping for " + obfFullName + "." + obfKey); | ||
| 212 | } | ||
| 213 | if (fieldMapping.getDeobfName() != null) { | ||
| 214 | String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc()); | ||
| 215 | if (fieldsByDeobf.containsKey(deobfKey)) { | ||
| 216 | throw new Error("Already have mapping for " + deobfName + "." + deobfKey); | ||
| 217 | } | ||
| 218 | boolean deobfWasAdded = fieldsByDeobf.put(deobfKey, fieldMapping) == null; | ||
| 219 | assert (deobfWasAdded); | ||
| 220 | } | ||
| 221 | boolean obfWasAdded = fieldsByObf.put(obfKey, fieldMapping) == null; | ||
| 222 | assert (obfWasAdded); | ||
| 223 | this.isDirty = true; | ||
| 224 | } | ||
| 225 | |||
| 226 | public void removeFieldMapping(FieldMapping fieldMapping) { | ||
| 227 | boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc())) != null; | ||
| 228 | assert (obfWasRemoved); | ||
| 229 | if (fieldMapping.getDeobfName() != null) { | ||
| 230 | boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) != null; | ||
| 231 | assert (deobfWasRemoved); | ||
| 232 | } | ||
| 233 | this.isDirty = true; | ||
| 234 | } | ||
| 235 | |||
| 236 | public FieldMapping getFieldByObf(String obfName, TypeDescriptor obfDesc) { | ||
| 237 | return fieldsByObf.get(getFieldKey(obfName, obfDesc)); | ||
| 238 | } | ||
| 239 | |||
| 240 | public FieldMapping getFieldByObf(FieldEntry field) { | ||
| 241 | return getFieldByObf(field.getName(), field.getDesc()); | ||
| 242 | } | ||
| 243 | |||
| 244 | public FieldMapping getFieldByDeobf(String deobfName, TypeDescriptor obfDesc) { | ||
| 245 | return fieldsByDeobf.get(getFieldKey(deobfName, obfDesc)); | ||
| 246 | } | ||
| 247 | |||
| 248 | public String getObfFieldName(String deobfName, TypeDescriptor obfDesc) { | ||
| 249 | FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfDesc)); | ||
| 250 | if (fieldMapping != null) { | ||
| 251 | return fieldMapping.getObfName(); | ||
| 252 | } | ||
| 253 | return null; | ||
| 254 | } | ||
| 255 | |||
| 256 | public String getDeobfFieldName(String obfName, TypeDescriptor obfDesc) { | ||
| 257 | FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc)); | ||
| 258 | if (fieldMapping != null) { | ||
| 259 | return fieldMapping.getDeobfName(); | ||
| 260 | } | ||
| 261 | return null; | ||
| 262 | } | ||
| 263 | |||
| 264 | private String getFieldKey(String name, TypeDescriptor desc) { | ||
| 265 | if (name == null) { | ||
| 266 | throw new IllegalArgumentException("name cannot be null!"); | ||
| 267 | } | ||
| 268 | if (desc == null) { | ||
| 269 | throw new IllegalArgumentException("desc cannot be null!"); | ||
| 270 | } | ||
| 271 | return name + ":" + desc; | ||
| 272 | } | ||
| 273 | |||
| 274 | public void setFieldName(String obfName, TypeDescriptor obfDesc, String deobfName) { | ||
| 275 | assert (deobfName != null); | ||
| 276 | FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc)); | ||
| 277 | if (fieldMapping == null) { | ||
| 278 | fieldMapping = new FieldMapping(obfName, obfDesc, deobfName, Mappings.EntryModifier.UNCHANGED); | ||
| 279 | boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfDesc), fieldMapping) == null; | ||
| 280 | assert (obfWasAdded); | ||
| 281 | } else { | ||
| 282 | boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfDesc)) != null; | ||
| 283 | assert (wasRemoved); | ||
| 284 | } | ||
| 285 | fieldMapping.setDeobfName(deobfName); | ||
| 286 | if (deobfName != null) { | ||
| 287 | boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfDesc), fieldMapping) == null; | ||
| 288 | assert (wasAdded); | ||
| 289 | } | ||
| 290 | this.isDirty = true; | ||
| 291 | } | ||
| 292 | |||
| 293 | //// METHODS //////// | ||
| 294 | |||
| 295 | public void setFieldObfNameAndType(String oldObfName, TypeDescriptor obfDesc, String newObfName, TypeDescriptor newObfDesc) { | ||
| 296 | assert (newObfName != null); | ||
| 297 | FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfDesc)); | ||
| 298 | assert (fieldMapping != null); | ||
| 299 | fieldMapping.setObfName(newObfName); | ||
| 300 | fieldMapping.setObfDesc(newObfDesc); | ||
| 301 | boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfDesc), fieldMapping) == null; | ||
| 302 | assert (obfWasAdded); | ||
| 303 | this.isDirty = true; | ||
| 304 | } | ||
| 305 | |||
| 306 | public Iterable<MethodMapping> methods() { | ||
| 307 | assert (methodsByObf.size() >= methodsByDeobf.size()); | ||
| 308 | return methodsByObf.values(); | ||
| 309 | } | ||
| 310 | |||
| 311 | public boolean containsObfMethod(String obfName, MethodDescriptor obfDescriptor) { | ||
| 312 | return methodsByObf.containsKey(getMethodKey(obfName, obfDescriptor)); | ||
| 313 | } | ||
| 314 | |||
| 315 | public boolean containsDeobfMethod(String deobfName, MethodDescriptor obfDescriptor) { | ||
| 316 | return methodsByDeobf.containsKey(getMethodKey(deobfName, obfDescriptor)); | ||
| 317 | } | ||
| 318 | |||
| 319 | public void addMethodMapping(MethodMapping methodMapping) { | ||
| 320 | String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc()); | ||
| 321 | if (methodsByObf.containsKey(obfKey)) { | ||
| 322 | throw new Error("Already have mapping for " + obfFullName + "." + obfKey); | ||
| 323 | } | ||
| 324 | boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null; | ||
| 325 | assert (wasAdded); | ||
| 326 | if (!methodMapping.isObfuscated()) { | ||
| 327 | String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc()); | ||
| 328 | if (methodsByDeobf.containsKey(deobfKey)) { | ||
| 329 | throw new Error("Already have mapping for " + deobfName + "." + deobfKey); | ||
| 330 | } | ||
| 331 | boolean deobfWasAdded = methodsByDeobf.put(deobfKey, methodMapping) == null; | ||
| 332 | assert (deobfWasAdded); | ||
| 333 | } | ||
| 334 | this.isDirty = true; | ||
| 335 | assert (methodsByObf.size() >= methodsByDeobf.size()); | ||
| 336 | } | ||
| 337 | |||
| 338 | public void removeMethodMapping(MethodMapping methodMapping) { | ||
| 339 | boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc())) != null; | ||
| 340 | assert (obfWasRemoved); | ||
| 341 | if (!methodMapping.isObfuscated()) { | ||
| 342 | boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null; | ||
| 343 | assert (deobfWasRemoved); | ||
| 344 | } | ||
| 345 | this.isDirty = true; | ||
| 346 | } | ||
| 347 | |||
| 348 | public MethodMapping getMethodByObf(String obfName, MethodDescriptor obfDescriptor) { | ||
| 349 | return methodsByObf.get(getMethodKey(obfName, obfDescriptor)); | ||
| 350 | } | ||
| 351 | |||
| 352 | public MethodMapping getMethodByObf(MethodEntry method) { | ||
| 353 | return getMethodByObf(method.getName(), method.getDesc()); | ||
| 354 | } | ||
| 355 | |||
| 356 | public MethodMapping getMethodByDeobf(String deobfName, MethodDescriptor obfDescriptor) { | ||
| 357 | return methodsByDeobf.get(getMethodKey(deobfName, obfDescriptor)); | ||
| 358 | } | ||
| 359 | |||
| 360 | private String getMethodKey(String name, MethodDescriptor descriptor) { | ||
| 361 | if (name == null) { | ||
| 362 | throw new IllegalArgumentException("name cannot be null!"); | ||
| 363 | } | ||
| 364 | if (descriptor == null) { | ||
| 365 | throw new IllegalArgumentException("descriptor cannot be null!"); | ||
| 366 | } | ||
| 367 | return name + descriptor; | ||
| 368 | } | ||
| 369 | |||
| 370 | public void setMethodName(String obfName, MethodDescriptor obfDescriptor, String deobfName) { | ||
| 371 | MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfDescriptor)); | ||
| 372 | if (methodMapping == null) { | ||
| 373 | methodMapping = createMethodMapping(obfName, obfDescriptor); | ||
| 374 | } else if (!methodMapping.isObfuscated()) { | ||
| 375 | boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null; | ||
| 376 | assert (wasRemoved); | ||
| 377 | } | ||
| 378 | methodMapping.setDeobfName(deobfName); | ||
| 379 | if (deobfName != null) { | ||
| 380 | boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfDescriptor), methodMapping) == null; | ||
| 381 | assert (wasAdded); | ||
| 382 | } | ||
| 383 | this.isDirty = true; | ||
| 384 | } | ||
| 385 | |||
| 386 | //// ARGUMENTS //////// | ||
| 387 | |||
| 388 | public void setMethodObfNameAndSignature(String oldObfName, MethodDescriptor obfDescriptor, String newObfName, MethodDescriptor newObfDescriptor) { | ||
| 389 | assert (newObfName != null); | ||
| 390 | MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfDescriptor)); | ||
| 391 | assert (methodMapping != null); | ||
| 392 | methodMapping.setObfName(newObfName); | ||
| 393 | methodMapping.setObfDescriptor(newObfDescriptor); | ||
| 394 | boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfDescriptor), methodMapping) == null; | ||
| 395 | assert (obfWasAdded); | ||
| 396 | this.isDirty = true; | ||
| 397 | } | ||
| 398 | |||
| 399 | public void setArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex, String argumentName) { | ||
| 400 | assert (argumentName != null); | ||
| 401 | MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor)); | ||
| 402 | if (methodMapping == null) { | ||
| 403 | methodMapping = createMethodMapping(obfMethodName, obfMethodDescriptor); | ||
| 404 | } | ||
| 405 | methodMapping.setLocalVariableName(argumentIndex, argumentName); | ||
| 406 | this.isDirty = true; | ||
| 407 | } | ||
| 408 | |||
| 409 | public void removeArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex) { | ||
| 410 | methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor)).removeLocalVariableName(argumentIndex); | ||
| 411 | this.isDirty = true; | ||
| 412 | } | ||
| 413 | |||
| 414 | private MethodMapping createMethodMapping(String obfName, MethodDescriptor obfDescriptor) { | ||
| 415 | MethodMapping methodMapping = new MethodMapping(obfName, obfDescriptor); | ||
| 416 | boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfDescriptor), methodMapping) == null; | ||
| 417 | assert (wasAdded); | ||
| 418 | this.isDirty = true; | ||
| 419 | return methodMapping; | ||
| 420 | } | ||
| 421 | |||
| 422 | @Override | ||
| 423 | public String toString() { | ||
| 424 | StringBuilder buf = new StringBuilder(); | ||
| 425 | buf.append(obfFullName); | ||
| 426 | buf.append(" <-> "); | ||
| 427 | buf.append(deobfName); | ||
| 428 | buf.append("\n"); | ||
| 429 | buf.append("Fields:\n"); | ||
| 430 | for (FieldMapping fieldMapping : fields()) { | ||
| 431 | buf.append("\t"); | ||
| 432 | buf.append(fieldMapping.getObfName()); | ||
| 433 | buf.append(" <-> "); | ||
| 434 | buf.append(fieldMapping.getDeobfName()); | ||
| 435 | buf.append("\n"); | ||
| 436 | } | ||
| 437 | buf.append("Methods:\n"); | ||
| 438 | for (MethodMapping methodMapping : methodsByObf.values()) { | ||
| 439 | buf.append(methodMapping); | ||
| 440 | buf.append("\n"); | ||
| 441 | } | ||
| 442 | buf.append("Inner Classes:\n"); | ||
| 443 | for (ClassMapping classMapping : innerClassesByObfSimple.values()) { | ||
| 444 | buf.append("\t"); | ||
| 445 | buf.append(classMapping.getObfSimpleName()); | ||
| 446 | buf.append(" <-> "); | ||
| 447 | buf.append(classMapping.getDeobfName()); | ||
| 448 | buf.append("\n"); | ||
| 449 | } | ||
| 450 | return buf.toString(); | ||
| 451 | } | ||
| 452 | |||
| 453 | @Override | ||
| 454 | public int compareTo(ClassMapping other) { | ||
| 455 | // sort by a, b, c, ... aa, ab, etc | ||
| 456 | if (obfFullName.length() != other.obfFullName.length()) { | ||
| 457 | return obfFullName.length() - other.obfFullName.length(); | ||
| 458 | } | ||
| 459 | return obfFullName.compareTo(other.obfFullName); | ||
| 460 | } | ||
| 461 | |||
| 462 | public boolean renameObfClass(String oldObfClassName, String newObfClassName) { | ||
| 463 | |||
| 464 | // rename inner classes | ||
| 465 | for (ClassMapping innerClassMapping : new ArrayList<>(innerClassesByObfSimple.values())) { | ||
| 466 | if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) { | ||
| 467 | boolean wasRemoved = innerClassesByObfSimple.remove(oldObfClassName) != null; | ||
| 468 | assert (wasRemoved); | ||
| 469 | boolean wasAdded = innerClassesByObfSimple.put(newObfClassName, innerClassMapping) == null; | ||
| 470 | assert (wasAdded); | ||
| 471 | } | ||
| 472 | } | ||
| 473 | |||
| 474 | // rename field types | ||
| 475 | for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) { | ||
| 476 | String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc()); | ||
| 477 | if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) { | ||
| 478 | boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null; | ||
| 479 | assert (wasRemoved); | ||
| 480 | boolean wasAdded = fieldsByObf | ||
| 481 | .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc()), fieldMapping) == null; | ||
| 482 | assert (wasAdded); | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | // rename method signatures | ||
| 487 | for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) { | ||
| 488 | String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc()); | ||
| 489 | if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { | ||
| 490 | boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null; | ||
| 491 | assert (wasRemoved); | ||
| 492 | boolean wasAdded = methodsByObf | ||
| 493 | .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc()), methodMapping) == null; | ||
| 494 | assert (wasAdded); | ||
| 495 | } | ||
| 496 | } | ||
| 497 | |||
| 498 | if (obfFullName.equals(oldObfClassName)) { | ||
| 499 | // rename this class | ||
| 500 | obfFullName = newObfClassName; | ||
| 501 | return true; | ||
| 502 | } | ||
| 503 | this.isDirty = true; | ||
| 504 | return false; | ||
| 505 | } | ||
| 506 | |||
| 507 | public boolean containsArgument(MethodEntry obfMethodEntry, String name) { | ||
| 508 | MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodEntry.getName(), obfMethodEntry.getDesc())); | ||
| 509 | return methodMapping != null && methodMapping.containsLocalVariable(name); | ||
| 510 | } | ||
| 511 | |||
| 512 | public ClassEntry getObfEntry() { | ||
| 513 | return new ClassEntry(obfFullName); | ||
| 514 | } | ||
| 515 | |||
| 516 | public ClassEntry getDeObfEntry() { | ||
| 517 | return deobfFullName != null ? new ClassEntry(deobfFullName) : null; | ||
| 518 | } | ||
| 519 | |||
| 520 | public boolean isObfuscated() { | ||
| 521 | return this.deobfName == null || this.deobfName.equals(this.obfFullName); | ||
| 522 | } | ||
| 523 | |||
| 524 | public String getSaveName() { | ||
| 525 | return this.isObfuscated() ? this.obfFullName : this.deobfName; | ||
| 526 | } | ||
| 527 | |||
| 528 | public boolean isDirty() { | ||
| 529 | return isDirty || areInnersDirty(); | ||
| 530 | } | ||
| 531 | |||
| 532 | private boolean areInnersDirty(){ | ||
| 533 | for (ClassMapping c : this.innerClasses()){ | ||
| 534 | if (c.isDirty()){ | ||
| 535 | return true; | ||
| 536 | } | ||
| 537 | } | ||
| 538 | return false; | ||
| 539 | } | ||
| 540 | |||
| 541 | public void resetDirty() { | ||
| 542 | this.isDirty = false; | ||
| 543 | } | ||
| 544 | |||
| 545 | public void markDirty() { | ||
| 546 | this.isDirty = true; | ||
| 547 | } | ||
| 548 | |||
| 549 | public Mappings.EntryModifier getModifier() { | ||
| 550 | return modifier; | ||
| 551 | } | ||
| 552 | |||
| 553 | public void setModifier(Mappings.EntryModifier modifier) { | ||
| 554 | if (this.modifier != modifier) | ||
| 555 | this.isDirty = true; | ||
| 556 | this.modifier = modifier; | ||
| 557 | } | ||
| 558 | |||
| 559 | public void setFieldModifier(String obfName, TypeDescriptor obfDesc, Mappings.EntryModifier modifier) { | ||
| 560 | FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfDesc), | ||
| 561 | k -> new FieldMapping(obfName, obfDesc, null, Mappings.EntryModifier.UNCHANGED)); | ||
| 562 | |||
| 563 | if (fieldMapping.getModifier() != modifier) { | ||
| 564 | fieldMapping.setModifier(modifier); | ||
| 565 | this.isDirty = true; | ||
| 566 | } | ||
| 567 | } | ||
| 568 | |||
| 569 | public void setMethodModifier(String obfName, MethodDescriptor sig, Mappings.EntryModifier modifier) { | ||
| 570 | MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig), | ||
| 571 | k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED)); | ||
| 572 | |||
| 573 | if (methodMapping.getModifier() != modifier) { | ||
| 574 | methodMapping.setModifier(modifier); | ||
| 575 | this.isDirty = true; | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | // Used for tiny parsing to keep track of deobfuscate inner classes | ||
| 580 | public ClassMapping setDeobfInner(String deobName) { | ||
| 581 | this.deobfFullName = deobName; | ||
| 582 | return this; | ||
| 583 | } | ||
| 584 | |||
| 585 | public ClassMapping copy() { | ||
| 586 | ClassMapping copied = new ClassMapping(this.obfFullName); | ||
| 587 | copied.obfSimpleName= this.obfSimpleName; | ||
| 588 | copied.modifier = this.modifier; | ||
| 589 | copied.deobfFullName = this.deobfFullName; | ||
| 590 | copied.deobfName = this.deobfName; | ||
| 591 | copied.innerClassesByDeobf = this.innerClassesByDeobf; | ||
| 592 | copied.innerClassesByObfFull = this.innerClassesByObfFull; | ||
| 593 | copied.innerClassesByObfSimple = this.innerClassesByObfSimple; | ||
| 594 | copied.fieldsByObf = this.fieldsByObf; | ||
| 595 | copied.fieldsByDeobf = this.fieldsByDeobf; | ||
| 596 | copied.methodsByObf = this.methodsByObf; | ||
| 597 | copied.methodsByDeobf = this.methodsByDeobf; | ||
| 598 | return copied; | ||
| 599 | } | ||
| 600 | |||
| 601 | @Override | ||
| 602 | public int hashCode() { | ||
| 603 | return this.obfFullName.hashCode(); | ||
| 604 | } | ||
| 605 | |||
| 606 | @Override | ||
| 607 | public boolean equals(Object obj) { | ||
| 608 | return obj instanceof ClassMapping && ((ClassMapping) obj).obfFullName.equals(this.obfFullName); | ||
| 609 | } | ||
| 610 | |||
| 611 | public boolean isEmpty() { | ||
| 612 | if (fieldsByDeobf.isEmpty() && methodsByDeobf.isEmpty() && deobfFullName == null && deobfName == null | ||
| 613 | && innerClassesByObfSimple.values().stream().allMatch(ClassMapping::isEmpty)) { | ||
| 614 | |||
| 615 | // check args | ||
| 616 | for (MethodMapping mapping : methodsByObf.values()) { | ||
| 617 | if (mapping.arguments().iterator().hasNext()) { | ||
| 618 | return false; | ||
| 619 | } | ||
| 620 | } | ||
| 621 | |||
| 622 | return true; | ||
| 623 | } | ||
| 624 | |||
| 625 | return false; | ||
| 626 | } | ||
| 627 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java b/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java deleted file mode 100644 index 388e7ac..0000000 --- a/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java +++ /dev/null | |||
| @@ -1,371 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import com.google.common.collect.Maps; | ||
| 16 | import cuchaz.enigma.analysis.TranslationIndex; | ||
| 17 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 18 | import cuchaz.enigma.mapping.entry.*; | ||
| 19 | |||
| 20 | import java.util.ArrayList; | ||
| 21 | import java.util.List; | ||
| 22 | import java.util.Map; | ||
| 23 | import java.util.ServiceLoader; | ||
| 24 | |||
| 25 | public class DirectionalTranslator implements Translator { | ||
| 26 | private final TranslationDirection direction; | ||
| 27 | private final Map<String, ClassMapping> classes; | ||
| 28 | private final TranslationIndex index; | ||
| 29 | |||
| 30 | public DirectionalTranslator(ReferencedEntryPool entryPool) { | ||
| 31 | this.direction = null; | ||
| 32 | this.classes = Maps.newHashMap(); | ||
| 33 | this.index = new TranslationIndex(entryPool); | ||
| 34 | } | ||
| 35 | |||
| 36 | public DirectionalTranslator(TranslationDirection direction, Map<String, ClassMapping> classes, TranslationIndex index) { | ||
| 37 | this.direction = direction; | ||
| 38 | this.classes = classes; | ||
| 39 | this.index = index; | ||
| 40 | } | ||
| 41 | |||
| 42 | public TranslationDirection getDirection() { | ||
| 43 | return direction; | ||
| 44 | } | ||
| 45 | |||
| 46 | public TranslationIndex getTranslationIndex() { | ||
| 47 | return index; | ||
| 48 | } | ||
| 49 | |||
| 50 | @Override | ||
| 51 | public ClassEntry getTranslatedClass(ClassEntry entry) { | ||
| 52 | String className; | ||
| 53 | if (entry.isArray()) { | ||
| 54 | className = this.getTranslatedTypeDesc(new TypeDescriptor(entry.getName())).toString(); | ||
| 55 | } else { | ||
| 56 | className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry); | ||
| 57 | } | ||
| 58 | return new ClassEntry(className); | ||
| 59 | } | ||
| 60 | |||
| 61 | @Override | ||
| 62 | public ClassDefEntry getTranslatedClassDef(ClassDefEntry entry) { | ||
| 63 | String className; | ||
| 64 | if (entry.isArray()) { | ||
| 65 | className = this.getTranslatedTypeDesc(new TypeDescriptor(entry.getName())).toString(); | ||
| 66 | } else { | ||
| 67 | className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry); | ||
| 68 | } | ||
| 69 | Signature translatedSignature = this.getTranslatedSignature(entry.getSignature()); | ||
| 70 | return new ClassDefEntry(className, translatedSignature, getClassModifier(entry).transform(entry.getAccess())); | ||
| 71 | } | ||
| 72 | |||
| 73 | private String translateClassName(ClassEntry entry) { | ||
| 74 | // normal classes are easy | ||
| 75 | ClassMapping classMapping = this.classes.get(entry.getName()); | ||
| 76 | if (classMapping == null) { | ||
| 77 | return entry.getName(); | ||
| 78 | } | ||
| 79 | return classMapping.getTranslatedName(direction); | ||
| 80 | } | ||
| 81 | |||
| 82 | private String translateInnerClassName(ClassEntry entry) { | ||
| 83 | // translate as much of the class chain as we can | ||
| 84 | List<ClassMapping> mappingsChain = getClassMappingChain(entry); | ||
| 85 | String[] obfClassNames = entry.getName().split("\\$"); | ||
| 86 | StringBuilder buf = new StringBuilder(); | ||
| 87 | for (int i = 0; i < obfClassNames.length; i++) { | ||
| 88 | boolean isFirstClass = buf.length() == 0; | ||
| 89 | String className = null; | ||
| 90 | ClassMapping classMapping = mappingsChain.get(i); | ||
| 91 | if (classMapping != null) { | ||
| 92 | className = this.direction.choose( | ||
| 93 | classMapping.getDeobfName(), | ||
| 94 | isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName() | ||
| 95 | ); | ||
| 96 | } | ||
| 97 | if (className == null) { | ||
| 98 | className = obfClassNames[i]; | ||
| 99 | } | ||
| 100 | if (!isFirstClass) { | ||
| 101 | buf.append("$"); | ||
| 102 | } | ||
| 103 | buf.append(className); | ||
| 104 | } | ||
| 105 | return buf.toString(); | ||
| 106 | } | ||
| 107 | |||
| 108 | @Override | ||
| 109 | public FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry) { | ||
| 110 | String translatedName = translateFieldName(entry); | ||
| 111 | if (translatedName == null) { | ||
| 112 | translatedName = entry.getName(); | ||
| 113 | } | ||
| 114 | ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry()); | ||
| 115 | TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc()); | ||
| 116 | Signature translatedSignature = getTranslatedSignature(entry.getSignature()); | ||
| 117 | AccessFlags translatedAccess = getFieldModifier(entry).transform(entry.getAccess()); | ||
| 118 | return new FieldDefEntry(translatedOwner, translatedName, translatedDesc, translatedSignature, translatedAccess); | ||
| 119 | } | ||
| 120 | |||
| 121 | @Override | ||
| 122 | public FieldEntry getTranslatedField(FieldEntry entry) { | ||
| 123 | String translatedName = translateFieldName(entry); | ||
| 124 | if (translatedName == null) { | ||
| 125 | translatedName = entry.getName(); | ||
| 126 | } | ||
| 127 | ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry()); | ||
| 128 | TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc()); | ||
| 129 | return new FieldEntry(translatedOwner, translatedName, translatedDesc); | ||
| 130 | } | ||
| 131 | |||
| 132 | private String translateFieldName(FieldEntry entry) { | ||
| 133 | // resolve the class entry | ||
| 134 | ClassEntry resolvedClassEntry = this.index.resolveEntryOwner(entry); | ||
| 135 | if (resolvedClassEntry != null) { | ||
| 136 | // look for the class | ||
| 137 | ClassMapping classMapping = findClassMapping(resolvedClassEntry); | ||
| 138 | if (classMapping != null) { | ||
| 139 | // look for the field | ||
| 140 | FieldMapping mapping = this.direction.choose( | ||
| 141 | classMapping.getFieldByObf(entry.getName(), entry.getDesc()), | ||
| 142 | classMapping.getFieldByDeobf(entry.getName(), getTranslatedTypeDesc(entry.getDesc())) | ||
| 143 | ); | ||
| 144 | if (mapping != null) { | ||
| 145 | return this.direction.choose(mapping.getDeobfName(), mapping.getObfName()); | ||
| 146 | } | ||
| 147 | } | ||
| 148 | } | ||
| 149 | return null; | ||
| 150 | } | ||
| 151 | |||
| 152 | @Override | ||
| 153 | public MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry) { | ||
| 154 | String translatedName = translateMethodName(entry); | ||
| 155 | if (translatedName == null) { | ||
| 156 | translatedName = entry.getName(); | ||
| 157 | } | ||
| 158 | ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry()); | ||
| 159 | MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc()); | ||
| 160 | Signature translatedSignature = getTranslatedSignature(entry.getSignature()); | ||
| 161 | AccessFlags access = getMethodModifier(entry).transform(entry.getAccess()); | ||
| 162 | return new MethodDefEntry(translatedOwner, translatedName, translatedDesc, translatedSignature, access); | ||
| 163 | } | ||
| 164 | |||
| 165 | @Override | ||
| 166 | public MethodEntry getTranslatedMethod(MethodEntry entry) { | ||
| 167 | String translatedName = translateMethodName(entry); | ||
| 168 | if (translatedName == null) { | ||
| 169 | translatedName = entry.getName(); | ||
| 170 | } | ||
| 171 | ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry()); | ||
| 172 | MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc()); | ||
| 173 | return new MethodEntry(translatedOwner, translatedName, translatedDesc); | ||
| 174 | } | ||
| 175 | |||
| 176 | private String translateMethodName(MethodEntry entry) { | ||
| 177 | // resolve the class entry | ||
| 178 | ClassEntry resolvedOwner = this.index.resolveEntryOwner(entry); | ||
| 179 | if (resolvedOwner != null) { | ||
| 180 | // look for class | ||
| 181 | ClassMapping classMapping = findClassMapping(resolvedOwner); | ||
| 182 | if (classMapping != null) { | ||
| 183 | // look for the method | ||
| 184 | MethodMapping mapping = this.direction.choose( | ||
| 185 | classMapping.getMethodByObf(entry.getName(), entry.getDesc()), | ||
| 186 | classMapping.getMethodByDeobf(entry.getName(), getTranslatedMethodDesc(entry.getDesc())) | ||
| 187 | ); | ||
| 188 | if (mapping != null) { | ||
| 189 | return this.direction.choose(mapping.getDeobfName(), mapping.getObfName()); | ||
| 190 | } | ||
| 191 | } | ||
| 192 | } | ||
| 193 | return null; | ||
| 194 | } | ||
| 195 | |||
| 196 | @Override | ||
| 197 | public LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry) { | ||
| 198 | String translatedArgumentName = translateLocalVariableName(entry); | ||
| 199 | if (translatedArgumentName == null) { | ||
| 200 | translatedArgumentName = inheritLocalVariableName(entry); | ||
| 201 | } | ||
| 202 | if (translatedArgumentName == null) { | ||
| 203 | translatedArgumentName = entry.getName(); | ||
| 204 | } | ||
| 205 | // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this? | ||
| 206 | MethodEntry translatedOwner = getTranslatedMethod(entry.getOwnerEntry()); | ||
| 207 | return new LocalVariableEntry(translatedOwner, entry.getIndex(), translatedArgumentName, entry.isParameter()); | ||
| 208 | } | ||
| 209 | |||
| 210 | @Override | ||
| 211 | public LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry) { | ||
| 212 | String translatedArgumentName = translateLocalVariableName(entry); | ||
| 213 | if (translatedArgumentName == null) { | ||
| 214 | translatedArgumentName = inheritLocalVariableName(entry); | ||
| 215 | } | ||
| 216 | // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this? | ||
| 217 | MethodDefEntry translatedOwner = getTranslatedMethodDef(entry.getOwnerEntry()); | ||
| 218 | TypeDescriptor translatedTypeDesc = getTranslatedTypeDesc(entry.getDesc()); | ||
| 219 | return new LocalVariableDefEntry(translatedOwner, entry.getIndex(), translatedArgumentName != null ? translatedArgumentName : entry.getName(), entry.isParameter(), translatedTypeDesc); | ||
| 220 | } | ||
| 221 | |||
| 222 | @Override | ||
| 223 | public boolean hasClassMapping(ClassEntry entry) { | ||
| 224 | return classes.containsKey(entry.getName()); | ||
| 225 | } | ||
| 226 | |||
| 227 | @Override | ||
| 228 | public boolean hasFieldMapping(FieldEntry entry) { | ||
| 229 | return translateFieldName(entry) != null; | ||
| 230 | } | ||
| 231 | |||
| 232 | @Override | ||
| 233 | public boolean hasMethodMapping(MethodEntry entry) { | ||
| 234 | return translateMethodName(entry) != null; | ||
| 235 | } | ||
| 236 | |||
| 237 | @Override | ||
| 238 | public boolean hasLocalVariableMapping(LocalVariableEntry entry) { | ||
| 239 | return translateLocalVariableName(entry) != null || inheritLocalVariableName(entry) != null; | ||
| 240 | } | ||
| 241 | |||
| 242 | // TODO: support not identical behavior (specific to constructor) | ||
| 243 | private String translateLocalVariableName(LocalVariableEntry entry) { | ||
| 244 | // look for identical behavior in superclasses | ||
| 245 | ClassEntry ownerEntry = entry.getOwnerClassEntry(); | ||
| 246 | if (ownerEntry != null) { | ||
| 247 | // look for the class | ||
| 248 | ClassMapping classMapping = findClassMapping(ownerEntry); | ||
| 249 | if (classMapping != null) { | ||
| 250 | // look for the method | ||
| 251 | MethodMapping methodMapping = this.direction.choose( | ||
| 252 | classMapping.getMethodByObf(entry.getMethodName(), entry.getMethodDesc()), | ||
| 253 | classMapping.getMethodByDeobf(entry.getMethodName(), getTranslatedMethodDesc(entry.getMethodDesc())) | ||
| 254 | ); | ||
| 255 | if (methodMapping != null) { | ||
| 256 | int index = entry.getIndex(); | ||
| 257 | return this.direction.choose( | ||
| 258 | methodMapping.getDeobfLocalVariableName(index), | ||
| 259 | methodMapping.getObfLocalVariableName(index) | ||
| 260 | ); | ||
| 261 | } | ||
| 262 | } | ||
| 263 | } | ||
| 264 | return null; | ||
| 265 | } | ||
| 266 | |||
| 267 | private String inheritLocalVariableName(LocalVariableEntry entry) { | ||
| 268 | List<ClassEntry> ancestry = this.index.getAncestry(entry.getOwnerClassEntry()); | ||
| 269 | // Check in mother class for the arg | ||
| 270 | for (ClassEntry ancestorEntry : ancestry) { | ||
| 271 | LocalVariableEntry motherArg = entry.updateOwnership(ancestorEntry); | ||
| 272 | if (this.index.entryExists(motherArg)) { | ||
| 273 | String result = translateLocalVariableName(motherArg); | ||
| 274 | if (result != null) { | ||
| 275 | return result; | ||
| 276 | } | ||
| 277 | } | ||
| 278 | } | ||
| 279 | return null; | ||
| 280 | } | ||
| 281 | |||
| 282 | @Override | ||
| 283 | public TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc) { | ||
| 284 | return desc.remap(this::remapClass); | ||
| 285 | } | ||
| 286 | |||
| 287 | @Override | ||
| 288 | public MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor) { | ||
| 289 | List<TypeDescriptor> arguments = descriptor.getArgumentDescs(); | ||
| 290 | List<TypeDescriptor> translatedArguments = new ArrayList<>(arguments.size()); | ||
| 291 | for (TypeDescriptor argument : arguments) { | ||
| 292 | translatedArguments.add(getTranslatedTypeDesc(argument)); | ||
| 293 | } | ||
| 294 | return new MethodDescriptor(translatedArguments, getTranslatedTypeDesc(descriptor.getReturnDesc())); | ||
| 295 | } | ||
| 296 | |||
| 297 | @Override | ||
| 298 | public Signature getTranslatedSignature(Signature signature) { | ||
| 299 | if (signature == null) { | ||
| 300 | return null; | ||
| 301 | } | ||
| 302 | return signature.remap(this::remapClass); | ||
| 303 | } | ||
| 304 | |||
| 305 | private ClassMapping findClassMapping(ClassEntry entry) { | ||
| 306 | List<ClassMapping> mappingChain = getClassMappingChain(entry); | ||
| 307 | return mappingChain.get(mappingChain.size() - 1); | ||
| 308 | } | ||
| 309 | |||
| 310 | private List<ClassMapping> getClassMappingChain(ClassEntry entry) { | ||
| 311 | |||
| 312 | // get a list of all the classes in the hierarchy | ||
| 313 | String[] parts = entry.getName().split("\\$"); | ||
| 314 | List<ClassMapping> mappingsChain = Lists.newArrayList(); | ||
| 315 | |||
| 316 | // get mappings for the outer class | ||
| 317 | ClassMapping outerClassMapping = this.classes.get(parts[0]); | ||
| 318 | mappingsChain.add(outerClassMapping); | ||
| 319 | |||
| 320 | for (int i = 1; i < parts.length; i++) { | ||
| 321 | |||
| 322 | // get mappings for the inner class | ||
| 323 | ClassMapping innerClassMapping = null; | ||
| 324 | if (outerClassMapping != null) { | ||
| 325 | innerClassMapping = this.direction.choose( | ||
| 326 | outerClassMapping.getInnerClassByObfSimple(parts[i]), | ||
| 327 | outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i]) | ||
| 328 | ); | ||
| 329 | } | ||
| 330 | mappingsChain.add(innerClassMapping); | ||
| 331 | outerClassMapping = innerClassMapping; | ||
| 332 | } | ||
| 333 | |||
| 334 | assert (mappingsChain.size() == parts.length); | ||
| 335 | return mappingsChain; | ||
| 336 | } | ||
| 337 | |||
| 338 | private Mappings.EntryModifier getClassModifier(ClassEntry entry) { | ||
| 339 | ClassMapping classMapping = findClassMapping(entry); | ||
| 340 | if (classMapping != null) { | ||
| 341 | return classMapping.getModifier(); | ||
| 342 | } | ||
| 343 | return Mappings.EntryModifier.UNCHANGED; | ||
| 344 | } | ||
| 345 | |||
| 346 | private Mappings.EntryModifier getFieldModifier(FieldEntry entry) { | ||
| 347 | ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry()); | ||
| 348 | if (classMapping != null) { | ||
| 349 | FieldMapping fieldMapping = classMapping.getFieldByObf(entry); | ||
| 350 | if (fieldMapping != null) { | ||
| 351 | return fieldMapping.getModifier(); | ||
| 352 | } | ||
| 353 | } | ||
| 354 | return Mappings.EntryModifier.UNCHANGED; | ||
| 355 | } | ||
| 356 | |||
| 357 | private Mappings.EntryModifier getMethodModifier(MethodEntry entry) { | ||
| 358 | ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry()); | ||
| 359 | if (classMapping != null) { | ||
| 360 | MethodMapping methodMapping = classMapping.getMethodByObf(entry); | ||
| 361 | if (methodMapping != null) { | ||
| 362 | return methodMapping.getModifier(); | ||
| 363 | } | ||
| 364 | } | ||
| 365 | return Mappings.EntryModifier.UNCHANGED; | ||
| 366 | } | ||
| 367 | |||
| 368 | private String remapClass(String name) { | ||
| 369 | return getTranslatedClass(new ClassEntry(name)).getName(); | ||
| 370 | } | ||
| 371 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java deleted file mode 100644 index 8fbe095..0000000 --- a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java +++ /dev/null | |||
| @@ -1,100 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 15 | import cuchaz.enigma.mapping.entry.FieldEntry; | ||
| 16 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 17 | |||
| 18 | public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<FieldEntry> { | ||
| 19 | |||
| 20 | private String obfName; | ||
| 21 | private String deobfName; | ||
| 22 | private TypeDescriptor obfDesc; | ||
| 23 | private Mappings.EntryModifier modifier; | ||
| 24 | |||
| 25 | public FieldMapping(String obfName, TypeDescriptor obfDesc, String deobfName, Mappings.EntryModifier modifier) { | ||
| 26 | this.obfName = obfName; | ||
| 27 | this.deobfName = NameValidator.validateFieldName(deobfName); | ||
| 28 | this.obfDesc = obfDesc; | ||
| 29 | this.modifier = modifier; | ||
| 30 | } | ||
| 31 | |||
| 32 | @Override | ||
| 33 | public FieldEntry getObfEntry(ClassEntry classEntry) { | ||
| 34 | return new FieldEntry(classEntry, this.obfName, this.obfDesc); | ||
| 35 | } | ||
| 36 | |||
| 37 | @Override | ||
| 38 | public String getObfName() { | ||
| 39 | return this.obfName; | ||
| 40 | } | ||
| 41 | |||
| 42 | public void setObfName(String name) { | ||
| 43 | try { | ||
| 44 | NameValidator.validateMethodName(name); | ||
| 45 | } catch (IllegalNameException ex) { | ||
| 46 | // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues | ||
| 47 | if (this.deobfName == null) { | ||
| 48 | System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob")); | ||
| 49 | setDeobfName(name + "_auto_deob"); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | this.obfName = name; | ||
| 53 | } | ||
| 54 | |||
| 55 | public String getDeobfName() { | ||
| 56 | return this.deobfName; | ||
| 57 | } | ||
| 58 | |||
| 59 | public void setDeobfName(String val) { | ||
| 60 | this.deobfName = NameValidator.validateFieldName(val); | ||
| 61 | } | ||
| 62 | |||
| 63 | public TypeDescriptor getObfDesc() { | ||
| 64 | return this.obfDesc; | ||
| 65 | } | ||
| 66 | |||
| 67 | public void setObfDesc(TypeDescriptor val) { | ||
| 68 | this.obfDesc = val; | ||
| 69 | } | ||
| 70 | |||
| 71 | public Mappings.EntryModifier getModifier() { | ||
| 72 | return modifier; | ||
| 73 | } | ||
| 74 | |||
| 75 | public void setModifier(Mappings.EntryModifier modifier) { | ||
| 76 | this.modifier = modifier; | ||
| 77 | } | ||
| 78 | |||
| 79 | @Override | ||
| 80 | public int compareTo(FieldMapping other) { | ||
| 81 | return (this.obfName + this.obfDesc).compareTo(other.obfName + other.obfDesc); | ||
| 82 | } | ||
| 83 | |||
| 84 | public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { | ||
| 85 | // rename obf classes in the desc | ||
| 86 | TypeDescriptor newDesc = this.obfDesc.remap(className -> { | ||
| 87 | if (className.equals(oldObfClassName)) { | ||
| 88 | return newObfClassName; | ||
| 89 | } | ||
| 90 | return className; | ||
| 91 | }); | ||
| 92 | |||
| 93 | if (!newDesc.equals(this.obfDesc)) { | ||
| 94 | this.obfDesc = newDesc; | ||
| 95 | return true; | ||
| 96 | } | ||
| 97 | return false; | ||
| 98 | } | ||
| 99 | |||
| 100 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java deleted file mode 100644 index bfe66b2..0000000 --- a/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java +++ /dev/null | |||
| @@ -1,58 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import cuchaz.enigma.mapping.entry.LocalVariableEntry; | ||
| 15 | import cuchaz.enigma.mapping.entry.MethodEntry; | ||
| 16 | |||
| 17 | public class LocalVariableMapping implements Comparable<LocalVariableMapping> { | ||
| 18 | |||
| 19 | private int index; | ||
| 20 | private String name; | ||
| 21 | |||
| 22 | // NOTE: this argument order is important for the MethodReader/MethodWriter | ||
| 23 | public LocalVariableMapping(int index, String name) { | ||
| 24 | this.index = index; | ||
| 25 | this.name = NameValidator.validateArgumentName(name); | ||
| 26 | } | ||
| 27 | |||
| 28 | public LocalVariableMapping(LocalVariableMapping other) { | ||
| 29 | this.index = other.index; | ||
| 30 | this.name = other.name; | ||
| 31 | } | ||
| 32 | |||
| 33 | public int getIndex() { | ||
| 34 | return this.index; | ||
| 35 | } | ||
| 36 | |||
| 37 | public String getName() { | ||
| 38 | return this.name; | ||
| 39 | } | ||
| 40 | |||
| 41 | public void setName(String val) { | ||
| 42 | this.name = NameValidator.validateArgumentName(val); | ||
| 43 | } | ||
| 44 | |||
| 45 | @Deprecated | ||
| 46 | public LocalVariableEntry getObfEntry(MethodEntry methodEntry) { | ||
| 47 | return new LocalVariableEntry(methodEntry, index, name); | ||
| 48 | } | ||
| 49 | |||
| 50 | public LocalVariableEntry getObfEntry(MethodEntry methodEntry, boolean parameter) { | ||
| 51 | return new LocalVariableEntry(methodEntry, index, name, parameter); | ||
| 52 | } | ||
| 53 | |||
| 54 | @Override | ||
| 55 | public int compareTo(LocalVariableMapping other) { | ||
| 56 | return Integer.compare(this.index, other.index); | ||
| 57 | } | ||
| 58 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java deleted file mode 100644 index c865079..0000000 --- a/src/main/java/cuchaz/enigma/mapping/Mappings.java +++ /dev/null | |||
| @@ -1,268 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import com.google.common.collect.Maps; | ||
| 16 | import com.google.common.collect.Sets; | ||
| 17 | import cuchaz.enigma.analysis.TranslationIndex; | ||
| 18 | import cuchaz.enigma.api.EnigmaPlugin; | ||
| 19 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 20 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 21 | import cuchaz.enigma.mapping.entry.MethodEntry; | ||
| 22 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 23 | |||
| 24 | import java.io.File; | ||
| 25 | import java.io.IOException; | ||
| 26 | import java.util.*; | ||
| 27 | import java.util.stream.Collectors; | ||
| 28 | |||
| 29 | public class Mappings { | ||
| 30 | |||
| 31 | private final FormatType originMapping; | ||
| 32 | protected Map<String, ClassMapping> classesByObf; | ||
| 33 | protected Map<String, ClassMapping> classesByDeobf; | ||
| 34 | private Mappings previousState; | ||
| 35 | |||
| 36 | public Mappings() { | ||
| 37 | this(FormatType.ENIGMA_DIRECTORY); | ||
| 38 | } | ||
| 39 | |||
| 40 | public Mappings(FormatType originMapping) { | ||
| 41 | this.originMapping = originMapping; | ||
| 42 | this.classesByObf = Maps.newHashMap(); | ||
| 43 | this.classesByDeobf = Maps.newHashMap(); | ||
| 44 | } | ||
| 45 | |||
| 46 | public Collection<ClassMapping> classes() { | ||
| 47 | assert (this.classesByObf.size() >= this.classesByDeobf.size()); | ||
| 48 | return this.classesByObf.values(); | ||
| 49 | } | ||
| 50 | |||
| 51 | public void addClassMapping(ClassMapping classMapping) throws MappingConflict { | ||
| 52 | if (this.classesByObf.containsKey(classMapping.getObfFullName())) { | ||
| 53 | throw new MappingConflict("class", classMapping.getObfFullName(), this.classesByObf.get(classMapping.getObfFullName()).getObfFullName()); | ||
| 54 | } | ||
| 55 | this.classesByObf.put(classMapping.getObfFullName(), classMapping); | ||
| 56 | |||
| 57 | if (classMapping.getDeobfName() != null) { | ||
| 58 | if (this.classesByDeobf.containsKey(classMapping.getDeobfName())) { | ||
| 59 | throw new MappingConflict("class", classMapping.getDeobfName(), this.classesByDeobf.get(classMapping.getDeobfName()).getDeobfName()); | ||
| 60 | } | ||
| 61 | this.classesByDeobf.put(classMapping.getDeobfName(), classMapping); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | public void removeClassMapping(ClassMapping classMapping) { | ||
| 66 | boolean obfWasRemoved = this.classesByObf.remove(classMapping.getObfFullName()) != null; | ||
| 67 | assert (obfWasRemoved); | ||
| 68 | if (classMapping.getDeobfName() != null) { | ||
| 69 | boolean deobfWasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; | ||
| 70 | assert (deobfWasRemoved); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | public ClassMapping getClassByObf(ClassEntry entry) { | ||
| 75 | return getClassByObf(entry.getName()); | ||
| 76 | } | ||
| 77 | |||
| 78 | public ClassMapping getClassByObf(String obfName) { | ||
| 79 | return this.classesByObf.get(obfName); | ||
| 80 | } | ||
| 81 | |||
| 82 | public ClassMapping getClassByDeobf(ClassEntry entry) { | ||
| 83 | return getClassByDeobf(entry.getName()); | ||
| 84 | } | ||
| 85 | |||
| 86 | public ClassMapping getClassByDeobf(String deobfName) { | ||
| 87 | return this.classesByDeobf.get(deobfName); | ||
| 88 | } | ||
| 89 | |||
| 90 | public void setClassDeobfName(ClassMapping classMapping, String deobfName) { | ||
| 91 | if (classMapping.getDeobfName() != null) { | ||
| 92 | boolean wasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; | ||
| 93 | assert (wasRemoved); | ||
| 94 | } | ||
| 95 | classMapping.setDeobfName(deobfName); | ||
| 96 | if (deobfName != null) { | ||
| 97 | boolean wasAdded = this.classesByDeobf.put(deobfName, classMapping) == null; | ||
| 98 | assert (wasAdded); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { | ||
| 103 | switch (direction) { | ||
| 104 | case DEOBFUSCATING: | ||
| 105 | |||
| 106 | return new DirectionalTranslator(direction, this.classesByObf, index); | ||
| 107 | |||
| 108 | case OBFUSCATING: | ||
| 109 | |||
| 110 | // fill in the missing deobf class entries with obf entries | ||
| 111 | Map<String, ClassMapping> classes = Maps.newHashMap(); | ||
| 112 | for (ClassMapping classMapping : classes()) { | ||
| 113 | if (classMapping.getDeobfName() != null) { | ||
| 114 | classes.put(classMapping.getDeobfName(), classMapping); | ||
| 115 | } else { | ||
| 116 | classes.put(classMapping.getObfFullName(), classMapping); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | // translate the translation index | ||
| 121 | // NOTE: this isn't actually recursive | ||
| 122 | TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.DEOBFUSCATING, index)); | ||
| 123 | |||
| 124 | return new DirectionalTranslator(direction, classes, deobfIndex); | ||
| 125 | |||
| 126 | default: | ||
| 127 | throw new Error("Invalid translation direction!"); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | @Override | ||
| 132 | public String toString() { | ||
| 133 | StringBuilder buf = new StringBuilder(); | ||
| 134 | for (ClassMapping classMapping : this.classesByObf.values()) { | ||
| 135 | buf.append(classMapping); | ||
| 136 | buf.append("\n"); | ||
| 137 | } | ||
| 138 | return buf.toString(); | ||
| 139 | } | ||
| 140 | |||
| 141 | public void renameObfClass(String oldObfName, String newObfName) { | ||
| 142 | new ArrayList<>(classes()).stream().filter(classMapping -> classMapping.renameObfClass(oldObfName, newObfName)).forEach(classMapping -> { | ||
| 143 | boolean wasRemoved = this.classesByObf.remove(oldObfName) != null; | ||
| 144 | assert (wasRemoved); | ||
| 145 | boolean wasAdded = this.classesByObf.put(newObfName, classMapping) == null; | ||
| 146 | assert (wasAdded); | ||
| 147 | }); | ||
| 148 | } | ||
| 149 | |||
| 150 | public Set<String> getAllObfClassNames() { | ||
| 151 | final Set<String> classNames = Sets.newHashSet(); | ||
| 152 | for (ClassMapping classMapping : classes()) { | ||
| 153 | |||
| 154 | // add the class name | ||
| 155 | classNames.add(classMapping.getObfFullName()); | ||
| 156 | |||
| 157 | // add classes from method signatures | ||
| 158 | for (MethodMapping methodMapping : classMapping.methods()) { | ||
| 159 | for (TypeDescriptor desc : methodMapping.getObfDesc().types()) { | ||
| 160 | if (desc.containsType()) { | ||
| 161 | classNames.add(desc.getTypeEntry().getClassName()); | ||
| 162 | } | ||
| 163 | } | ||
| 164 | } | ||
| 165 | } | ||
| 166 | return classNames; | ||
| 167 | } | ||
| 168 | |||
| 169 | public boolean containsDeobfClass(String deobfName) { | ||
| 170 | return this.classesByDeobf.containsKey(deobfName); | ||
| 171 | } | ||
| 172 | |||
| 173 | public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, TypeDescriptor obfDesc) { | ||
| 174 | ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); | ||
| 175 | return classMapping != null && classMapping.containsDeobfField(deobfName, obfDesc); | ||
| 176 | } | ||
| 177 | |||
| 178 | public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { | ||
| 179 | ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); | ||
| 180 | if (classMapping != null) | ||
| 181 | for (FieldMapping fieldMapping : classMapping.fields()) | ||
| 182 | if (deobfName.equals(fieldMapping.getDeobfName()) || deobfName.equals(fieldMapping.getObfName())) | ||
| 183 | return true; | ||
| 184 | |||
| 185 | return false; | ||
| 186 | } | ||
| 187 | |||
| 188 | public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, MethodDescriptor obfDescriptor) { | ||
| 189 | ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); | ||
| 190 | return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfDescriptor); | ||
| 191 | } | ||
| 192 | |||
| 193 | public boolean containsArgument(MethodEntry obfMethodEntry, String name) { | ||
| 194 | ClassMapping classMapping = this.classesByObf.get(obfMethodEntry.getClassName()); | ||
| 195 | return classMapping != null && classMapping.containsArgument(obfMethodEntry, name); | ||
| 196 | } | ||
| 197 | |||
| 198 | public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) { | ||
| 199 | List<ClassMapping> mappingChain = Lists.newArrayList(); | ||
| 200 | ClassMapping classMapping = null; | ||
| 201 | for (ClassEntry obfClassEntry : obfClass.getClassChain()) { | ||
| 202 | if (mappingChain.isEmpty()) { | ||
| 203 | classMapping = this.classesByObf.get(obfClassEntry.getName()); | ||
| 204 | } else if (classMapping != null) { | ||
| 205 | classMapping = classMapping.getInnerClassByObfSimple(obfClassEntry.getInnermostClassName()); | ||
| 206 | } | ||
| 207 | mappingChain.add(classMapping); | ||
| 208 | } | ||
| 209 | return mappingChain; | ||
| 210 | } | ||
| 211 | |||
| 212 | public FormatType getOriginMappingFormat() { | ||
| 213 | return originMapping; | ||
| 214 | } | ||
| 215 | |||
| 216 | public void savePreviousState() { | ||
| 217 | this.previousState = new Mappings(this.originMapping); | ||
| 218 | this.previousState.classesByDeobf = new HashMap<>(); | ||
| 219 | for (Map.Entry<String, ClassMapping> entry : this.classesByDeobf.entrySet()) { | ||
| 220 | this.previousState.classesByDeobf.put(entry.getKey(), entry.getValue().copy()); | ||
| 221 | } | ||
| 222 | this.previousState.classesByObf = new HashMap<>(); | ||
| 223 | for (Map.Entry<String, ClassMapping> entry : this.classesByObf.entrySet()) { | ||
| 224 | this.previousState.classesByObf.put(entry.getKey(), entry.getValue().copy()); | ||
| 225 | } | ||
| 226 | classesByDeobf.values().forEach(ClassMapping::resetDirty); | ||
| 227 | classesByObf.values().forEach(ClassMapping::resetDirty); | ||
| 228 | } | ||
| 229 | |||
| 230 | public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException { | ||
| 231 | new MappingsEnigmaWriter().write(file, this, isDirectoryFormat); | ||
| 232 | this.savePreviousState(); | ||
| 233 | } | ||
| 234 | |||
| 235 | public void saveSRGMappings(File file) throws IOException { | ||
| 236 | new MappingsSRGWriter().write(file, this); | ||
| 237 | } | ||
| 238 | |||
| 239 | public Mappings getPreviousState() { | ||
| 240 | return previousState; | ||
| 241 | } | ||
| 242 | |||
| 243 | public enum FormatType { | ||
| 244 | ENIGMA_FILE, ENIGMA_DIRECTORY, TINY_FILE, SRG_FILE | ||
| 245 | } | ||
| 246 | |||
| 247 | public enum EntryModifier { | ||
| 248 | UNCHANGED, PUBLIC, PROTECTED, PRIVATE; | ||
| 249 | |||
| 250 | public String getFormattedName() { | ||
| 251 | return " ACC:" + super.toString(); | ||
| 252 | } | ||
| 253 | |||
| 254 | public AccessFlags transform(AccessFlags access) { | ||
| 255 | switch (this) { | ||
| 256 | case PUBLIC: | ||
| 257 | return access.setPublic(); | ||
| 258 | case PROTECTED: | ||
| 259 | return access.setProtected(); | ||
| 260 | case PRIVATE: | ||
| 261 | return access.setPrivate(); | ||
| 262 | case UNCHANGED: | ||
| 263 | default: | ||
| 264 | return access; | ||
| 265 | } | ||
| 266 | } | ||
| 267 | } | ||
| 268 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java deleted file mode 100644 index a42f255..0000000 --- a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java +++ /dev/null | |||
| @@ -1,101 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import com.google.common.collect.Maps; | ||
| 16 | import cuchaz.enigma.analysis.JarIndex; | ||
| 17 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 18 | import cuchaz.enigma.mapping.entry.EntryFactory; | ||
| 19 | import cuchaz.enigma.mapping.entry.FieldEntry; | ||
| 20 | import cuchaz.enigma.mapping.entry.MethodEntry; | ||
| 21 | |||
| 22 | import java.util.Map; | ||
| 23 | |||
| 24 | public class MappingsChecker { | ||
| 25 | |||
| 26 | private JarIndex index; | ||
| 27 | private Map<ClassEntry, ClassMapping> droppedClassMappings; | ||
| 28 | private Map<ClassEntry, ClassMapping> droppedInnerClassMappings; | ||
| 29 | private Map<FieldEntry, FieldMapping> droppedFieldMappings; | ||
| 30 | private Map<MethodEntry, MethodMapping> droppedMethodMappings; | ||
| 31 | |||
| 32 | public MappingsChecker(JarIndex index) { | ||
| 33 | this.index = index; | ||
| 34 | this.droppedClassMappings = Maps.newHashMap(); | ||
| 35 | this.droppedInnerClassMappings = Maps.newHashMap(); | ||
| 36 | this.droppedFieldMappings = Maps.newHashMap(); | ||
| 37 | this.droppedMethodMappings = Maps.newHashMap(); | ||
| 38 | } | ||
| 39 | |||
| 40 | public Map<ClassEntry, ClassMapping> getDroppedClassMappings() { | ||
| 41 | return this.droppedClassMappings; | ||
| 42 | } | ||
| 43 | |||
| 44 | public Map<ClassEntry, ClassMapping> getDroppedInnerClassMappings() { | ||
| 45 | return this.droppedInnerClassMappings; | ||
| 46 | } | ||
| 47 | |||
| 48 | public Map<FieldEntry, FieldMapping> getDroppedFieldMappings() { | ||
| 49 | return this.droppedFieldMappings; | ||
| 50 | } | ||
| 51 | |||
| 52 | public Map<MethodEntry, MethodMapping> getDroppedMethodMappings() { | ||
| 53 | return this.droppedMethodMappings; | ||
| 54 | } | ||
| 55 | |||
| 56 | public void dropBrokenMappings(Mappings mappings) { | ||
| 57 | for (ClassMapping classMapping : Lists.newArrayList(mappings.classes())) { | ||
| 58 | if (!checkClassMapping(classMapping)) { | ||
| 59 | mappings.removeClassMapping(classMapping); | ||
| 60 | this.droppedClassMappings.put(EntryFactory.getObfClassEntry(this.index, classMapping), classMapping); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | private boolean checkClassMapping(ClassMapping classMapping) { | ||
| 66 | |||
| 67 | // check the class | ||
| 68 | ClassEntry classEntry = EntryFactory.getObfClassEntry(this.index, classMapping); | ||
| 69 | if (!this.index.getObfClassEntries().contains(classEntry)) { | ||
| 70 | return false; | ||
| 71 | } | ||
| 72 | |||
| 73 | // check the fields | ||
| 74 | for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { | ||
| 75 | FieldEntry obfFieldEntry = EntryFactory.getObfFieldEntry(classMapping, fieldMapping); | ||
| 76 | if (!this.index.containsObfField(obfFieldEntry)) { | ||
| 77 | classMapping.removeFieldMapping(fieldMapping); | ||
| 78 | this.droppedFieldMappings.put(obfFieldEntry, fieldMapping); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | // check methods | ||
| 83 | for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { | ||
| 84 | MethodEntry obfMethodEntry = EntryFactory.getObfMethodEntry(classEntry, methodMapping); | ||
| 85 | if (!this.index.containsObfMethod(obfMethodEntry)) { | ||
| 86 | classMapping.removeMethodMapping(methodMapping); | ||
| 87 | this.droppedMethodMappings.put(obfMethodEntry, methodMapping); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | // check inner classes | ||
| 92 | for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) { | ||
| 93 | if (!checkClassMapping(innerClassMapping)) { | ||
| 94 | classMapping.removeInnerClassMapping(innerClassMapping); | ||
| 95 | this.droppedInnerClassMappings.put(EntryFactory.getObfClassEntry(this.index, innerClassMapping), innerClassMapping); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | return true; | ||
| 100 | } | ||
| 101 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java deleted file mode 100644 index ddbee76..0000000 --- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java +++ /dev/null | |||
| @@ -1,186 +0,0 @@ | |||
| 1 | package cuchaz.enigma.mapping; | ||
| 2 | |||
| 3 | import com.google.common.base.Charsets; | ||
| 4 | import com.google.common.collect.Queues; | ||
| 5 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 6 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 7 | |||
| 8 | import java.io.BufferedReader; | ||
| 9 | import java.io.File; | ||
| 10 | import java.io.FileInputStream; | ||
| 11 | import java.io.IOException; | ||
| 12 | import java.io.InputStream; | ||
| 13 | import java.io.InputStreamReader; | ||
| 14 | import java.util.Deque; | ||
| 15 | import java.util.function.Supplier; | ||
| 16 | |||
| 17 | public class MappingsEnigmaReader { | ||
| 18 | |||
| 19 | public Mappings read(File file) throws IOException, MappingParseException { | ||
| 20 | Mappings mappings; | ||
| 21 | |||
| 22 | // Multiple file | ||
| 23 | if (file.isDirectory()) { | ||
| 24 | mappings = new Mappings(Mappings.FormatType.ENIGMA_DIRECTORY); | ||
| 25 | readDirectory(mappings, file); | ||
| 26 | } else { | ||
| 27 | mappings = new Mappings(); | ||
| 28 | readFile(mappings, file); | ||
| 29 | } | ||
| 30 | return mappings; | ||
| 31 | } | ||
| 32 | |||
| 33 | public void readDirectory(Mappings mappings, File directory) throws IOException, MappingParseException { | ||
| 34 | File[] files = directory.listFiles(); | ||
| 35 | if (files != null) { | ||
| 36 | for (File file : files) { | ||
| 37 | if (file.isFile() && !file.getName().startsWith(".") && file.getName().endsWith(".mapping")) | ||
| 38 | readFile(mappings, file); | ||
| 39 | else if (file.isDirectory()) | ||
| 40 | readDirectory(mappings, file.getAbsoluteFile()); | ||
| 41 | } | ||
| 42 | mappings.savePreviousState(); | ||
| 43 | } else | ||
| 44 | throw new IOException("Cannot access directory" + directory.getAbsolutePath()); | ||
| 45 | } | ||
| 46 | |||
| 47 | public Mappings readFile(Mappings mappings, File file) throws IOException, MappingParseException { | ||
| 48 | return readFileStream(mappings, new FileInputStream(file), file::getAbsolutePath); | ||
| 49 | } | ||
| 50 | |||
| 51 | public Mappings readFileStream(Mappings mappings, InputStream stream, Supplier<String> filenameSupplier) throws IOException, MappingParseException { | ||
| 52 | try (BufferedReader in = new BufferedReader(new InputStreamReader(stream, Charsets.UTF_8))) { | ||
| 53 | Deque<Object> mappingStack = Queues.newArrayDeque(); | ||
| 54 | |||
| 55 | int lineNumber = 0; | ||
| 56 | String line; | ||
| 57 | while ((line = in.readLine()) != null) { | ||
| 58 | lineNumber++; | ||
| 59 | |||
| 60 | // strip comments | ||
| 61 | int commentPos = line.indexOf('#'); | ||
| 62 | if (commentPos >= 0) { | ||
| 63 | line = line.substring(0, commentPos); | ||
| 64 | } | ||
| 65 | |||
| 66 | // skip blank lines | ||
| 67 | if (line.trim().length() <= 0) { | ||
| 68 | continue; | ||
| 69 | } | ||
| 70 | |||
| 71 | // get the indent of this line | ||
| 72 | int indent = 0; | ||
| 73 | for (int i = 0; i < line.length(); i++) { | ||
| 74 | if (line.charAt(i) != '\t') { | ||
| 75 | break; | ||
| 76 | } | ||
| 77 | indent++; | ||
| 78 | } | ||
| 79 | |||
| 80 | // handle stack pops | ||
| 81 | while (indent < mappingStack.size()) { | ||
| 82 | mappingStack.pop(); | ||
| 83 | } | ||
| 84 | |||
| 85 | String[] parts = line.trim().split("\\s"); | ||
| 86 | try { | ||
| 87 | // read the first token | ||
| 88 | String token = parts[0]; | ||
| 89 | |||
| 90 | if (token.equalsIgnoreCase("CLASS")) { | ||
| 91 | ClassMapping classMapping; | ||
| 92 | if (indent <= 0) { | ||
| 93 | // outer class | ||
| 94 | classMapping = readClass(parts, false); | ||
| 95 | mappings.addClassMapping(classMapping); | ||
| 96 | } else { | ||
| 97 | |||
| 98 | // inner class | ||
| 99 | if (!(mappingStack.peek() instanceof ClassMapping)) { | ||
| 100 | throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected CLASS entry here!"); | ||
| 101 | } | ||
| 102 | |||
| 103 | classMapping = readClass(parts, true); | ||
| 104 | ((ClassMapping) mappingStack.peek()).addInnerClassMapping(classMapping); | ||
| 105 | } | ||
| 106 | mappingStack.push(classMapping); | ||
| 107 | } else if (token.equalsIgnoreCase("FIELD")) { | ||
| 108 | if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { | ||
| 109 | throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected FIELD entry here!"); | ||
| 110 | } | ||
| 111 | ((ClassMapping) mappingStack.peek()).addFieldMapping(readField(parts)); | ||
| 112 | } else if (token.equalsIgnoreCase("METHOD")) { | ||
| 113 | if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { | ||
| 114 | throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected METHOD entry here!"); | ||
| 115 | } | ||
| 116 | MethodMapping methodMapping = readMethod(parts); | ||
| 117 | ((ClassMapping) mappingStack.peek()).addMethodMapping(methodMapping); | ||
| 118 | mappingStack.push(methodMapping); | ||
| 119 | } else if (token.equalsIgnoreCase("ARG")) { | ||
| 120 | if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof MethodMapping)) { | ||
| 121 | throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected ARG entry here!"); | ||
| 122 | } | ||
| 123 | ((MethodMapping) mappingStack.peek()).addArgumentMapping(readArgument(parts)); | ||
| 124 | } | ||
| 125 | } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) { | ||
| 126 | throw new MappingParseException(filenameSupplier, lineNumber, "Malformed line:\n" + line); | ||
| 127 | } catch (MappingConflict e) { | ||
| 128 | throw new MappingParseException(filenameSupplier, lineNumber, e.getMessage()); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | return mappings; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | private LocalVariableMapping readArgument(String[] parts) { | ||
| 136 | return new LocalVariableMapping(Integer.parseInt(parts[1]), parts[2]); | ||
| 137 | } | ||
| 138 | |||
| 139 | private ClassMapping readClass(String[] parts, boolean makeSimple) { | ||
| 140 | if (parts.length == 2) { | ||
| 141 | return new ClassMapping(parts[1]); | ||
| 142 | } else if (parts.length == 3) { | ||
| 143 | boolean access = parts[2].startsWith("ACC:"); | ||
| 144 | ClassMapping mapping; | ||
| 145 | if (access) | ||
| 146 | mapping = new ClassMapping(parts[1], null, Mappings.EntryModifier.valueOf(parts[2].substring(4))); | ||
| 147 | else | ||
| 148 | mapping = new ClassMapping(parts[1], parts[2]); | ||
| 149 | |||
| 150 | return mapping; | ||
| 151 | } else if (parts.length == 4) | ||
| 152 | return new ClassMapping(parts[1], parts[2], Mappings.EntryModifier.valueOf(parts[3].substring(4))); | ||
| 153 | return null; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* TEMP */ | ||
| 157 | protected FieldMapping readField(String[] parts) { | ||
| 158 | FieldMapping mapping = null; | ||
| 159 | if (parts.length == 4) { | ||
| 160 | boolean access = parts[3].startsWith("ACC:"); | ||
| 161 | if (access) | ||
| 162 | mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[2]), null, | ||
| 163 | Mappings.EntryModifier.valueOf(parts[3].substring(4))); | ||
| 164 | else | ||
| 165 | mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED); | ||
| 166 | } else if (parts.length == 5) | ||
| 167 | mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4))); | ||
| 168 | return mapping; | ||
| 169 | } | ||
| 170 | |||
| 171 | private MethodMapping readMethod(String[] parts) { | ||
| 172 | MethodMapping mapping = null; | ||
| 173 | if (parts.length == 3) | ||
| 174 | mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2])); | ||
| 175 | else if (parts.length == 4) { | ||
| 176 | boolean access = parts[3].startsWith("ACC:"); | ||
| 177 | if (access) | ||
| 178 | mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4))); | ||
| 179 | else | ||
| 180 | mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2]); | ||
| 181 | } else if (parts.length == 5) | ||
| 182 | mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2], | ||
| 183 | Mappings.EntryModifier.valueOf(parts[4].substring(4))); | ||
| 184 | return mapping; | ||
| 185 | } | ||
| 186 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java deleted file mode 100644 index e3302b1..0000000 --- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java +++ /dev/null | |||
| @@ -1,160 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.base.Charsets; | ||
| 15 | |||
| 16 | import java.io.*; | ||
| 17 | import java.util.*; | ||
| 18 | |||
| 19 | public class MappingsEnigmaWriter { | ||
| 20 | |||
| 21 | public void write(File out, Mappings mappings, boolean isDirectoryFormat) throws IOException { | ||
| 22 | if (!isDirectoryFormat) { | ||
| 23 | PrintWriter outputWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(out), Charsets.UTF_8)); | ||
| 24 | write(outputWriter, mappings); | ||
| 25 | outputWriter.close(); | ||
| 26 | } else | ||
| 27 | writeAsDirectory(out, mappings); | ||
| 28 | } | ||
| 29 | |||
| 30 | public void writeAsDirectory(File target, Mappings mappings) throws IOException { | ||
| 31 | if (!target.exists() && !target.mkdirs()) | ||
| 32 | throw new IOException("Cannot create mapping directory!"); | ||
| 33 | |||
| 34 | Mappings previousState = mappings.getPreviousState(); | ||
| 35 | for (ClassMapping classMapping : sorted(mappings.classes())) { | ||
| 36 | File result = new File(target, classMapping.getSaveName() + ".mapping"); | ||
| 37 | |||
| 38 | if (!classMapping.isDirty()) { | ||
| 39 | continue; | ||
| 40 | } | ||
| 41 | |||
| 42 | if (classMapping.isEmpty()) { | ||
| 43 | if (result.exists()) { | ||
| 44 | result.delete(); | ||
| 45 | } | ||
| 46 | continue; | ||
| 47 | } | ||
| 48 | |||
| 49 | if (previousState != null) { | ||
| 50 | ClassMapping previousClass = previousState.classesByObf.get(classMapping.getObfFullName()); | ||
| 51 | File previousFile; | ||
| 52 | if (previousClass != null) { | ||
| 53 | previousFile = new File(target, previousClass.getSaveName() + ".mapping"); | ||
| 54 | } else { | ||
| 55 | previousFile = new File(target, classMapping.getObfFullName() + ".mapping"); | ||
| 56 | } | ||
| 57 | if (previousFile.exists() && !previousFile.delete()) { | ||
| 58 | System.err.println("Failed to delete old class mapping " + previousFile.getName()); | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | File packageFile = result.getParentFile(); | ||
| 63 | if (!packageFile.exists()) { | ||
| 64 | packageFile.mkdirs(); | ||
| 65 | } | ||
| 66 | result.createNewFile(); | ||
| 67 | |||
| 68 | try (PrintWriter outputWriter = new PrintWriter(new BufferedWriter(new FileWriter(result)))) { | ||
| 69 | write(outputWriter, classMapping, 0); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | // Remove dropped mappings | ||
| 74 | if (previousState != null) { | ||
| 75 | Set<ClassMapping> droppedClassMappings = new HashSet<>(previousState.classes()); | ||
| 76 | droppedClassMappings.removeAll(mappings.classes()); | ||
| 77 | for (ClassMapping droppedMapping : droppedClassMappings) { | ||
| 78 | File result = new File(target, droppedMapping.getSaveName() + ".mapping"); | ||
| 79 | if (!result.exists()) { | ||
| 80 | continue; | ||
| 81 | } | ||
| 82 | if (!result.delete()) { | ||
| 83 | System.err.println("Failed to delete dropped class mapping " + result.getName()); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | public void write(PrintWriter out, Mappings mappings) throws IOException { | ||
| 90 | for (ClassMapping classMapping : sorted(mappings.classes())) { | ||
| 91 | write(out, classMapping, 0); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | protected void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException { | ||
| 96 | if (classMapping.getDeobfName() == null) { | ||
| 97 | out.format("%sCLASS %s%s\n", getIndent(depth), classMapping.getObfFullName(), | ||
| 98 | classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); | ||
| 99 | } else { | ||
| 100 | out.format("%sCLASS %s %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName(), | ||
| 101 | classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); | ||
| 102 | } | ||
| 103 | |||
| 104 | for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { | ||
| 105 | write(out, innerClassMapping, depth + 1); | ||
| 106 | } | ||
| 107 | |||
| 108 | for (FieldMapping fieldMapping : sorted(classMapping.fields())) { | ||
| 109 | write(out, fieldMapping, depth + 1); | ||
| 110 | } | ||
| 111 | |||
| 112 | for (MethodMapping methodMapping : sorted(classMapping.methods())) { | ||
| 113 | write(out, methodMapping, depth + 1); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | private void write(PrintWriter out, FieldMapping fieldMapping, int depth) { | ||
| 118 | if (fieldMapping.getDeobfName() == null) | ||
| 119 | out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfDesc().toString(), | ||
| 120 | fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); | ||
| 121 | else | ||
| 122 | out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfDesc().toString(), | ||
| 123 | fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); | ||
| 124 | } | ||
| 125 | |||
| 126 | private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException { | ||
| 127 | if (methodMapping.isObfuscated()) { | ||
| 128 | out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfDesc(), | ||
| 129 | methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); | ||
| 130 | } else { | ||
| 131 | out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfDesc(), | ||
| 132 | methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); | ||
| 133 | } | ||
| 134 | |||
| 135 | for (LocalVariableMapping localVariableMapping : sorted(methodMapping.arguments())) { | ||
| 136 | write(out, localVariableMapping, depth + 1); | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | private void write(PrintWriter out, LocalVariableMapping localVariableMapping, int depth) { | ||
| 141 | out.format("%sARG %d %s\n", getIndent(depth), localVariableMapping.getIndex(), localVariableMapping.getName()); | ||
| 142 | } | ||
| 143 | |||
| 144 | protected <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) { | ||
| 145 | List<T> out = new ArrayList<>(); | ||
| 146 | for (T t : classes) { | ||
| 147 | out.add(t); | ||
| 148 | } | ||
| 149 | Collections.sort(out); | ||
| 150 | return out; | ||
| 151 | } | ||
| 152 | |||
| 153 | private String getIndent(int depth) { | ||
| 154 | StringBuilder buf = new StringBuilder(); | ||
| 155 | for (int i = 0; i < depth; i++) { | ||
| 156 | buf.append("\t"); | ||
| 157 | } | ||
| 158 | return buf.toString(); | ||
| 159 | } | ||
| 160 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java deleted file mode 100644 index 8ef4f12..0000000 --- a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java +++ /dev/null | |||
| @@ -1,365 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import cuchaz.enigma.analysis.JarIndex; | ||
| 16 | import cuchaz.enigma.mapping.entry.*; | ||
| 17 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 18 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 19 | |||
| 20 | import java.io.IOException; | ||
| 21 | import java.io.ObjectOutputStream; | ||
| 22 | import java.io.OutputStream; | ||
| 23 | import java.util.List; | ||
| 24 | import java.util.Set; | ||
| 25 | import java.util.zip.GZIPOutputStream; | ||
| 26 | |||
| 27 | public class MappingsRenamer { | ||
| 28 | |||
| 29 | private final JarIndex index; | ||
| 30 | private final ReferencedEntryPool entryPool; | ||
| 31 | private Mappings mappings; | ||
| 32 | |||
| 33 | public MappingsRenamer(JarIndex index, Mappings mappings, ReferencedEntryPool entryPool) { | ||
| 34 | this.index = index; | ||
| 35 | this.mappings = mappings; | ||
| 36 | this.entryPool = entryPool; | ||
| 37 | } | ||
| 38 | |||
| 39 | public void setMappings(Mappings mappings) { | ||
| 40 | this.mappings = mappings; | ||
| 41 | } | ||
| 42 | |||
| 43 | public void setClassName(ClassEntry obf, String deobfName) { | ||
| 44 | |||
| 45 | deobfName = NameValidator.validateClassName(deobfName, !obf.isInnerClass()); | ||
| 46 | |||
| 47 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf); | ||
| 48 | if (mappingChain.size() == 1) { | ||
| 49 | |||
| 50 | if (deobfName != null) { | ||
| 51 | // make sure we don't rename to an existing obf or deobf class | ||
| 52 | if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(entryPool.getClass(deobfName))) { | ||
| 53 | throw new IllegalNameException(deobfName, "There is already a class with that name"); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | ClassMapping classMapping = mappingChain.get(0); | ||
| 58 | mappings.setClassDeobfName(classMapping, deobfName); | ||
| 59 | |||
| 60 | } else { | ||
| 61 | |||
| 62 | ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); | ||
| 63 | |||
| 64 | if (deobfName != null) { | ||
| 65 | // make sure we don't rename to an existing obf or deobf inner class | ||
| 66 | if (outerClassMapping.hasInnerClassByDeobf(deobfName) || outerClassMapping.hasInnerClassByObfSimple(deobfName)) { | ||
| 67 | throw new IllegalNameException(deobfName, "There is already a class with that name"); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | outerClassMapping.setInnerClassName(obf, deobfName); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | public void removeClassMapping(ClassEntry obf) { | ||
| 76 | setClassName(obf, null); | ||
| 77 | } | ||
| 78 | |||
| 79 | public void markClassAsDeobfuscated(ClassEntry obf) { | ||
| 80 | String deobfName = obf.isInnerClass() ? obf.getInnermostClassName() : obf.getName(); | ||
| 81 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf); | ||
| 82 | if (mappingChain.size() == 1) { | ||
| 83 | ClassMapping classMapping = mappingChain.get(0); | ||
| 84 | mappings.setClassDeobfName(classMapping, deobfName); | ||
| 85 | } else { | ||
| 86 | ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); | ||
| 87 | outerClassMapping.setInnerClassName(obf, deobfName); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | public void setFieldName(FieldEntry obf, String deobfName) { | ||
| 92 | deobfName = NameValidator.validateFieldName(deobfName); | ||
| 93 | FieldEntry targetEntry = entryPool.getField(obf.getOwnerClassEntry(), deobfName, obf.getDesc()); | ||
| 94 | ClassEntry definedClass = null; | ||
| 95 | if (mappings.containsDeobfField(obf.getOwnerClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry)) { | ||
| 96 | definedClass = obf.getOwnerClassEntry(); | ||
| 97 | } else { | ||
| 98 | for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getOwnerClassEntry())) { | ||
| 99 | if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.updateOwnership(ancestorEntry))) { | ||
| 100 | definedClass = ancestorEntry; | ||
| 101 | break; | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | if (definedClass != null) { | ||
| 107 | Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex()); | ||
| 108 | String className = translator.getTranslatedClass(entryPool.getClass(definedClass.getClassName())).getName(); | ||
| 109 | if (className == null) | ||
| 110 | className = definedClass.getClassName(); | ||
| 111 | throw new IllegalNameException(deobfName, "There is already a field with that name in " + className); | ||
| 112 | } | ||
| 113 | |||
| 114 | ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); | ||
| 115 | classMapping.setFieldName(obf.getName(), obf.getDesc(), deobfName); | ||
| 116 | } | ||
| 117 | |||
| 118 | public void removeFieldMapping(FieldEntry obf) { | ||
| 119 | ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); | ||
| 120 | classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getDesc())); | ||
| 121 | } | ||
| 122 | |||
| 123 | public void markFieldAsDeobfuscated(FieldEntry obf) { | ||
| 124 | ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); | ||
| 125 | classMapping.setFieldName(obf.getName(), obf.getDesc(), obf.getName()); | ||
| 126 | } | ||
| 127 | |||
| 128 | private void validateMethodTreeName(MethodEntry entry, String deobfName) { | ||
| 129 | MethodEntry targetEntry = entryPool.getMethod(entry.getOwnerClassEntry(), deobfName, entry.getDesc()); | ||
| 130 | |||
| 131 | // TODO: Verify if I don't break things | ||
| 132 | ClassMapping classMapping = mappings.getClassByObf(entry.getOwnerClassEntry()); | ||
| 133 | if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getDesc()) && classMapping.getMethodByObf(entry.getName(), entry.getDesc()) != classMapping.getMethodByDeobf(deobfName, entry.getDesc())) | ||
| 134 | || index.containsObfMethod(targetEntry)) { | ||
| 135 | Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex()); | ||
| 136 | String deobfClassName = translator.getTranslatedClass(entryPool.getClass(entry.getClassName())).getClassName(); | ||
| 137 | if (deobfClassName == null) { | ||
| 138 | deobfClassName = entry.getClassName(); | ||
| 139 | } | ||
| 140 | throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); | ||
| 141 | } | ||
| 142 | |||
| 143 | for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getOwnerClassEntry())) { | ||
| 144 | validateMethodTreeName(entry.updateOwnership(child), deobfName); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | public void setMethodTreeName(MethodEntry obf, String deobfName) { | ||
| 149 | Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obf); | ||
| 150 | |||
| 151 | deobfName = NameValidator.validateMethodName(deobfName); | ||
| 152 | for (MethodEntry entry : implementations) { | ||
| 153 | validateMethodTreeName(entry, deobfName); | ||
| 154 | } | ||
| 155 | |||
| 156 | for (MethodEntry entry : implementations) { | ||
| 157 | setMethodName(entry, deobfName); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | public void setMethodName(MethodEntry obf, String deobfName) { | ||
| 162 | deobfName = NameValidator.validateMethodName(deobfName); | ||
| 163 | MethodEntry targetEntry = entryPool.getMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc()); | ||
| 164 | ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); | ||
| 165 | |||
| 166 | // TODO: Verify if I don't break things | ||
| 167 | if ((mappings.containsDeobfMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc()) && classMapping.getMethodByObf(obf.getName(), obf.getDesc()) != classMapping.getMethodByDeobf(deobfName, obf.getDesc())) | ||
| 168 | || index.containsObfMethod(targetEntry)) { | ||
| 169 | Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex()); | ||
| 170 | String deobfClassName = translator.getTranslatedClass(entryPool.getClass(obf.getClassName())).getClassName(); | ||
| 171 | if (deobfClassName == null) { | ||
| 172 | deobfClassName = obf.getClassName(); | ||
| 173 | } | ||
| 174 | throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); | ||
| 175 | } | ||
| 176 | |||
| 177 | classMapping.setMethodName(obf.getName(), obf.getDesc(), deobfName); | ||
| 178 | } | ||
| 179 | |||
| 180 | public void removeMethodTreeMapping(MethodEntry obf) { | ||
| 181 | index.getRelatedMethodImplementations(obf).forEach(this::removeMethodMapping); | ||
| 182 | } | ||
| 183 | |||
| 184 | public void removeMethodMapping(MethodEntry obf) { | ||
| 185 | ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); | ||
| 186 | classMapping.setMethodName(obf.getName(), obf.getDesc(), null); | ||
| 187 | } | ||
| 188 | |||
| 189 | public void markMethodTreeAsDeobfuscated(MethodEntry obf) { | ||
| 190 | index.getRelatedMethodImplementations(obf).forEach(this::markMethodAsDeobfuscated); | ||
| 191 | } | ||
| 192 | |||
| 193 | public void markMethodAsDeobfuscated(MethodEntry obf) { | ||
| 194 | ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); | ||
| 195 | classMapping.setMethodName(obf.getName(), obf.getDesc(), obf.getName()); | ||
| 196 | } | ||
| 197 | |||
| 198 | public void setLocalVariableTreeName(LocalVariableEntry obf, String deobfName) { | ||
| 199 | MethodEntry obfMethod = obf.getOwnerEntry(); | ||
| 200 | if (!obf.isParameter()) { | ||
| 201 | setLocalVariableName(obf, deobfName); | ||
| 202 | return; | ||
| 203 | } | ||
| 204 | |||
| 205 | Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obfMethod); | ||
| 206 | for (MethodEntry entry : implementations) { | ||
| 207 | ClassMapping classMapping = mappings.getClassByObf(entry.getOwnerClassEntry()); | ||
| 208 | if (classMapping != null) { | ||
| 209 | MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getDesc()); | ||
| 210 | // NOTE: don't need to check arguments for name collisions with names determined by Procyon | ||
| 211 | // TODO: Verify if I don't break things | ||
| 212 | if (mapping != null) { | ||
| 213 | for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) { | ||
| 214 | if (localVariableMapping.getIndex() != obf.getIndex()) { | ||
| 215 | if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName) | ||
| 216 | || localVariableMapping.getName().equals(deobfName)) { | ||
| 217 | throw new IllegalNameException(deobfName, "There is already an argument with that name"); | ||
| 218 | } | ||
| 219 | } | ||
| 220 | } | ||
| 221 | } | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | for (MethodEntry entry : implementations) { | ||
| 226 | setLocalVariableName(new LocalVariableEntry(entry, obf.getIndex(), obf.getName(), obf.isParameter()), deobfName); | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | public void setLocalVariableName(LocalVariableEntry obf, String deobfName) { | ||
| 231 | deobfName = NameValidator.validateArgumentName(deobfName); | ||
| 232 | ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); | ||
| 233 | MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodDesc()); | ||
| 234 | // NOTE: don't need to check arguments for name collisions with names determined by Procyon | ||
| 235 | // TODO: Verify if I don't break things | ||
| 236 | if (mapping != null) { | ||
| 237 | for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) { | ||
| 238 | if (localVariableMapping.getIndex() != obf.getIndex()) { | ||
| 239 | if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName) | ||
| 240 | || localVariableMapping.getName().equals(deobfName)) { | ||
| 241 | throw new IllegalNameException(deobfName, "There is already an argument with that name"); | ||
| 242 | } | ||
| 243 | } | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), deobfName); | ||
| 248 | } | ||
| 249 | |||
| 250 | public void removeLocalVariableMapping(LocalVariableEntry obf) { | ||
| 251 | ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); | ||
| 252 | classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex()); | ||
| 253 | } | ||
| 254 | |||
| 255 | public void markArgumentAsDeobfuscated(LocalVariableEntry obf) { | ||
| 256 | ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); | ||
| 257 | classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), obf.getName()); | ||
| 258 | } | ||
| 259 | |||
| 260 | public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { | ||
| 261 | classMapping.removeFieldMapping(fieldMapping); | ||
| 262 | ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); | ||
| 263 | if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfDesc())) { | ||
| 264 | if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) { | ||
| 265 | targetClassMapping.addFieldMapping(fieldMapping); | ||
| 266 | return true; | ||
| 267 | } else { | ||
| 268 | System.err.println("WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName()); | ||
| 269 | } | ||
| 270 | } | ||
| 271 | return false; | ||
| 272 | } | ||
| 273 | |||
| 274 | public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { | ||
| 275 | classMapping.removeMethodMapping(methodMapping); | ||
| 276 | ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); | ||
| 277 | if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfDesc())) { | ||
| 278 | if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfDesc())) { | ||
| 279 | targetClassMapping.addMethodMapping(methodMapping); | ||
| 280 | return true; | ||
| 281 | } else { | ||
| 282 | System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfDesc()); | ||
| 283 | } | ||
| 284 | } | ||
| 285 | return false; | ||
| 286 | } | ||
| 287 | |||
| 288 | public void write(OutputStream out) throws IOException { | ||
| 289 | // TEMP: just use the object output for now. We can find a more efficient storage format later | ||
| 290 | GZIPOutputStream gzipout = new GZIPOutputStream(out); | ||
| 291 | ObjectOutputStream oout = new ObjectOutputStream(gzipout); | ||
| 292 | oout.writeObject(this); | ||
| 293 | gzipout.finish(); | ||
| 294 | } | ||
| 295 | |||
| 296 | private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) { | ||
| 297 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obfClassEntry); | ||
| 298 | return mappingChain.get(mappingChain.size() - 1); | ||
| 299 | } | ||
| 300 | |||
| 301 | private List<ClassMapping> getOrCreateClassMappingChain(ClassEntry obfClassEntry) { | ||
| 302 | List<ClassEntry> classChain = obfClassEntry.getClassChain(); | ||
| 303 | List<ClassMapping> mappingChain = mappings.getClassMappingChain(obfClassEntry); | ||
| 304 | for (int i = 0; i < classChain.size(); i++) { | ||
| 305 | ClassEntry classEntry = classChain.get(i); | ||
| 306 | ClassMapping classMapping = mappingChain.get(i); | ||
| 307 | if (classMapping == null) { | ||
| 308 | |||
| 309 | // create it | ||
| 310 | classMapping = new ClassMapping(classEntry.getName()); | ||
| 311 | mappingChain.set(i, classMapping); | ||
| 312 | |||
| 313 | // add it to the right parent | ||
| 314 | try { | ||
| 315 | if (i == 0) { | ||
| 316 | mappings.addClassMapping(classMapping); | ||
| 317 | } else { | ||
| 318 | mappingChain.get(i - 1).addInnerClassMapping(classMapping); | ||
| 319 | } | ||
| 320 | } catch (MappingConflict mappingConflict) { | ||
| 321 | mappingConflict.printStackTrace(); | ||
| 322 | } | ||
| 323 | } | ||
| 324 | } | ||
| 325 | return mappingChain; | ||
| 326 | } | ||
| 327 | |||
| 328 | public void setClassModifier(ClassEntry obEntry, Mappings.EntryModifier modifier) { | ||
| 329 | ClassMapping classMapping = getOrCreateClassMapping(obEntry); | ||
| 330 | classMapping.setModifier(modifier); | ||
| 331 | } | ||
| 332 | |||
| 333 | public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) { | ||
| 334 | ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry()); | ||
| 335 | classMapping.setFieldModifier(obEntry.getName(), obEntry.getDesc(), modifier); | ||
| 336 | } | ||
| 337 | |||
| 338 | public void setMethodModifier(MethodEntry obEntry, Mappings.EntryModifier modifier) { | ||
| 339 | ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry()); | ||
| 340 | classMapping.setMethodModifier(obEntry.getName(), obEntry.getDesc(), modifier); | ||
| 341 | } | ||
| 342 | |||
| 343 | public Mappings.EntryModifier getClassModifier(ClassEntry obfEntry) { | ||
| 344 | ClassMapping classMapping = getOrCreateClassMapping(obfEntry); | ||
| 345 | return classMapping.getModifier(); | ||
| 346 | } | ||
| 347 | |||
| 348 | public Mappings.EntryModifier getFieldModifier(FieldEntry obfEntry) { | ||
| 349 | ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry()); | ||
| 350 | FieldMapping fieldMapping = classMapping.getFieldByObf(obfEntry); | ||
| 351 | if (fieldMapping == null) { | ||
| 352 | return Mappings.EntryModifier.UNCHANGED; | ||
| 353 | } | ||
| 354 | return fieldMapping.getModifier(); | ||
| 355 | } | ||
| 356 | |||
| 357 | public Mappings.EntryModifier getMethodModfifier(MethodEntry obfEntry) { | ||
| 358 | ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry()); | ||
| 359 | MethodMapping methodMapping = classMapping.getMethodByObf(obfEntry); | ||
| 360 | if (methodMapping == null) { | ||
| 361 | return Mappings.EntryModifier.UNCHANGED; | ||
| 362 | } | ||
| 363 | return methodMapping.getModifier(); | ||
| 364 | } | ||
| 365 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java deleted file mode 100644 index 32f0ee9..0000000 --- a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java +++ /dev/null | |||
| @@ -1,80 +0,0 @@ | |||
| 1 | package cuchaz.enigma.mapping; | ||
| 2 | |||
| 3 | import com.google.common.base.Charsets; | ||
| 4 | import cuchaz.enigma.analysis.TranslationIndex; | ||
| 5 | import cuchaz.enigma.mapping.entry.ReferencedEntryPool; | ||
| 6 | |||
| 7 | import java.io.*; | ||
| 8 | import java.util.ArrayList; | ||
| 9 | import java.util.Collections; | ||
| 10 | import java.util.List; | ||
| 11 | |||
| 12 | /** | ||
| 13 | * Created by Mark on 11/08/2016. | ||
| 14 | */ | ||
| 15 | public class MappingsSRGWriter { | ||
| 16 | |||
| 17 | public void write(File file, Mappings mappings) throws IOException { | ||
| 18 | if (file.exists()) { | ||
| 19 | file.delete(); | ||
| 20 | } | ||
| 21 | file.createNewFile(); | ||
| 22 | |||
| 23 | TranslationIndex index = new TranslationIndex(new ReferencedEntryPool()); | ||
| 24 | |||
| 25 | PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)); | ||
| 26 | List<String> fieldMappings = new ArrayList<>(); | ||
| 27 | List<String> methodMappings = new ArrayList<>(); | ||
| 28 | for (ClassMapping classMapping : sorted(mappings.classes())) { | ||
| 29 | if (classMapping.getDeobfName() == null || classMapping.getObfSimpleName() == null || classMapping.getDeobfName() == null) { | ||
| 30 | continue; | ||
| 31 | } | ||
| 32 | writer.write("CL: " + classMapping.getObfSimpleName() + " " + classMapping.getDeobfName()); | ||
| 33 | writer.write(System.lineSeparator()); | ||
| 34 | for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { | ||
| 35 | if (innerClassMapping.getDeobfName() == null || innerClassMapping.getObfSimpleName() == null || innerClassMapping.getDeobfName() == null) { | ||
| 36 | continue; | ||
| 37 | } | ||
| 38 | String innerClassName = classMapping.getObfSimpleName() + "$" + innerClassMapping.getObfSimpleName(); | ||
| 39 | String innerDeobfClassName = classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName(); | ||
| 40 | writer.write("CL: " + innerClassName + " " + classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName()); | ||
| 41 | writer.write(System.lineSeparator()); | ||
| 42 | for (FieldMapping fieldMapping : sorted(innerClassMapping.fields())) { | ||
| 43 | fieldMappings.add("FD: " + innerClassName + "/" + fieldMapping.getObfName() + " " + innerDeobfClassName + "/" + fieldMapping.getDeobfName()); | ||
| 44 | } | ||
| 45 | |||
| 46 | for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) { | ||
| 47 | methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc())); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | for (FieldMapping fieldMapping : sorted(classMapping.fields())) { | ||
| 52 | fieldMappings.add("FD: " + classMapping.getObfFullName() + "/" + fieldMapping.getObfName() + " " + classMapping.getDeobfName() + "/" + fieldMapping.getDeobfName()); | ||
| 53 | } | ||
| 54 | |||
| 55 | for (MethodMapping methodMapping : sorted(classMapping.methods())) { | ||
| 56 | methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc())); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | for (String fd : fieldMappings) { | ||
| 60 | writer.write(fd); | ||
| 61 | writer.write(System.lineSeparator()); | ||
| 62 | } | ||
| 63 | |||
| 64 | for (String md : methodMappings) { | ||
| 65 | writer.write(md); | ||
| 66 | writer.write(System.lineSeparator()); | ||
| 67 | } | ||
| 68 | |||
| 69 | writer.close(); | ||
| 70 | } | ||
| 71 | |||
| 72 | private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) { | ||
| 73 | List<T> out = new ArrayList<>(); | ||
| 74 | for (T t : classes) { | ||
| 75 | out.add(t); | ||
| 76 | } | ||
| 77 | Collections.sort(out); | ||
| 78 | return out; | ||
| 79 | } | ||
| 80 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java deleted file mode 100644 index 756ac43..0000000 --- a/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java +++ /dev/null | |||
| @@ -1,130 +0,0 @@ | |||
| 1 | package cuchaz.enigma.mapping; | ||
| 2 | |||
| 3 | import com.google.common.base.Charsets; | ||
| 4 | import com.google.common.collect.Maps; | ||
| 5 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 6 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 7 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 8 | |||
| 9 | import java.io.File; | ||
| 10 | import java.io.IOException; | ||
| 11 | import java.nio.file.Files; | ||
| 12 | import java.util.ArrayList; | ||
| 13 | import java.util.List; | ||
| 14 | import java.util.Map; | ||
| 15 | |||
| 16 | public class MappingsTinyReader { | ||
| 17 | public ClassMapping readClass(String[] parts) { | ||
| 18 | // Extract the inner naming of the deob form if it have one | ||
| 19 | String deobName = parts[2].contains("$") ? parts[2].substring(parts[2].lastIndexOf('$') + 1) : parts[2]; | ||
| 20 | return new ClassMapping(parts[1], deobName).setDeobfInner(parts[2]); | ||
| 21 | } | ||
| 22 | |||
| 23 | public FieldMapping readField(String[] parts) { | ||
| 24 | return new FieldMapping(parts[3], new TypeDescriptor(parts[2]), parts[4], Mappings.EntryModifier.UNCHANGED); | ||
| 25 | } | ||
| 26 | |||
| 27 | public MethodMapping readMethod(String[] parts) { | ||
| 28 | return new MethodMapping(parts[3], new MethodDescriptor(parts[2]), parts[4]); | ||
| 29 | } | ||
| 30 | |||
| 31 | public Mappings read(File file) throws IOException, MappingParseException { | ||
| 32 | Mappings mappings = new Mappings(Mappings.FormatType.TINY_FILE); | ||
| 33 | List<String> lines = Files.readAllLines(file.toPath(), Charsets.UTF_8); | ||
| 34 | Map<String, ClassMapping> classMappingMap = Maps.newHashMap(); | ||
| 35 | lines.remove(0); // TODO: use the header | ||
| 36 | for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) { | ||
| 37 | String line = lines.get(lineNumber); | ||
| 38 | String[] parts = line.split("\t"); | ||
| 39 | try { | ||
| 40 | String token = parts[0]; | ||
| 41 | ClassMapping classMapping; | ||
| 42 | switch (token) { | ||
| 43 | case "CLASS": | ||
| 44 | |||
| 45 | // Check for orphan created by field or method entries. It shouldn't be possible but I prefer to handle this case | ||
| 46 | if (classMappingMap.containsKey(parts[1])) { | ||
| 47 | classMapping = classMappingMap.get(parts[1]); | ||
| 48 | |||
| 49 | // We have the full deob name, Enigma only support simple class name so we extract it. | ||
| 50 | String deobName = parts[2].contains("$") ? | ||
| 51 | parts[2].substring(parts[2].lastIndexOf('$') + 1) : | ||
| 52 | parts[2]; | ||
| 53 | |||
| 54 | // Add full deob name to the class mapping to handle inner class after this loop | ||
| 55 | classMappingMap.put(parts[2], classMapping.setDeobfInner(parts[2])); | ||
| 56 | classMapping.setDeobfName(deobName); | ||
| 57 | |||
| 58 | // Avoid to make the mapping dirty directly at the startup | ||
| 59 | classMapping.resetDirty(); | ||
| 60 | } else | ||
| 61 | classMapping = readClass(parts); | ||
| 62 | classMappingMap.put(parts[1], classMapping); | ||
| 63 | break; | ||
| 64 | case "FIELD": | ||
| 65 | // We can have missing classes mappings because they don't have a ob name, so we create it and use it | ||
| 66 | classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1])); | ||
| 67 | classMapping.addFieldMapping(readField(parts)); | ||
| 68 | break; | ||
| 69 | case "METHOD": | ||
| 70 | // We can have missing classes mappings because they don't have a ob name, so we create it and use it | ||
| 71 | classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1])); | ||
| 72 | classMapping.addMethodMapping(readMethod(parts)); | ||
| 73 | break; | ||
| 74 | case "MTH-ARG": | ||
| 75 | classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1])); | ||
| 76 | classMapping.setArgumentName(parts[3], new MethodDescriptor(parts[2]), Integer.parseInt(parts[4]), parts[5]); | ||
| 77 | break; | ||
| 78 | default: | ||
| 79 | throw new MappingParseException(file, lineNumber, "Unknown token '" + token + "' !"); | ||
| 80 | } | ||
| 81 | } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) { | ||
| 82 | ex.printStackTrace(); | ||
| 83 | throw new MappingParseException(file, lineNumber, "Malformed line:\n" + line); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | List<ClassMapping> toRegister = new ArrayList<>(classMappingMap.values()); | ||
| 88 | |||
| 89 | // After having completely parsed the file, we need to register it to the real mapping | ||
| 90 | for (ClassMapping classMapping : toRegister) { | ||
| 91 | ClassEntry obEntry = classMapping.getObfEntry(); | ||
| 92 | ClassEntry deobEntry = classMapping.getDeObfEntry(); | ||
| 93 | try { | ||
| 94 | if (obEntry.isInnerClass()) { | ||
| 95 | ClassMapping parent = classMappingMap.get(obEntry.getOuterClassName()); | ||
| 96 | // Inner class can miss their parent... So we create it and add it to the mappings | ||
| 97 | if (parent == null) { | ||
| 98 | parent = new ClassMapping(obEntry.getOuterClassName()); // FIXME: WE ACTUALLY DON'T MANAGE INNER CLASS OF INNER CLASS | ||
| 99 | classMappingMap.put(obEntry.getOuterClassName(), parent); | ||
| 100 | mappings.addClassMapping(parent); | ||
| 101 | } | ||
| 102 | // Add the inner class to the parent | ||
| 103 | parent.addInnerClassMapping(classMapping); | ||
| 104 | } | ||
| 105 | // obf class can become deobf inner classs, manage this case. | ||
| 106 | else if (deobEntry != null && deobEntry.isInnerClass()) { | ||
| 107 | String outerClassName = deobEntry.getOuterClassName(); | ||
| 108 | ClassMapping parent = classMappingMap.get(outerClassName); | ||
| 109 | |||
| 110 | // Only the inner is deob??? Okay | ||
| 111 | if (parent == null) { | ||
| 112 | parent = classMappingMap.get(outerClassName); | ||
| 113 | if (parent == null) { | ||
| 114 | parent = new ClassMapping(outerClassName); // FIXME: WE ACTUALLY DON'T MANAGE INNER CLASS OF INNER CLASS | ||
| 115 | classMappingMap.put(outerClassName, parent); | ||
| 116 | mappings.addClassMapping(parent); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | parent.addInnerClassMapping(classMapping); | ||
| 120 | } else | ||
| 121 | mappings.addClassMapping(classMapping); | ||
| 122 | } catch (MappingConflict e) { | ||
| 123 | throw new MappingParseException(file, -1, e.getMessage()); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | lines.clear(); | ||
| 127 | classMappingMap.clear(); | ||
| 128 | return mappings; | ||
| 129 | } | ||
| 130 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java deleted file mode 100644 index 6effb91..0000000 --- a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java +++ /dev/null | |||
| @@ -1,21 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 15 | import cuchaz.enigma.mapping.entry.Entry; | ||
| 16 | |||
| 17 | public interface MemberMapping<T extends Entry> { | ||
| 18 | T getObfEntry(ClassEntry classEntry); | ||
| 19 | |||
| 20 | String getObfName(); | ||
| 21 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java b/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java deleted file mode 100644 index 0fc0351..0000000 --- a/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java +++ /dev/null | |||
| @@ -1,114 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 16 | import cuchaz.enigma.utils.Utils; | ||
| 17 | |||
| 18 | import java.util.ArrayList; | ||
| 19 | import java.util.List; | ||
| 20 | import java.util.function.Function; | ||
| 21 | |||
| 22 | public class MethodDescriptor { | ||
| 23 | |||
| 24 | private List<TypeDescriptor> argumentDescs; | ||
| 25 | private TypeDescriptor returnDesc; | ||
| 26 | |||
| 27 | public MethodDescriptor(String desc) { | ||
| 28 | try { | ||
| 29 | this.argumentDescs = Lists.newArrayList(); | ||
| 30 | int i = 0; | ||
| 31 | while (i < desc.length()) { | ||
| 32 | char c = desc.charAt(i); | ||
| 33 | if (c == '(') { | ||
| 34 | assert (this.argumentDescs.isEmpty()); | ||
| 35 | assert (this.returnDesc == null); | ||
| 36 | i++; | ||
| 37 | } else if (c == ')') { | ||
| 38 | i++; | ||
| 39 | break; | ||
| 40 | } else { | ||
| 41 | String type = TypeDescriptor.parseFirst(desc.substring(i)); | ||
| 42 | this.argumentDescs.add(new TypeDescriptor(type)); | ||
| 43 | i += type.length(); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | this.returnDesc = new TypeDescriptor(TypeDescriptor.parseFirst(desc.substring(i))); | ||
| 47 | } catch (Exception ex) { | ||
| 48 | throw new IllegalArgumentException("Unable to parse method descriptor: " + desc, ex); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | public MethodDescriptor(List<TypeDescriptor> argumentDescs, TypeDescriptor returnDesc) { | ||
| 53 | this.argumentDescs = argumentDescs; | ||
| 54 | this.returnDesc = returnDesc; | ||
| 55 | } | ||
| 56 | |||
| 57 | public List<TypeDescriptor> getArgumentDescs() { | ||
| 58 | return this.argumentDescs; | ||
| 59 | } | ||
| 60 | |||
| 61 | public TypeDescriptor getReturnDesc() { | ||
| 62 | return this.returnDesc; | ||
| 63 | } | ||
| 64 | |||
| 65 | @Override | ||
| 66 | public String toString() { | ||
| 67 | StringBuilder buf = new StringBuilder(); | ||
| 68 | buf.append("("); | ||
| 69 | for (TypeDescriptor desc : this.argumentDescs) { | ||
| 70 | buf.append(desc); | ||
| 71 | } | ||
| 72 | buf.append(")"); | ||
| 73 | buf.append(this.returnDesc); | ||
| 74 | return buf.toString(); | ||
| 75 | } | ||
| 76 | |||
| 77 | public Iterable<TypeDescriptor> types() { | ||
| 78 | List<TypeDescriptor> descs = Lists.newArrayList(); | ||
| 79 | descs.addAll(this.argumentDescs); | ||
| 80 | descs.add(this.returnDesc); | ||
| 81 | return descs; | ||
| 82 | } | ||
| 83 | |||
| 84 | @Override | ||
| 85 | public boolean equals(Object other) { | ||
| 86 | return other instanceof MethodDescriptor && equals((MethodDescriptor) other); | ||
| 87 | } | ||
| 88 | |||
| 89 | public boolean equals(MethodDescriptor other) { | ||
| 90 | return this.argumentDescs.equals(other.argumentDescs) && this.returnDesc.equals(other.returnDesc); | ||
| 91 | } | ||
| 92 | |||
| 93 | @Override | ||
| 94 | public int hashCode() { | ||
| 95 | return Utils.combineHashesOrdered(this.argumentDescs.hashCode(), this.returnDesc.hashCode()); | ||
| 96 | } | ||
| 97 | |||
| 98 | public boolean hasClass(ClassEntry classEntry) { | ||
| 99 | for (TypeDescriptor desc : types()) { | ||
| 100 | if (desc.containsType() && desc.getTypeEntry().equals(classEntry)) { | ||
| 101 | return true; | ||
| 102 | } | ||
| 103 | } | ||
| 104 | return false; | ||
| 105 | } | ||
| 106 | |||
| 107 | public MethodDescriptor remap(Function<String, String> remapper) { | ||
| 108 | List<TypeDescriptor> argumentDescs = new ArrayList<>(this.argumentDescs.size()); | ||
| 109 | for (TypeDescriptor desc : this.argumentDescs) { | ||
| 110 | argumentDescs.add(desc.remap(remapper)); | ||
| 111 | } | ||
| 112 | return new MethodDescriptor(argumentDescs, returnDesc.remap(remapper)); | ||
| 113 | } | ||
| 114 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java deleted file mode 100644 index 2f10144..0000000 --- a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java +++ /dev/null | |||
| @@ -1,210 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import com.google.common.collect.Maps; | ||
| 16 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 17 | import cuchaz.enigma.mapping.entry.MethodEntry; | ||
| 18 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 19 | import cuchaz.enigma.throwables.MappingConflict; | ||
| 20 | |||
| 21 | import java.util.Map; | ||
| 22 | |||
| 23 | public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<MethodEntry> { | ||
| 24 | |||
| 25 | private String obfName; | ||
| 26 | private String deobfName; | ||
| 27 | private MethodDescriptor obfDescriptor; | ||
| 28 | private Map<Integer, LocalVariableMapping> localVariables; | ||
| 29 | private Mappings.EntryModifier modifier; | ||
| 30 | |||
| 31 | public MethodMapping(String obfName, MethodDescriptor obfDescriptor) { | ||
| 32 | this(obfName, obfDescriptor, null, Mappings.EntryModifier.UNCHANGED); | ||
| 33 | } | ||
| 34 | |||
| 35 | public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName) { | ||
| 36 | this(obfName, obfDescriptor, deobfName, Mappings.EntryModifier.UNCHANGED); | ||
| 37 | } | ||
| 38 | |||
| 39 | public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName, Mappings.EntryModifier modifier) { | ||
| 40 | Preconditions.checkNotNull(obfName, "Method obf name cannot be null"); | ||
| 41 | Preconditions.checkNotNull(obfDescriptor, "Method obf desc cannot be null"); | ||
| 42 | this.obfName = obfName; | ||
| 43 | this.deobfName = NameValidator.validateMethodName(deobfName); | ||
| 44 | this.obfDescriptor = obfDescriptor; | ||
| 45 | this.localVariables = Maps.newTreeMap(); | ||
| 46 | this.modifier = modifier; | ||
| 47 | } | ||
| 48 | |||
| 49 | public MethodMapping(MethodMapping other, Translator translator) { | ||
| 50 | this.obfName = other.obfName; | ||
| 51 | this.deobfName = other.deobfName; | ||
| 52 | this.modifier = other.modifier; | ||
| 53 | this.obfDescriptor = translator.getTranslatedMethodDesc(other.obfDescriptor); | ||
| 54 | this.localVariables = Maps.newTreeMap(); | ||
| 55 | for (Map.Entry<Integer, LocalVariableMapping> entry : other.localVariables.entrySet()) { | ||
| 56 | this.localVariables.put(entry.getKey(), new LocalVariableMapping(entry.getValue())); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | @Override | ||
| 61 | public String getObfName() { | ||
| 62 | return this.obfName; | ||
| 63 | } | ||
| 64 | |||
| 65 | public void setObfName(String name) { | ||
| 66 | try { | ||
| 67 | NameValidator.validateMethodName(name); | ||
| 68 | } catch (IllegalNameException ex) { | ||
| 69 | // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues | ||
| 70 | if (this.deobfName == null) { | ||
| 71 | System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob")); | ||
| 72 | setDeobfName(name + "_auto_deob"); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | this.obfName = name; | ||
| 76 | } | ||
| 77 | |||
| 78 | public String getDeobfName() { | ||
| 79 | if (deobfName == null) { | ||
| 80 | return obfName; | ||
| 81 | } | ||
| 82 | return this.deobfName; | ||
| 83 | } | ||
| 84 | |||
| 85 | public void setDeobfName(String val) { | ||
| 86 | this.deobfName = NameValidator.validateMethodName(val); | ||
| 87 | } | ||
| 88 | |||
| 89 | public MethodDescriptor getObfDesc() { | ||
| 90 | return this.obfDescriptor; | ||
| 91 | } | ||
| 92 | |||
| 93 | public void setObfDescriptor(MethodDescriptor val) { | ||
| 94 | this.obfDescriptor = val; | ||
| 95 | } | ||
| 96 | |||
| 97 | public Iterable<LocalVariableMapping> arguments() { | ||
| 98 | return this.localVariables.values(); | ||
| 99 | } | ||
| 100 | |||
| 101 | public void addArgumentMapping(LocalVariableMapping localVariableMapping) throws MappingConflict { | ||
| 102 | if (this.localVariables.containsKey(localVariableMapping.getIndex())) { | ||
| 103 | throw new MappingConflict("argument", localVariableMapping.getName(), this.localVariables.get(localVariableMapping.getIndex()).getName()); | ||
| 104 | } | ||
| 105 | this.localVariables.put(localVariableMapping.getIndex(), localVariableMapping); | ||
| 106 | } | ||
| 107 | |||
| 108 | public String getObfLocalVariableName(int index) { | ||
| 109 | LocalVariableMapping localVariableMapping = this.localVariables.get(index); | ||
| 110 | if (localVariableMapping != null) { | ||
| 111 | return localVariableMapping.getName(); | ||
| 112 | } | ||
| 113 | |||
| 114 | return null; | ||
| 115 | } | ||
| 116 | |||
| 117 | public String getDeobfLocalVariableName(int index) { | ||
| 118 | LocalVariableMapping localVariableMapping = this.localVariables.get(index); | ||
| 119 | if (localVariableMapping != null) { | ||
| 120 | return localVariableMapping.getName(); | ||
| 121 | } | ||
| 122 | |||
| 123 | return null; | ||
| 124 | } | ||
| 125 | |||
| 126 | public void setLocalVariableName(int index, String name) { | ||
| 127 | LocalVariableMapping localVariableMapping = this.localVariables.get(index); | ||
| 128 | if (localVariableMapping == null) { | ||
| 129 | localVariableMapping = new LocalVariableMapping(index, name); | ||
| 130 | boolean wasAdded = this.localVariables.put(index, localVariableMapping) == null; | ||
| 131 | assert (wasAdded); | ||
| 132 | } else { | ||
| 133 | localVariableMapping.setName(name); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | public void removeLocalVariableName(int index) { | ||
| 138 | boolean wasRemoved = this.localVariables.remove(index) != null; | ||
| 139 | assert (wasRemoved); | ||
| 140 | } | ||
| 141 | |||
| 142 | @Override | ||
| 143 | public String toString() { | ||
| 144 | StringBuilder buf = new StringBuilder(); | ||
| 145 | buf.append("\t"); | ||
| 146 | buf.append(this.obfName); | ||
| 147 | buf.append(" <-> "); | ||
| 148 | buf.append(this.deobfName); | ||
| 149 | buf.append("\n"); | ||
| 150 | buf.append("\t"); | ||
| 151 | buf.append(this.obfDescriptor); | ||
| 152 | buf.append("\n"); | ||
| 153 | buf.append("\tLocal Variables:\n"); | ||
| 154 | for (LocalVariableMapping localVariableMapping : this.localVariables.values()) { | ||
| 155 | buf.append("\t\t"); | ||
| 156 | buf.append(localVariableMapping.getIndex()); | ||
| 157 | buf.append(" -> "); | ||
| 158 | buf.append(localVariableMapping.getName()); | ||
| 159 | buf.append("\n"); | ||
| 160 | } | ||
| 161 | return buf.toString(); | ||
| 162 | } | ||
| 163 | |||
| 164 | @Override | ||
| 165 | public int compareTo(MethodMapping other) { | ||
| 166 | return (this.obfName + this.obfDescriptor).compareTo(other.obfName + other.obfDescriptor); | ||
| 167 | } | ||
| 168 | |||
| 169 | public boolean containsLocalVariable(String name) { | ||
| 170 | for (LocalVariableMapping localVariableMapping : this.localVariables.values()) { | ||
| 171 | if (localVariableMapping.getName().equals(name)) { | ||
| 172 | return true; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | return false; | ||
| 176 | } | ||
| 177 | |||
| 178 | public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { | ||
| 179 | // rename obf classes in the signature | ||
| 180 | MethodDescriptor newDescriptor = obfDescriptor.remap(className -> { | ||
| 181 | if (className.equals(oldObfClassName)) { | ||
| 182 | return newObfClassName; | ||
| 183 | } | ||
| 184 | return className; | ||
| 185 | }); | ||
| 186 | |||
| 187 | if (!newDescriptor.equals(this.obfDescriptor)) { | ||
| 188 | this.obfDescriptor = newDescriptor; | ||
| 189 | return true; | ||
| 190 | } | ||
| 191 | return false; | ||
| 192 | } | ||
| 193 | |||
| 194 | @Override | ||
| 195 | public MethodEntry getObfEntry(ClassEntry classEntry) { | ||
| 196 | return new MethodEntry(classEntry, this.obfName, this.obfDescriptor); | ||
| 197 | } | ||
| 198 | |||
| 199 | public Mappings.EntryModifier getModifier() { | ||
| 200 | return modifier; | ||
| 201 | } | ||
| 202 | |||
| 203 | public void setModifier(Mappings.EntryModifier modifier) { | ||
| 204 | this.modifier = modifier; | ||
| 205 | } | ||
| 206 | |||
| 207 | public boolean isObfuscated() { | ||
| 208 | return deobfName == null || deobfName.equals(obfName); | ||
| 209 | } | ||
| 210 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/NameValidator.java b/src/main/java/cuchaz/enigma/mapping/NameValidator.java deleted file mode 100644 index fca8cfc..0000000 --- a/src/main/java/cuchaz/enigma/mapping/NameValidator.java +++ /dev/null | |||
| @@ -1,73 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 15 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 16 | |||
| 17 | import java.util.Arrays; | ||
| 18 | import java.util.List; | ||
| 19 | import java.util.regex.Pattern; | ||
| 20 | |||
| 21 | public class NameValidator { | ||
| 22 | |||
| 23 | private static final Pattern IdentifierPattern; | ||
| 24 | private static final Pattern ClassPattern; | ||
| 25 | private static final List<String> ReservedWords = Arrays.asList( | ||
| 26 | "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", | ||
| 27 | "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", | ||
| 28 | "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", | ||
| 29 | "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", | ||
| 30 | "long", "strictfp", "volatile", "const", "float", "native", "super", "while" | ||
| 31 | ); | ||
| 32 | |||
| 33 | static { | ||
| 34 | String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*"; | ||
| 35 | IdentifierPattern = Pattern.compile(identifierRegex); | ||
| 36 | ClassPattern = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex)); | ||
| 37 | } | ||
| 38 | |||
| 39 | public static String validateClassName(String name, boolean packageRequired) { | ||
| 40 | if (name == null) { | ||
| 41 | return null; | ||
| 42 | } | ||
| 43 | if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) { | ||
| 44 | throw new IllegalNameException(name, "This doesn't look like a legal class name"); | ||
| 45 | } | ||
| 46 | if (packageRequired && ClassEntry.getPackageName(name) == null) { | ||
| 47 | throw new IllegalNameException(name, "Class must be in a package"); | ||
| 48 | } | ||
| 49 | return name; | ||
| 50 | } | ||
| 51 | |||
| 52 | public static String validateFieldName(String name) { | ||
| 53 | if (name == null) { | ||
| 54 | return null; | ||
| 55 | } | ||
| 56 | if (!IdentifierPattern.matcher(name).matches() || ReservedWords.contains(name)) { | ||
| 57 | throw new IllegalNameException(name, "This doesn't look like a legal identifier"); | ||
| 58 | } | ||
| 59 | return name; | ||
| 60 | } | ||
| 61 | |||
| 62 | public static String validateMethodName(String name) { | ||
| 63 | return validateFieldName(name); | ||
| 64 | } | ||
| 65 | |||
| 66 | public static String validateArgumentName(String name) { | ||
| 67 | return validateFieldName(name); | ||
| 68 | } | ||
| 69 | |||
| 70 | public static boolean isReserved(String name) { | ||
| 71 | return ReservedWords.contains(name); | ||
| 72 | } | ||
| 73 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/Signature.java b/src/main/java/cuchaz/enigma/mapping/Signature.java deleted file mode 100644 index 071e4af..0000000 --- a/src/main/java/cuchaz/enigma/mapping/Signature.java +++ /dev/null | |||
| @@ -1,82 +0,0 @@ | |||
| 1 | package cuchaz.enigma.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.bytecode.translators.TranslationSignatureVisitor; | ||
| 4 | import org.objectweb.asm.signature.SignatureReader; | ||
| 5 | import org.objectweb.asm.signature.SignatureVisitor; | ||
| 6 | import org.objectweb.asm.signature.SignatureWriter; | ||
| 7 | |||
| 8 | import java.util.function.Function; | ||
| 9 | import java.util.regex.Pattern; | ||
| 10 | |||
| 11 | public class Signature { | ||
| 12 | private static final Pattern OBJECT_PATTERN = Pattern.compile(".*:Ljava/lang/Object;:.*"); | ||
| 13 | |||
| 14 | private final String signature; | ||
| 15 | private final boolean isType; | ||
| 16 | |||
| 17 | private Signature(String signature, boolean isType) { | ||
| 18 | if (signature != null && OBJECT_PATTERN.matcher(signature).matches()) { | ||
| 19 | signature = signature.replaceAll(":Ljava/lang/Object;:", "::"); | ||
| 20 | } | ||
| 21 | |||
| 22 | this.signature = signature; | ||
| 23 | this.isType = isType; | ||
| 24 | } | ||
| 25 | |||
| 26 | public static Signature createTypedSignature(String signature) { | ||
| 27 | if (signature != null && !signature.isEmpty()) { | ||
| 28 | return new Signature(signature, true); | ||
| 29 | } | ||
| 30 | return new Signature(null, true); | ||
| 31 | } | ||
| 32 | |||
| 33 | public static Signature createSignature(String signature) { | ||
| 34 | if (signature != null && !signature.isEmpty()) { | ||
| 35 | return new Signature(signature, false); | ||
| 36 | } | ||
| 37 | return new Signature(null, false); | ||
| 38 | } | ||
| 39 | |||
| 40 | public String getSignature() { | ||
| 41 | return signature; | ||
| 42 | } | ||
| 43 | |||
| 44 | public boolean isType() { | ||
| 45 | return isType; | ||
| 46 | } | ||
| 47 | |||
| 48 | public Signature remap(Function<String, String> remapper) { | ||
| 49 | if (signature == null) { | ||
| 50 | return this; | ||
| 51 | } | ||
| 52 | SignatureWriter writer = new SignatureWriter(); | ||
| 53 | SignatureVisitor visitor = new TranslationSignatureVisitor(remapper, writer); | ||
| 54 | if (isType) { | ||
| 55 | new SignatureReader(signature).acceptType(visitor); | ||
| 56 | } else { | ||
| 57 | new SignatureReader(signature).accept(visitor); | ||
| 58 | } | ||
| 59 | return new Signature(writer.toString(), isType); | ||
| 60 | } | ||
| 61 | |||
| 62 | @Override | ||
| 63 | public boolean equals(Object obj) { | ||
| 64 | if (obj instanceof Signature) { | ||
| 65 | Signature other = (Signature) obj; | ||
| 66 | return (other.signature == null && signature == null || other.signature != null | ||
| 67 | && signature != null && other.signature.equals(signature)) | ||
| 68 | && other.isType == this.isType; | ||
| 69 | } | ||
| 70 | return false; | ||
| 71 | } | ||
| 72 | |||
| 73 | @Override | ||
| 74 | public int hashCode() { | ||
| 75 | return signature.hashCode() | (isType ? 1 : 0) << 16; | ||
| 76 | } | ||
| 77 | |||
| 78 | @Override | ||
| 79 | public String toString() { | ||
| 80 | return signature; | ||
| 81 | } | ||
| 82 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java b/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java deleted file mode 100644 index ddc5af4..0000000 --- a/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java +++ /dev/null | |||
| @@ -1,92 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | |||
| 16 | import java.io.IOException; | ||
| 17 | import java.io.StringReader; | ||
| 18 | import java.util.List; | ||
| 19 | |||
| 20 | public class SignatureUpdater { | ||
| 21 | |||
| 22 | public static String update(String signature, ClassNameUpdater updater) { | ||
| 23 | try { | ||
| 24 | StringBuilder buf = new StringBuilder(); | ||
| 25 | |||
| 26 | // read the signature character-by-character | ||
| 27 | StringReader reader = new StringReader(signature); | ||
| 28 | int i; | ||
| 29 | while ((i = reader.read()) != -1) { | ||
| 30 | char c = (char) i; | ||
| 31 | |||
| 32 | // does this character start a class name? | ||
| 33 | if (c == 'L') { | ||
| 34 | // update the class name and add it to the buffer | ||
| 35 | buf.append('L'); | ||
| 36 | String className = readClass(reader); | ||
| 37 | if (className == null) { | ||
| 38 | throw new IllegalArgumentException("Malformed signature: " + signature); | ||
| 39 | } | ||
| 40 | buf.append(updater.update(className)); | ||
| 41 | buf.append(';'); | ||
| 42 | } else { | ||
| 43 | // copy the character into the buffer | ||
| 44 | buf.append(c); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | return buf.toString(); | ||
| 49 | } catch (IOException ex) { | ||
| 50 | // I'm pretty sure a StringReader will never throw one of these | ||
| 51 | throw new Error(ex); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | private static String readClass(StringReader reader) throws IOException { | ||
| 56 | // read all the characters in the buffer until we hit a ';' | ||
| 57 | // remember to treat generics correctly | ||
| 58 | StringBuilder buf = new StringBuilder(); | ||
| 59 | int depth = 0; | ||
| 60 | int i; | ||
| 61 | while ((i = reader.read()) != -1) { | ||
| 62 | char c = (char) i; | ||
| 63 | |||
| 64 | if (c == '<') { | ||
| 65 | depth++; | ||
| 66 | } else if (c == '>') { | ||
| 67 | depth--; | ||
| 68 | } else if (depth == 0) { | ||
| 69 | if (c == ';') { | ||
| 70 | return buf.toString(); | ||
| 71 | } else { | ||
| 72 | buf.append(c); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | return null; | ||
| 78 | } | ||
| 79 | |||
| 80 | public static List<String> getClasses(String signature) { | ||
| 81 | final List<String> classNames = Lists.newArrayList(); | ||
| 82 | update(signature, className -> { | ||
| 83 | classNames.add(className); | ||
| 84 | return className; | ||
| 85 | }); | ||
| 86 | return classNames; | ||
| 87 | } | ||
| 88 | |||
| 89 | public interface ClassNameUpdater { | ||
| 90 | String update(String className); | ||
| 91 | } | ||
| 92 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java b/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java deleted file mode 100644 index 4bbde54..0000000 --- a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java +++ /dev/null | |||
| @@ -1,36 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | public enum TranslationDirection { | ||
| 15 | |||
| 16 | DEOBFUSCATING { | ||
| 17 | @Override | ||
| 18 | public <T> T choose(T deobfChoice, T obfChoice) { | ||
| 19 | if (deobfChoice == null) { | ||
| 20 | return obfChoice; | ||
| 21 | } | ||
| 22 | return deobfChoice; | ||
| 23 | } | ||
| 24 | }, | ||
| 25 | OBFUSCATING { | ||
| 26 | @Override | ||
| 27 | public <T> T choose(T deobfChoice, T obfChoice) { | ||
| 28 | if (obfChoice == null) { | ||
| 29 | return deobfChoice; | ||
| 30 | } | ||
| 31 | return obfChoice; | ||
| 32 | } | ||
| 33 | }; | ||
| 34 | |||
| 35 | public abstract <T> T choose(T deobfChoice, T obfChoice); | ||
| 36 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/Translator.java b/src/main/java/cuchaz/enigma/mapping/Translator.java deleted file mode 100644 index a9ff1cb..0000000 --- a/src/main/java/cuchaz/enigma/mapping/Translator.java +++ /dev/null | |||
| @@ -1,109 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import cuchaz.enigma.mapping.entry.*; | ||
| 15 | import org.objectweb.asm.Handle; | ||
| 16 | import org.objectweb.asm.Type; | ||
| 17 | |||
| 18 | public interface Translator { | ||
| 19 | ClassEntry getTranslatedClass(ClassEntry entry); | ||
| 20 | |||
| 21 | ClassDefEntry getTranslatedClassDef(ClassDefEntry entry); | ||
| 22 | |||
| 23 | FieldEntry getTranslatedField(FieldEntry entry); | ||
| 24 | |||
| 25 | FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry); | ||
| 26 | |||
| 27 | MethodEntry getTranslatedMethod(MethodEntry entry); | ||
| 28 | |||
| 29 | MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry); | ||
| 30 | |||
| 31 | LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry); | ||
| 32 | |||
| 33 | LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry); | ||
| 34 | |||
| 35 | boolean hasClassMapping(ClassEntry entry); | ||
| 36 | |||
| 37 | boolean hasFieldMapping(FieldEntry entry); | ||
| 38 | |||
| 39 | boolean hasMethodMapping(MethodEntry entry); | ||
| 40 | |||
| 41 | boolean hasLocalVariableMapping(LocalVariableEntry entry); | ||
| 42 | |||
| 43 | TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc); | ||
| 44 | |||
| 45 | MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor); | ||
| 46 | |||
| 47 | Signature getTranslatedSignature(Signature signature); | ||
| 48 | |||
| 49 | default Type getTranslatedType(Type type) { | ||
| 50 | String descString = type.getDescriptor(); | ||
| 51 | switch (type.getSort()) { | ||
| 52 | case Type.OBJECT: { | ||
| 53 | ClassEntry classEntry = new ClassEntry(type.getInternalName()); | ||
| 54 | return Type.getObjectType(getTranslatedClass(classEntry).getName()); | ||
| 55 | } | ||
| 56 | case Type.ARRAY: { | ||
| 57 | TypeDescriptor descriptor = new TypeDescriptor(descString); | ||
| 58 | return Type.getType(getTranslatedTypeDesc(descriptor).toString()); | ||
| 59 | } | ||
| 60 | case Type.METHOD: { | ||
| 61 | MethodDescriptor descriptor = new MethodDescriptor(descString); | ||
| 62 | return Type.getMethodType(getTranslatedMethodDesc(descriptor).toString()); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | return type; | ||
| 66 | } | ||
| 67 | |||
| 68 | default Handle getTranslatedHandle(Handle handle) { | ||
| 69 | MethodEntry entry = new MethodEntry(new ClassEntry(handle.getOwner()), handle.getName(), new MethodDescriptor(handle.getDesc())); | ||
| 70 | MethodEntry translatedMethod = getTranslatedMethod(entry); | ||
| 71 | ClassEntry ownerClass = translatedMethod.getOwnerClassEntry(); | ||
| 72 | return new Handle(handle.getTag(), ownerClass.getName(), translatedMethod.getName(), translatedMethod.getDesc().toString(), handle.isInterface()); | ||
| 73 | } | ||
| 74 | |||
| 75 | default Object getTranslatedValue(Object value) { | ||
| 76 | if (value instanceof Type) { | ||
| 77 | return this.getTranslatedType((Type) value); | ||
| 78 | } else if (value instanceof Handle) { | ||
| 79 | return getTranslatedHandle((Handle) value); | ||
| 80 | } | ||
| 81 | return value; | ||
| 82 | } | ||
| 83 | |||
| 84 | @SuppressWarnings("unchecked") | ||
| 85 | default <T extends Entry> T getTranslatedEntry(T entry) { | ||
| 86 | if (entry instanceof ClassDefEntry) { | ||
| 87 | return (T) getTranslatedClassDef((ClassDefEntry) entry); | ||
| 88 | } else if (entry instanceof ClassEntry) { | ||
| 89 | return (T) getTranslatedClass((ClassEntry) entry); | ||
| 90 | } else if (entry instanceof FieldDefEntry) { | ||
| 91 | return (T) getTranslatedFieldDef((FieldDefEntry) entry); | ||
| 92 | } else if (entry instanceof MethodDefEntry) { | ||
| 93 | return (T) getTranslatedMethodDef((MethodDefEntry) entry); | ||
| 94 | } else if (entry instanceof FieldEntry) { | ||
| 95 | return (T) getTranslatedField((FieldEntry) entry); | ||
| 96 | } else if (entry instanceof MethodEntry) { | ||
| 97 | return (T) getTranslatedMethod((MethodEntry) entry); | ||
| 98 | } else if (entry instanceof LocalVariableDefEntry) { | ||
| 99 | return (T) getTranslatedVariableDef((LocalVariableDefEntry) entry); | ||
| 100 | } else if (entry instanceof LocalVariableEntry) { | ||
| 101 | return (T) getTranslatedVariable((LocalVariableEntry) entry); | ||
| 102 | } else if (entry instanceof TypeDescriptor) { | ||
| 103 | return (T) getTranslatedTypeDesc((TypeDescriptor) entry); | ||
| 104 | } else if (entry instanceof MethodDescriptor) { | ||
| 105 | return (T) getTranslatedMethodDesc((MethodDescriptor) entry); | ||
| 106 | } | ||
| 107 | throw new IllegalArgumentException("Cannot translate unknown entry type"); | ||
| 108 | } | ||
| 109 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java b/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java deleted file mode 100644 index 6e58aa0..0000000 --- a/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java +++ /dev/null | |||
| @@ -1,258 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import com.google.common.collect.Maps; | ||
| 16 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 17 | |||
| 18 | import java.util.Map; | ||
| 19 | import java.util.function.Function; | ||
| 20 | |||
| 21 | public class TypeDescriptor { | ||
| 22 | |||
| 23 | protected final String desc; | ||
| 24 | |||
| 25 | public TypeDescriptor(String desc) { | ||
| 26 | Preconditions.checkNotNull(desc, "Desc cannot be null"); | ||
| 27 | |||
| 28 | // don't deal with generics | ||
| 29 | // this is just for raw jvm types | ||
| 30 | if (desc.charAt(0) == 'T' || desc.indexOf('<') >= 0 || desc.indexOf('>') >= 0) { | ||
| 31 | throw new IllegalArgumentException("don't use with generic types or templates: " + desc); | ||
| 32 | } | ||
| 33 | |||
| 34 | this.desc = desc; | ||
| 35 | } | ||
| 36 | |||
| 37 | public static String parseFirst(String in) { | ||
| 38 | |||
| 39 | if (in == null || in.length() <= 0) { | ||
| 40 | throw new IllegalArgumentException("No desc to parse, input is empty!"); | ||
| 41 | } | ||
| 42 | |||
| 43 | // read one desc from the input | ||
| 44 | |||
| 45 | char c = in.charAt(0); | ||
| 46 | |||
| 47 | // first check for void | ||
| 48 | if (c == 'V') { | ||
| 49 | return "V"; | ||
| 50 | } | ||
| 51 | |||
| 52 | // then check for primitives | ||
| 53 | Primitive primitive = Primitive.get(c); | ||
| 54 | if (primitive != null) { | ||
| 55 | return in.substring(0, 1); | ||
| 56 | } | ||
| 57 | |||
| 58 | // then check for classes | ||
| 59 | if (c == 'L') { | ||
| 60 | return readClass(in); | ||
| 61 | } | ||
| 62 | |||
| 63 | // then check for templates | ||
| 64 | if (c == 'T') { | ||
| 65 | return readClass(in); | ||
| 66 | } | ||
| 67 | |||
| 68 | // then check for arrays | ||
| 69 | int dim = countArrayDimension(in); | ||
| 70 | if (dim > 0) { | ||
| 71 | String arrayType = TypeDescriptor.parseFirst(in.substring(dim)); | ||
| 72 | return in.substring(0, dim + arrayType.length()); | ||
| 73 | } | ||
| 74 | |||
| 75 | throw new IllegalArgumentException("don't know how to parse: " + in); | ||
| 76 | } | ||
| 77 | |||
| 78 | private static int countArrayDimension(String in) { | ||
| 79 | int i = 0; | ||
| 80 | while (i < in.length() && in.charAt(i) == '[') | ||
| 81 | i++; | ||
| 82 | return i; | ||
| 83 | } | ||
| 84 | |||
| 85 | private static String readClass(String in) { | ||
| 86 | // read all the characters in the buffer until we hit a ';' | ||
| 87 | // include the parameters too | ||
| 88 | StringBuilder buf = new StringBuilder(); | ||
| 89 | int depth = 0; | ||
| 90 | for (int i = 0; i < in.length(); i++) { | ||
| 91 | char c = in.charAt(i); | ||
| 92 | buf.append(c); | ||
| 93 | |||
| 94 | if (c == '<') { | ||
| 95 | depth++; | ||
| 96 | } else if (c == '>') { | ||
| 97 | depth--; | ||
| 98 | } else if (depth == 0 && c == ';') { | ||
| 99 | return buf.toString(); | ||
| 100 | } | ||
| 101 | } | ||
| 102 | return null; | ||
| 103 | } | ||
| 104 | |||
| 105 | public static TypeDescriptor of(String name) { | ||
| 106 | return new TypeDescriptor("L" + name + ";"); | ||
| 107 | } | ||
| 108 | |||
| 109 | @Override | ||
| 110 | public String toString() { | ||
| 111 | return this.desc; | ||
| 112 | } | ||
| 113 | |||
| 114 | public boolean isVoid() { | ||
| 115 | return this.desc.length() == 1 && this.desc.charAt(0) == 'V'; | ||
| 116 | } | ||
| 117 | |||
| 118 | public boolean isPrimitive() { | ||
| 119 | return this.desc.length() == 1 && Primitive.get(this.desc.charAt(0)) != null; | ||
| 120 | } | ||
| 121 | |||
| 122 | public Primitive getPrimitive() { | ||
| 123 | if (!isPrimitive()) { | ||
| 124 | throw new IllegalStateException("not a primitive"); | ||
| 125 | } | ||
| 126 | return Primitive.get(this.desc.charAt(0)); | ||
| 127 | } | ||
| 128 | |||
| 129 | public boolean isType() { | ||
| 130 | return this.desc.charAt(0) == 'L' && this.desc.charAt(this.desc.length() - 1) == ';'; | ||
| 131 | } | ||
| 132 | |||
| 133 | public ClassEntry getTypeEntry() { | ||
| 134 | if (isType()) { | ||
| 135 | String name = this.desc.substring(1, this.desc.length() - 1); | ||
| 136 | |||
| 137 | int pos = name.indexOf('<'); | ||
| 138 | if (pos >= 0) { | ||
| 139 | // remove the parameters from the class name | ||
| 140 | name = name.substring(0, pos); | ||
| 141 | } | ||
| 142 | |||
| 143 | return new ClassEntry(name); | ||
| 144 | |||
| 145 | } else if (isArray() && getArrayType().isType()) { | ||
| 146 | return getArrayType().getTypeEntry(); | ||
| 147 | } else { | ||
| 148 | throw new IllegalStateException("desc doesn't have a class"); | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | public boolean isArray() { | ||
| 153 | return this.desc.charAt(0) == '['; | ||
| 154 | } | ||
| 155 | |||
| 156 | public int getArrayDimension() { | ||
| 157 | if (!isArray()) { | ||
| 158 | throw new IllegalStateException("not an array"); | ||
| 159 | } | ||
| 160 | return countArrayDimension(this.desc); | ||
| 161 | } | ||
| 162 | |||
| 163 | public TypeDescriptor getArrayType() { | ||
| 164 | if (!isArray()) { | ||
| 165 | throw new IllegalStateException("not an array"); | ||
| 166 | } | ||
| 167 | return new TypeDescriptor(this.desc.substring(getArrayDimension(), this.desc.length())); | ||
| 168 | } | ||
| 169 | |||
| 170 | public boolean containsType() { | ||
| 171 | return isType() || (isArray() && getArrayType().containsType()); | ||
| 172 | } | ||
| 173 | |||
| 174 | @Override | ||
| 175 | public boolean equals(Object other) { | ||
| 176 | return other instanceof TypeDescriptor && equals((TypeDescriptor) other); | ||
| 177 | } | ||
| 178 | |||
| 179 | public boolean equals(TypeDescriptor other) { | ||
| 180 | return this.desc.equals(other.desc); | ||
| 181 | } | ||
| 182 | |||
| 183 | @Override | ||
| 184 | public int hashCode() { | ||
| 185 | return this.desc.hashCode(); | ||
| 186 | } | ||
| 187 | |||
| 188 | public TypeDescriptor remap(Function<String, String> remapper) { | ||
| 189 | String desc = this.desc; | ||
| 190 | if (isType() || (isArray() && containsType())) { | ||
| 191 | String replacedName = remapper.apply(this.getTypeEntry().getName()); | ||
| 192 | if (replacedName != null) { | ||
| 193 | if (this.isType()) { | ||
| 194 | desc = "L" + replacedName + ";"; | ||
| 195 | } else { | ||
| 196 | desc = getArrayPrefix(this.getArrayDimension()) + "L" + replacedName + ";"; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | } | ||
| 200 | return new TypeDescriptor(desc); | ||
| 201 | } | ||
| 202 | |||
| 203 | private static String getArrayPrefix(int dimension) { | ||
| 204 | StringBuilder buf = new StringBuilder(); | ||
| 205 | for (int i = 0; i < dimension; i++) { | ||
| 206 | buf.append("["); | ||
| 207 | } | ||
| 208 | return buf.toString(); | ||
| 209 | } | ||
| 210 | |||
| 211 | public int getSize() { | ||
| 212 | switch (desc.charAt(0)) { | ||
| 213 | case 'J': | ||
| 214 | case 'D': | ||
| 215 | if (desc.length() == 1) { | ||
| 216 | return 2; | ||
| 217 | } else { | ||
| 218 | return 1; | ||
| 219 | } | ||
| 220 | default: | ||
| 221 | return 1; | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | public enum Primitive { | ||
| 226 | BYTE('B'), | ||
| 227 | CHARACTER('C'), | ||
| 228 | SHORT('S'), | ||
| 229 | INTEGER('I'), | ||
| 230 | LONG('J'), | ||
| 231 | FLOAT('F'), | ||
| 232 | DOUBLE('D'), | ||
| 233 | BOOLEAN('Z'); | ||
| 234 | |||
| 235 | private static final Map<Character, Primitive> lookup; | ||
| 236 | |||
| 237 | static { | ||
| 238 | lookup = Maps.newTreeMap(); | ||
| 239 | for (Primitive val : values()) { | ||
| 240 | lookup.put(val.getCode(), val); | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | private char code; | ||
| 245 | |||
| 246 | Primitive(char code) { | ||
| 247 | this.code = code; | ||
| 248 | } | ||
| 249 | |||
| 250 | public static Primitive get(char code) { | ||
| 251 | return lookup.get(code); | ||
| 252 | } | ||
| 253 | |||
| 254 | public char getCode() { | ||
| 255 | return this.code; | ||
| 256 | } | ||
| 257 | } | ||
| 258 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java deleted file mode 100644 index df72e7e..0000000 --- a/src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java +++ /dev/null | |||
| @@ -1,38 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 16 | import cuchaz.enigma.mapping.Signature; | ||
| 17 | |||
| 18 | public class ClassDefEntry extends ClassEntry implements DefEntry { | ||
| 19 | private final AccessFlags access; | ||
| 20 | private final Signature signature; | ||
| 21 | |||
| 22 | public ClassDefEntry(String className, Signature signature, AccessFlags access) { | ||
| 23 | super(className); | ||
| 24 | Preconditions.checkNotNull(signature, "Class signature cannot be null"); | ||
| 25 | Preconditions.checkNotNull(access, "Class access cannot be null"); | ||
| 26 | this.signature = signature; | ||
| 27 | this.access = access; | ||
| 28 | } | ||
| 29 | |||
| 30 | public Signature getSignature() { | ||
| 31 | return signature; | ||
| 32 | } | ||
| 33 | |||
| 34 | @Override | ||
| 35 | public AccessFlags getAccess() { | ||
| 36 | return access; | ||
| 37 | } | ||
| 38 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java deleted file mode 100644 index c795825..0000000 --- a/src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java +++ /dev/null | |||
| @@ -1,175 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import com.google.common.collect.Lists; | ||
| 16 | |||
| 17 | import java.util.List; | ||
| 18 | |||
| 19 | public class ClassEntry implements Entry { | ||
| 20 | |||
| 21 | private final String name; | ||
| 22 | |||
| 23 | public ClassEntry(String className) { | ||
| 24 | Preconditions.checkNotNull(className, "Class name cannot be null"); | ||
| 25 | |||
| 26 | if (className.indexOf('.') >= 0) { | ||
| 27 | throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className); | ||
| 28 | } | ||
| 29 | |||
| 30 | this.name = className; | ||
| 31 | |||
| 32 | if (isInnerClass() && getInnermostClassName().indexOf('/') >= 0) { | ||
| 33 | throw new IllegalArgumentException("Inner class must not have a package: " + className); | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | public ClassEntry(ClassEntry other) { | ||
| 38 | this.name = other.name; | ||
| 39 | } | ||
| 40 | |||
| 41 | @Override | ||
| 42 | public String getName() { | ||
| 43 | return this.name; | ||
| 44 | } | ||
| 45 | |||
| 46 | @Override | ||
| 47 | public String getClassName() { | ||
| 48 | return this.name; | ||
| 49 | } | ||
| 50 | |||
| 51 | @Override | ||
| 52 | public ClassEntry getOwnerClassEntry() { | ||
| 53 | return this; | ||
| 54 | } | ||
| 55 | |||
| 56 | @Override | ||
| 57 | public ClassEntry updateOwnership(ClassEntry classEntry) { | ||
| 58 | return classEntry; | ||
| 59 | } | ||
| 60 | |||
| 61 | @Override | ||
| 62 | public int hashCode() { | ||
| 63 | return this.name.hashCode(); | ||
| 64 | } | ||
| 65 | |||
| 66 | @Override | ||
| 67 | public boolean equals(Object other) { | ||
| 68 | return other instanceof ClassEntry && equals((ClassEntry) other); | ||
| 69 | } | ||
| 70 | |||
| 71 | public boolean equals(ClassEntry other) { | ||
| 72 | return other != null && this.name.equals(other.name); | ||
| 73 | } | ||
| 74 | |||
| 75 | @Override | ||
| 76 | public String toString() { | ||
| 77 | return this.name; | ||
| 78 | } | ||
| 79 | |||
| 80 | public boolean isArray() { | ||
| 81 | return this.name.lastIndexOf('[') >= 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | public boolean isInnerClass() { | ||
| 85 | return this.name.lastIndexOf('$') >= 0; | ||
| 86 | } | ||
| 87 | |||
| 88 | public List<String> getClassChainNames() { | ||
| 89 | return Lists.newArrayList(this.name.split("\\$")); | ||
| 90 | } | ||
| 91 | |||
| 92 | public List<ClassEntry> getClassChain() { | ||
| 93 | List<ClassEntry> entries = Lists.newArrayList(); | ||
| 94 | StringBuilder buf = new StringBuilder(); | ||
| 95 | for (String name : getClassChainNames()) { | ||
| 96 | if (buf.length() > 0) { | ||
| 97 | buf.append("$"); | ||
| 98 | } | ||
| 99 | buf.append(name); | ||
| 100 | entries.add(new ClassEntry(buf.toString())); | ||
| 101 | } | ||
| 102 | return entries; | ||
| 103 | } | ||
| 104 | |||
| 105 | public String getOutermostClassName() { | ||
| 106 | if (isInnerClass()) { | ||
| 107 | return this.name.substring(0, this.name.indexOf('$')); | ||
| 108 | } | ||
| 109 | return this.name; | ||
| 110 | } | ||
| 111 | |||
| 112 | public ClassEntry getOutermostClassEntry() { | ||
| 113 | return new ClassEntry(getOutermostClassName()); | ||
| 114 | } | ||
| 115 | |||
| 116 | public String getOuterClassName() { | ||
| 117 | if (!isInnerClass()) { | ||
| 118 | throw new Error("This is not an inner class!"); | ||
| 119 | } | ||
| 120 | return this.name.substring(0, this.name.lastIndexOf('$')); | ||
| 121 | } | ||
| 122 | |||
| 123 | public ClassEntry getOuterClassEntry() { | ||
| 124 | return new ClassEntry(getOuterClassName()); | ||
| 125 | } | ||
| 126 | |||
| 127 | public String getInnermostClassName() { | ||
| 128 | if (!isInnerClass()) { | ||
| 129 | throw new Error("This is not an inner class!"); | ||
| 130 | } | ||
| 131 | return this.name.substring(this.name.lastIndexOf('$') + 1); | ||
| 132 | } | ||
| 133 | |||
| 134 | public boolean isInDefaultPackage() { | ||
| 135 | return this.name.indexOf('/') < 0; | ||
| 136 | } | ||
| 137 | |||
| 138 | public String getPackageName() { | ||
| 139 | return getPackageName(this.name); | ||
| 140 | } | ||
| 141 | |||
| 142 | public String getSimpleName() { | ||
| 143 | int pos = this.name.lastIndexOf('/'); | ||
| 144 | if (pos > 0) { | ||
| 145 | return this.name.substring(pos + 1); | ||
| 146 | } | ||
| 147 | return this.name; | ||
| 148 | } | ||
| 149 | |||
| 150 | public static String getPackageName(String name) { | ||
| 151 | int pos = name.lastIndexOf('/'); | ||
| 152 | if (pos > 0) { | ||
| 153 | return name.substring(0, pos); | ||
| 154 | } | ||
| 155 | return null; | ||
| 156 | } | ||
| 157 | |||
| 158 | public ClassEntry buildClassEntry(List<ClassEntry> classChain) { | ||
| 159 | assert (classChain.contains(this)); | ||
| 160 | StringBuilder buf = new StringBuilder(); | ||
| 161 | for (ClassEntry chainEntry : classChain) { | ||
| 162 | if (buf.length() == 0) { | ||
| 163 | buf.append(chainEntry.getName()); | ||
| 164 | } else { | ||
| 165 | buf.append("$"); | ||
| 166 | buf.append(chainEntry.isInnerClass() ? chainEntry.getInnermostClassName() : chainEntry.getSimpleName()); | ||
| 167 | } | ||
| 168 | |||
| 169 | if (chainEntry == this) { | ||
| 170 | break; | ||
| 171 | } | ||
| 172 | } | ||
| 173 | return new ClassEntry(buf.toString()); | ||
| 174 | } | ||
| 175 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/DefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/DefEntry.java deleted file mode 100644 index 43ad027..0000000 --- a/src/main/java/cuchaz/enigma/mapping/entry/DefEntry.java +++ /dev/null | |||
| @@ -1,7 +0,0 @@ | |||
| 1 | package cuchaz.enigma.mapping.entry; | ||
| 2 | |||
| 3 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 4 | |||
| 5 | public interface DefEntry extends Entry { | ||
| 6 | AccessFlags getAccess(); | ||
| 7 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/Entry.java b/src/main/java/cuchaz/enigma/mapping/entry/Entry.java deleted file mode 100644 index b612140..0000000 --- a/src/main/java/cuchaz/enigma/mapping/entry/Entry.java +++ /dev/null | |||
| @@ -1,22 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | public interface Entry { | ||
| 15 | String getName(); | ||
| 16 | |||
| 17 | String getClassName(); | ||
| 18 | |||
| 19 | ClassEntry getOwnerClassEntry(); | ||
| 20 | |||
| 21 | Entry updateOwnership(ClassEntry classEntry); | ||
| 22 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java b/src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java deleted file mode 100644 index 5bd159f..0000000 --- a/src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java +++ /dev/null | |||
| @@ -1,49 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | import cuchaz.enigma.analysis.JarIndex; | ||
| 15 | import cuchaz.enigma.mapping.ClassMapping; | ||
| 16 | import cuchaz.enigma.mapping.FieldMapping; | ||
| 17 | import cuchaz.enigma.mapping.MethodDescriptor; | ||
| 18 | import cuchaz.enigma.mapping.MethodMapping; | ||
| 19 | |||
| 20 | public class EntryFactory { | ||
| 21 | public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { | ||
| 22 | ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName()); | ||
| 23 | return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry)); | ||
| 24 | } | ||
| 25 | |||
| 26 | private static ClassEntry getObfClassEntry(ClassMapping classMapping) { | ||
| 27 | return new ClassEntry(classMapping.getObfFullName()); | ||
| 28 | } | ||
| 29 | |||
| 30 | public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { | ||
| 31 | return new ClassEntry(classMapping.getDeobfName()); | ||
| 32 | } | ||
| 33 | |||
| 34 | public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { | ||
| 35 | return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfDesc()); | ||
| 36 | } | ||
| 37 | |||
| 38 | public static MethodEntry getMethodEntry(ClassEntry classEntry, String name, MethodDescriptor desc) { | ||
| 39 | return new MethodEntry(classEntry, name, desc); | ||
| 40 | } | ||
| 41 | |||
| 42 | public static MethodEntry getObfMethodEntry(ClassEntry classEntry, MethodMapping methodMapping) { | ||
| 43 | return getMethodEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfDesc()); | ||
| 44 | } | ||
| 45 | |||
| 46 | public static MethodEntry getObfMethodEntry(ClassMapping classMapping, MethodMapping methodMapping) { | ||
| 47 | return getObfMethodEntry(getObfClassEntry(classMapping), methodMapping); | ||
| 48 | } | ||
| 49 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java deleted file mode 100644 index 223410f..0000000 --- a/src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java +++ /dev/null | |||
| @@ -1,44 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 16 | import cuchaz.enigma.mapping.Signature; | ||
| 17 | import cuchaz.enigma.mapping.TypeDescriptor; | ||
| 18 | |||
| 19 | public class FieldDefEntry extends FieldEntry implements DefEntry { | ||
| 20 | private final AccessFlags access; | ||
| 21 | private final Signature signature; | ||
| 22 | |||
| 23 | public FieldDefEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc, Signature signature, AccessFlags access) { | ||
| 24 | super(ownerEntry, name, desc); | ||
| 25 | Preconditions.checkNotNull(access, "Field access cannot be null"); | ||
| 26 | Preconditions.checkNotNull(signature, "Field signature cannot be null"); | ||
| 27 | this.access = access; | ||
| 28 | this.signature = signature; | ||
| 29 | } | ||
| 30 | |||
| 31 | @Override | ||
| 32 | public AccessFlags getAccess() { | ||
| 33 | return access; | ||
| 34 | } | ||
| 35 | |||
| 36 | public Signature getSignature() { | ||
| 37 | return signature; | ||
| 38 | } | ||
| 39 | |||
| 40 | @Override | ||
| 41 | public FieldDefEntry updateOwnership(ClassEntry owner) { | ||
| 42 | return new FieldDefEntry(owner, this.name, this.desc, signature, access); | ||
| 43 | } | ||
| 44 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java deleted file mode 100644 index b6e1554..0000000 --- a/src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java +++ /dev/null | |||
| @@ -1,77 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.mapping.TypeDescriptor; | ||
| 16 | import cuchaz.enigma.utils.Utils; | ||
| 17 | |||
| 18 | public class FieldEntry implements Entry { | ||
| 19 | |||
| 20 | protected final ClassEntry ownerEntry; | ||
| 21 | protected final String name; | ||
| 22 | protected final TypeDescriptor desc; | ||
| 23 | |||
| 24 | // NOTE: this argument order is important for the MethodReader/MethodWriter | ||
| 25 | public FieldEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc) { | ||
| 26 | Preconditions.checkNotNull(ownerEntry, "Owner cannot be null"); | ||
| 27 | Preconditions.checkNotNull(name, "Field name cannot be null"); | ||
| 28 | Preconditions.checkNotNull(desc, "Field descriptor cannot be null"); | ||
| 29 | |||
| 30 | this.ownerEntry = ownerEntry; | ||
| 31 | this.name = name; | ||
| 32 | this.desc = desc; | ||
| 33 | } | ||
| 34 | |||
| 35 | @Override | ||
| 36 | public ClassEntry getOwnerClassEntry() { | ||
| 37 | return this.ownerEntry; | ||
| 38 | } | ||
| 39 | |||
| 40 | @Override | ||
| 41 | public String getName() { | ||
| 42 | return this.name; | ||
| 43 | } | ||
| 44 | |||
| 45 | @Override | ||
| 46 | public String getClassName() { | ||
| 47 | return this.ownerEntry.getName(); | ||
| 48 | } | ||
| 49 | |||
| 50 | public TypeDescriptor getDesc() { | ||
| 51 | return this.desc; | ||
| 52 | } | ||
| 53 | |||
| 54 | @Override | ||
| 55 | public FieldEntry updateOwnership(ClassEntry owner) { | ||
| 56 | return new FieldEntry(owner, this.name, this.desc); | ||
| 57 | } | ||
| 58 | |||
| 59 | @Override | ||
| 60 | public int hashCode() { | ||
| 61 | return Utils.combineHashesOrdered(this.ownerEntry, this.name, this.desc); | ||
| 62 | } | ||
| 63 | |||
| 64 | @Override | ||
| 65 | public boolean equals(Object other) { | ||
| 66 | return other instanceof FieldEntry && equals((FieldEntry) other); | ||
| 67 | } | ||
| 68 | |||
| 69 | public boolean equals(FieldEntry other) { | ||
| 70 | return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.desc.equals(other.desc); | ||
| 71 | } | ||
| 72 | |||
| 73 | @Override | ||
| 74 | public String toString() { | ||
| 75 | return this.ownerEntry.getName() + "." + this.name + ":" + this.desc; | ||
| 76 | } | ||
| 77 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java deleted file mode 100644 index d186664..0000000 --- a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java +++ /dev/null | |||
| @@ -1,61 +0,0 @@ | |||
| 1 | package cuchaz.enigma.mapping.entry; | ||
| 2 | |||
| 3 | import com.google.common.base.Preconditions; | ||
| 4 | import cuchaz.enigma.mapping.TypeDescriptor; | ||
| 5 | import cuchaz.enigma.utils.Utils; | ||
| 6 | |||
| 7 | /** | ||
| 8 | * TypeDescriptor... | ||
| 9 | * Created by Thog | ||
| 10 | * 19/10/2016 | ||
| 11 | */ | ||
| 12 | public class LocalVariableDefEntry extends LocalVariableEntry { | ||
| 13 | |||
| 14 | protected final MethodDefEntry ownerEntry; | ||
| 15 | protected final TypeDescriptor desc; | ||
| 16 | |||
| 17 | public LocalVariableDefEntry(MethodDefEntry ownerEntry, int index, String name, TypeDescriptor desc) { | ||
| 18 | this(ownerEntry, index, name, true, desc); | ||
| 19 | } | ||
| 20 | |||
| 21 | public LocalVariableDefEntry(MethodDefEntry ownerEntry, int index, String name, boolean parameter, TypeDescriptor desc) { | ||
| 22 | super(ownerEntry, index, name, parameter); | ||
| 23 | Preconditions.checkNotNull(desc, "Variable desc cannot be null"); | ||
| 24 | |||
| 25 | this.ownerEntry = ownerEntry; | ||
| 26 | this.desc = desc; | ||
| 27 | } | ||
| 28 | |||
| 29 | @Override | ||
| 30 | public MethodDefEntry getOwnerEntry() { | ||
| 31 | return this.ownerEntry; | ||
| 32 | } | ||
| 33 | |||
| 34 | public TypeDescriptor getDesc() { | ||
| 35 | return desc; | ||
| 36 | } | ||
| 37 | |||
| 38 | @Override | ||
| 39 | public LocalVariableDefEntry updateOwnership(ClassEntry classEntry) { | ||
| 40 | return new LocalVariableDefEntry(ownerEntry.updateOwnership(classEntry), index, name, parameter, desc); | ||
| 41 | } | ||
| 42 | |||
| 43 | @Override | ||
| 44 | public int hashCode() { | ||
| 45 | return Utils.combineHashesOrdered(this.ownerEntry, this.desc.hashCode(), this.name.hashCode(), Integer.hashCode(this.index)); | ||
| 46 | } | ||
| 47 | |||
| 48 | @Override | ||
| 49 | public boolean equals(Object other) { | ||
| 50 | return other instanceof LocalVariableDefEntry && equals((LocalVariableDefEntry) other); | ||
| 51 | } | ||
| 52 | |||
| 53 | public boolean equals(LocalVariableDefEntry other) { | ||
| 54 | return this.ownerEntry.equals(other.ownerEntry) && this.desc.equals(other.desc) && this.name.equals(other.name) && this.index == other.index; | ||
| 55 | } | ||
| 56 | |||
| 57 | @Override | ||
| 58 | public String toString() { | ||
| 59 | return this.ownerEntry + "(" + this.index + ":" + this.name + ":" + this.desc + ")"; | ||
| 60 | } | ||
| 61 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java deleted file mode 100644 index 3507b25..0000000 --- a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java +++ /dev/null | |||
| @@ -1,93 +0,0 @@ | |||
| 1 | package cuchaz.enigma.mapping.entry; | ||
| 2 | |||
| 3 | import com.google.common.base.Preconditions; | ||
| 4 | import cuchaz.enigma.mapping.MethodDescriptor; | ||
| 5 | import cuchaz.enigma.utils.Utils; | ||
| 6 | |||
| 7 | /** | ||
| 8 | * TypeDescriptor... | ||
| 9 | * Created by Thog | ||
| 10 | * 19/10/2016 | ||
| 11 | */ | ||
| 12 | public class LocalVariableEntry implements Entry { | ||
| 13 | |||
| 14 | protected final MethodEntry ownerEntry; | ||
| 15 | protected final String name; | ||
| 16 | protected final int index; | ||
| 17 | protected final boolean parameter; | ||
| 18 | |||
| 19 | @Deprecated | ||
| 20 | public LocalVariableEntry(MethodEntry ownerEntry, int index, String name) { | ||
| 21 | this(ownerEntry, index, name, true); | ||
| 22 | } | ||
| 23 | |||
| 24 | public LocalVariableEntry(MethodEntry ownerEntry, int index, String name, boolean parameter) { | ||
| 25 | Preconditions.checkNotNull(ownerEntry, "Variable owner cannot be null"); | ||
| 26 | Preconditions.checkNotNull(name, "Variable name cannot be null"); | ||
| 27 | Preconditions.checkArgument(index >= 0, "Index must be positive"); | ||
| 28 | |||
| 29 | this.ownerEntry = ownerEntry; | ||
| 30 | this.name = name; | ||
| 31 | this.index = index; | ||
| 32 | this.parameter = parameter; | ||
| 33 | } | ||
| 34 | |||
| 35 | public boolean isParameter() { | ||
| 36 | return this.parameter; | ||
| 37 | } | ||
| 38 | |||
| 39 | public MethodEntry getOwnerEntry() { | ||
| 40 | return this.ownerEntry; | ||
| 41 | } | ||
| 42 | |||
| 43 | public int getIndex() { | ||
| 44 | return index; | ||
| 45 | } | ||
| 46 | |||
| 47 | @Override | ||
| 48 | public String getName() { | ||
| 49 | return this.name; | ||
| 50 | } | ||
| 51 | |||
| 52 | @Override | ||
| 53 | public ClassEntry getOwnerClassEntry() { | ||
| 54 | return this.ownerEntry.getOwnerClassEntry(); | ||
| 55 | } | ||
| 56 | |||
| 57 | @Override | ||
| 58 | public String getClassName() { | ||
| 59 | return this.ownerEntry.getClassName(); | ||
| 60 | } | ||
| 61 | |||
| 62 | @Override | ||
| 63 | public LocalVariableEntry updateOwnership(ClassEntry classEntry) { | ||
| 64 | return new LocalVariableEntry(ownerEntry.updateOwnership(classEntry), index, name, parameter); | ||
| 65 | } | ||
| 66 | |||
| 67 | public String getMethodName() { | ||
| 68 | return this.ownerEntry.getName(); | ||
| 69 | } | ||
| 70 | |||
| 71 | public MethodDescriptor getMethodDesc() { | ||
| 72 | return this.ownerEntry.getDesc(); | ||
| 73 | } | ||
| 74 | |||
| 75 | @Override | ||
| 76 | public int hashCode() { | ||
| 77 | return Utils.combineHashesOrdered(this.ownerEntry, this.name.hashCode(), Integer.hashCode(this.index)); | ||
| 78 | } | ||
| 79 | |||
| 80 | @Override | ||
| 81 | public boolean equals(Object other) { | ||
| 82 | return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other); | ||
| 83 | } | ||
| 84 | |||
| 85 | public boolean equals(LocalVariableEntry other) { | ||
| 86 | return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.index == other.index; | ||
| 87 | } | ||
| 88 | |||
| 89 | @Override | ||
| 90 | public String toString() { | ||
| 91 | return this.ownerEntry + "(" + this.index + ":" + this.name + ")"; | ||
| 92 | } | ||
| 93 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java deleted file mode 100644 index fa9e668..0000000 --- a/src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java +++ /dev/null | |||
| @@ -1,61 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 16 | import cuchaz.enigma.mapping.MethodDescriptor; | ||
| 17 | import cuchaz.enigma.mapping.Signature; | ||
| 18 | |||
| 19 | public class MethodDefEntry extends MethodEntry implements DefEntry { | ||
| 20 | |||
| 21 | private final AccessFlags access; | ||
| 22 | private final Signature signature; | ||
| 23 | |||
| 24 | public MethodDefEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor, Signature signature, AccessFlags access) { | ||
| 25 | super(classEntry, name, descriptor); | ||
| 26 | Preconditions.checkNotNull(access, "Method access cannot be null"); | ||
| 27 | Preconditions.checkNotNull(signature, "Method signature cannot be null"); | ||
| 28 | this.access = access; | ||
| 29 | this.signature = signature; | ||
| 30 | } | ||
| 31 | |||
| 32 | @Override | ||
| 33 | public AccessFlags getAccess() { | ||
| 34 | return access; | ||
| 35 | } | ||
| 36 | |||
| 37 | public Signature getSignature() { | ||
| 38 | return signature; | ||
| 39 | } | ||
| 40 | |||
| 41 | @Override | ||
| 42 | public MethodDefEntry updateOwnership(ClassEntry classEntry) { | ||
| 43 | return new MethodDefEntry(new ClassEntry(classEntry.getName()), name, descriptor, signature, access); | ||
| 44 | } | ||
| 45 | |||
| 46 | public int getArgumentIndex(ClassDefEntry ownerEntry, int localVariableIndex) { | ||
| 47 | int argumentIndex = localVariableIndex; | ||
| 48 | |||
| 49 | // Enum constructors have an implicit "name" and "ordinal" parameter as well as "this" | ||
| 50 | if (ownerEntry.getAccess().isEnum() && getName().startsWith("<")) { | ||
| 51 | argumentIndex -= 2; | ||
| 52 | } | ||
| 53 | |||
| 54 | // If we're not static, "this" is bound to index 0 | ||
| 55 | if (!getAccess().isStatic()) { | ||
| 56 | argumentIndex -= 1; | ||
| 57 | } | ||
| 58 | |||
| 59 | return argumentIndex; | ||
| 60 | } | ||
| 61 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java deleted file mode 100644 index 1abc5b1..0000000 --- a/src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java +++ /dev/null | |||
| @@ -1,80 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.mapping.MethodDescriptor; | ||
| 16 | import cuchaz.enigma.utils.Utils; | ||
| 17 | |||
| 18 | public class MethodEntry implements Entry { | ||
| 19 | |||
| 20 | protected final ClassEntry classEntry; | ||
| 21 | protected final String name; | ||
| 22 | protected final MethodDescriptor descriptor; | ||
| 23 | |||
| 24 | public MethodEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor) { | ||
| 25 | Preconditions.checkNotNull(classEntry, "Class cannot be null"); | ||
| 26 | Preconditions.checkNotNull(name, "Method name cannot be null"); | ||
| 27 | Preconditions.checkNotNull(descriptor, "Method descriptor cannot be null"); | ||
| 28 | |||
| 29 | this.classEntry = classEntry; | ||
| 30 | this.name = name; | ||
| 31 | this.descriptor = descriptor; | ||
| 32 | } | ||
| 33 | |||
| 34 | @Override | ||
| 35 | public ClassEntry getOwnerClassEntry() { | ||
| 36 | return this.classEntry; | ||
| 37 | } | ||
| 38 | |||
| 39 | @Override | ||
| 40 | public String getName() { | ||
| 41 | return this.name; | ||
| 42 | } | ||
| 43 | |||
| 44 | public MethodDescriptor getDesc() { | ||
| 45 | return this.descriptor; | ||
| 46 | } | ||
| 47 | |||
| 48 | public boolean isConstructor() { | ||
| 49 | return name.equals("<init>") || name.equals("<clinit>"); | ||
| 50 | } | ||
| 51 | |||
| 52 | @Override | ||
| 53 | public String getClassName() { | ||
| 54 | return this.classEntry.getName(); | ||
| 55 | } | ||
| 56 | |||
| 57 | @Override | ||
| 58 | public MethodEntry updateOwnership(ClassEntry classEntry) { | ||
| 59 | return new MethodEntry(new ClassEntry(classEntry.getName()), name, descriptor); | ||
| 60 | } | ||
| 61 | |||
| 62 | @Override | ||
| 63 | public int hashCode() { | ||
| 64 | return Utils.combineHashesOrdered(this.classEntry, this.name, this.descriptor); | ||
| 65 | } | ||
| 66 | |||
| 67 | @Override | ||
| 68 | public boolean equals(Object other) { | ||
| 69 | return other instanceof MethodEntry && equals((MethodEntry) other); | ||
| 70 | } | ||
| 71 | |||
| 72 | public boolean equals(MethodEntry other) { | ||
| 73 | return this.classEntry.equals(other.getOwnerClassEntry()) && this.name.equals(other.getName()) && this.descriptor.equals(other.getDesc()); | ||
| 74 | } | ||
| 75 | |||
| 76 | @Override | ||
| 77 | public String toString() { | ||
| 78 | return this.classEntry.getName() + "." + this.name + this.descriptor; | ||
| 79 | } | ||
| 80 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java b/src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java deleted file mode 100644 index 73770c5..0000000 --- a/src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java +++ /dev/null | |||
| @@ -1,48 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | import com.strobel.assembler.metadata.FieldDefinition; | ||
| 15 | import com.strobel.assembler.metadata.MemberReference; | ||
| 16 | import com.strobel.assembler.metadata.MethodDefinition; | ||
| 17 | import cuchaz.enigma.bytecode.AccessFlags; | ||
| 18 | import cuchaz.enigma.mapping.MethodDescriptor; | ||
| 19 | import cuchaz.enigma.mapping.Signature; | ||
| 20 | import cuchaz.enigma.mapping.TypeDescriptor; | ||
| 21 | |||
| 22 | public class ProcyonEntryFactory { | ||
| 23 | private final ReferencedEntryPool entryPool; | ||
| 24 | |||
| 25 | public ProcyonEntryFactory(ReferencedEntryPool entryPool) { | ||
| 26 | this.entryPool = entryPool; | ||
| 27 | } | ||
| 28 | |||
| 29 | public FieldEntry getFieldEntry(MemberReference def) { | ||
| 30 | ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); | ||
| 31 | return entryPool.getField(classEntry, def.getName(), def.getErasedSignature()); | ||
| 32 | } | ||
| 33 | |||
| 34 | public FieldDefEntry getFieldDefEntry(FieldDefinition def) { | ||
| 35 | ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); | ||
| 36 | return new FieldDefEntry(classEntry, def.getName(), new TypeDescriptor(def.getErasedSignature()), Signature.createTypedSignature(def.getSignature()), new AccessFlags(def.getModifiers())); | ||
| 37 | } | ||
| 38 | |||
| 39 | public MethodEntry getMethodEntry(MemberReference def) { | ||
| 40 | ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); | ||
| 41 | return entryPool.getMethod(classEntry, def.getName(), def.getErasedSignature()); | ||
| 42 | } | ||
| 43 | |||
| 44 | public MethodDefEntry getMethodDefEntry(MethodDefinition def) { | ||
| 45 | ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); | ||
| 46 | return new MethodDefEntry(classEntry, def.getName(), new MethodDescriptor(def.getErasedSignature()), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers())); | ||
| 47 | } | ||
| 48 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java b/src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java deleted file mode 100644 index 12b3955..0000000 --- a/src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java +++ /dev/null | |||
| @@ -1,59 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.mapping.entry; | ||
| 13 | |||
| 14 | import cuchaz.enigma.mapping.MethodDescriptor; | ||
| 15 | import cuchaz.enigma.mapping.TypeDescriptor; | ||
| 16 | |||
| 17 | import java.util.HashMap; | ||
| 18 | import java.util.Map; | ||
| 19 | |||
| 20 | public class ReferencedEntryPool { | ||
| 21 | private final Map<String, ClassEntry> classEntries = new HashMap<>(); | ||
| 22 | private final Map<String, Map<String, MethodEntry>> methodEntries = new HashMap<>(); | ||
| 23 | private final Map<String, Map<String, FieldEntry>> fieldEntries = new HashMap<>(); | ||
| 24 | |||
| 25 | public ClassEntry getClass(String name) { | ||
| 26 | // TODO: FIXME - I'm a hack! | ||
| 27 | if ("[T".equals(name) || "[[T".equals(name) || "[[[T".equals(name)) { | ||
| 28 | name = name.replaceAll("T", "Ljava/lang/Object;"); | ||
| 29 | } | ||
| 30 | |||
| 31 | final String computeName = name; | ||
| 32 | return this.classEntries.computeIfAbsent(name, s -> new ClassEntry(computeName)); | ||
| 33 | } | ||
| 34 | |||
| 35 | public MethodEntry getMethod(ClassEntry ownerEntry, String name, String desc) { | ||
| 36 | return getMethod(ownerEntry, name, new MethodDescriptor(desc)); | ||
| 37 | } | ||
| 38 | |||
| 39 | public MethodEntry getMethod(ClassEntry ownerEntry, String name, MethodDescriptor desc) { | ||
| 40 | String key = name + desc.toString(); | ||
| 41 | return getClassMethods(ownerEntry.getName()).computeIfAbsent(key, s -> new MethodEntry(ownerEntry, name, desc)); | ||
| 42 | } | ||
| 43 | |||
| 44 | public FieldEntry getField(ClassEntry ownerEntry, String name, String desc) { | ||
| 45 | return getField(ownerEntry, name, new TypeDescriptor(desc)); | ||
| 46 | } | ||
| 47 | |||
| 48 | public FieldEntry getField(ClassEntry ownerEntry, String name, TypeDescriptor desc) { | ||
| 49 | return getClassFields(ownerEntry.getName()).computeIfAbsent(name, s -> new FieldEntry(ownerEntry, name, desc)); | ||
| 50 | } | ||
| 51 | |||
| 52 | private Map<String, MethodEntry> getClassMethods(String name) { | ||
| 53 | return methodEntries.computeIfAbsent(name, s -> new HashMap<>()); | ||
| 54 | } | ||
| 55 | |||
| 56 | private Map<String, FieldEntry> getClassFields(String name) { | ||
| 57 | return fieldEntries.computeIfAbsent(name, s -> new HashMap<>()); | ||
| 58 | } | ||
| 59 | } | ||