summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java59
-rw-r--r--enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java7
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java41
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/Token.java7
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/TokenStore.java71
5 files changed, 146 insertions, 39 deletions
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java
index 9bf5fa72..3409fc14 100644
--- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java
+++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java
@@ -69,6 +69,7 @@ public class EditorPanel {
69 private final Gui gui; 69 private final Gui gui;
70 70
71 private EntryReference<Entry<?>, Entry<?>> cursorReference; 71 private EntryReference<Entry<?>, Entry<?>> cursorReference;
72 private EntryReference<Entry<?>, Entry<?>> nextReference;
72 private boolean mouseIsPressed = false; 73 private boolean mouseIsPressed = false;
73 private boolean shouldNavigateOnClick; 74 private boolean shouldNavigateOnClick;
74 75
@@ -332,13 +333,14 @@ public class EditorPanel {
332 } else { 333 } else {
333 this.displayError(res.unwrapErr()); 334 this.displayError(res.unwrapErr());
334 } 335 }
336 this.nextReference = null;
335 }); 337 });
336 } 338 }
337 339
338 public void displayError(ClassHandleError t) { 340 public void displayError(ClassHandleError t) {
339 this.setDisplayMode(DisplayMode.ERRORED); 341 this.setDisplayMode(DisplayMode.ERRORED);
340 String str; 342 String str;
341 switch(t.type) { 343 switch (t.type) {
342 case DECOMPILE: 344 case DECOMPILE:
343 str = "editor.decompile_error"; 345 str = "editor.decompile_error";
344 break; 346 break;
@@ -404,21 +406,13 @@ public class EditorPanel {
404 } 406 }
405 407
406 public void onCaretMove(int pos, boolean fromClick) { 408 public void onCaretMove(int pos, boolean fromClick) {
409 if (this.settingSource) return;
407 if (this.controller.project == null) return; 410 if (this.controller.project == null) return;
408 411
409 EntryRemapper mapper = this.controller.project.getMapper(); 412 EntryRemapper mapper = this.controller.project.getMapper();
410 Token token = getToken(pos); 413 Token token = getToken(pos);
411 414
412 if (this.settingSource) { 415 setCursorReference(getReference(token));
413 EntryReference<Entry<?>, Entry<?>> ref = getCursorReference();
414 EntryReference<Entry<?>, Entry<?>> refAtCursor = getReference(token);
415 if (this.editor.getDocument().getLength() != 0 && ref != null && !ref.equals(refAtCursor)) {
416 showReference0(ref);
417 }
418 return;
419 } else {
420 setCursorReference(getReference(token));
421 }
422 416
423 Entry<?> referenceEntry = this.cursorReference != null ? this.cursorReference.entry : null; 417 Entry<?> referenceEntry = this.cursorReference != null ? this.cursorReference.entry : null;
424 418
@@ -484,17 +478,49 @@ public class EditorPanel {
484 if (source == null) return; 478 if (source == null) return;
485 try { 479 try {
486 this.settingSource = true; 480 this.settingSource = true;
481
482 int newCaretPos = 0;
483 if (this.source != null && this.source.getEntry().equals(source.getEntry())) {
484 int caretPos = this.editor.getCaretPosition();
485
486 if (this.source.getTokenStore().isCompatible(source.getTokenStore())) {
487 newCaretPos = this.source.getTokenStore().mapPosition(source.getTokenStore(), caretPos);
488 } else {
489 // if the class is the same but the token stores aren't
490 // compatible, then the user probably switched decompilers
491
492 // check if there's a selected reference we can navigate to,
493 // but only if there's none already queued up for being selected
494 if (this.getCursorReference() != null && this.nextReference == null) {
495 this.nextReference = this.getCursorReference();
496 }
497
498 // otherwise fall back to just using the same average
499 // position in the file
500 float scale = (float) source.toString().length() / this.source.toString().length();
501 newCaretPos = (int) (caretPos * scale);
502 }
503 }
504
487 this.source = source; 505 this.source = source;
488 this.editor.getHighlighter().removeAllHighlights(); 506 this.editor.getHighlighter().removeAllHighlights();
489 this.editor.setText(source.toString()); 507 this.editor.setText(source.toString());
508 if (this.source != null) {
509 this.editor.setCaretPosition(newCaretPos);
510 }
490 setHighlightedTokens(source.getHighlightedTokens()); 511 setHighlightedTokens(source.getHighlightedTokens());
512 setCursorReference(getReference(getToken(this.editor.getCaretPosition())));
491 } finally { 513 } finally {
492 this.settingSource = false; 514 this.settingSource = false;
493 } 515 }
494 showReference0(getCursorReference()); 516
517 if (this.nextReference != null) {
518 this.showReference0(this.nextReference);
519 this.nextReference = null;
520 }
495 } 521 }
496 522
497 public void setHighlightedTokens(Map<RenamableTokenType, Collection<Token>> tokens) { 523 public void setHighlightedTokens(Map<RenamableTokenType, ? extends Collection<Token>> tokens) {
498 // remove any old highlighters 524 // remove any old highlighters
499 this.editor.getHighlighter().removeAllHighlights(); 525 this.editor.getHighlighter().removeAllHighlights();
500 526
@@ -526,8 +552,11 @@ public class EditorPanel {
526 } 552 }
527 553
528 public void showReference(EntryReference<Entry<?>, Entry<?>> reference) { 554 public void showReference(EntryReference<Entry<?>, Entry<?>> reference) {
529 setCursorReference(reference); 555 if (this.mode == DisplayMode.SUCCESS) {
530 showReference0(reference); 556 showReference0(reference);
557 } else if (this.mode != DisplayMode.ERRORED) {
558 this.nextReference = reference;
559 }
531 } 560 }
532 561
533 /** 562 /**
diff --git a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java
index bc38e61b..22722075 100644
--- a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java
+++ b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java
@@ -11,10 +11,9 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
11 11
12import javax.annotation.Nullable; 12import javax.annotation.Nullable;
13 13
14import cuchaz.enigma.EnigmaProject;
14import cuchaz.enigma.classprovider.CachingClassProvider; 15import cuchaz.enigma.classprovider.CachingClassProvider;
15import cuchaz.enigma.classprovider.ObfuscationFixClassProvider; 16import cuchaz.enigma.classprovider.ObfuscationFixClassProvider;
16
17import cuchaz.enigma.EnigmaProject;
18import cuchaz.enigma.events.ClassHandleListener; 17import cuchaz.enigma.events.ClassHandleListener;
19import cuchaz.enigma.events.ClassHandleListener.InvalidationType; 18import cuchaz.enigma.events.ClassHandleListener.InvalidationType;
20import cuchaz.enigma.source.*; 19import cuchaz.enigma.source.*;
@@ -277,8 +276,8 @@ public final class ClassHandleProvider {
277 if (res == null || mappedVersion.get() != v) return; 276 if (res == null || mappedVersion.get() != v) return;
278 res = res.andThen(source -> { 277 res = res.andThen(source -> {
279 try { 278 try {
280 source.remapSource(p.project, p.project.getMapper().getDeobfuscator()); 279 DecompiledClassSource remappedSource = source.remapSource(p.project, p.project.getMapper().getDeobfuscator());
281 return Result.ok(source); 280 return Result.ok(remappedSource);
282 } catch (Throwable e) { 281 } catch (Throwable e) {
283 return Result.err(ClassHandleError.remap(e)); 282 return Result.err(ClassHandleError.remap(e));
284 } 283 }
diff --git a/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java b/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java
index e4a71150..9ac611f5 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java
@@ -22,30 +22,35 @@ public class DecompiledClassSource {
22 private final ClassEntry classEntry; 22 private final ClassEntry classEntry;
23 23
24 private final SourceIndex obfuscatedIndex; 24 private final SourceIndex obfuscatedIndex;
25 private SourceIndex remappedIndex; 25 private final SourceIndex remappedIndex;
26 26
27 private final Map<RenamableTokenType, Collection<Token>> highlightedTokens = new EnumMap<>(RenamableTokenType.class); 27 private final TokenStore highlightedTokens;
28 28
29 public DecompiledClassSource(ClassEntry classEntry, SourceIndex index) { 29 private DecompiledClassSource(ClassEntry classEntry, SourceIndex obfuscatedIndex, SourceIndex remappedIndex, TokenStore highlightedTokens) {
30 this.classEntry = classEntry; 30 this.classEntry = classEntry;
31 this.obfuscatedIndex = index; 31 this.obfuscatedIndex = obfuscatedIndex;
32 this.remappedIndex = index; 32 this.remappedIndex = remappedIndex;
33 this.highlightedTokens = highlightedTokens;
34 }
35
36 public DecompiledClassSource(ClassEntry classEntry, SourceIndex index) {
37 this(classEntry, index, index, TokenStore.empty());
33 } 38 }
34 39
35 public static DecompiledClassSource text(ClassEntry classEntry, String text) { 40 public static DecompiledClassSource text(ClassEntry classEntry, String text) {
36 return new DecompiledClassSource(classEntry, new SourceIndex(text)); 41 return new DecompiledClassSource(classEntry, new SourceIndex(text));
37 } 42 }
38 43
39 public void remapSource(EnigmaProject project, Translator translator) { 44 public DecompiledClassSource remapSource(EnigmaProject project, Translator translator) {
40 highlightedTokens.clear();
41
42 SourceRemapper remapper = new SourceRemapper(obfuscatedIndex.getSource(), obfuscatedIndex.referenceTokens()); 45 SourceRemapper remapper = new SourceRemapper(obfuscatedIndex.getSource(), obfuscatedIndex.referenceTokens());
43 46
44 SourceRemapper.Result remapResult = remapper.remap((token, movedToken) -> remapToken(project, token, movedToken, translator)); 47 TokenStore tokenStore = TokenStore.create(this.obfuscatedIndex);
45 remappedIndex = obfuscatedIndex.remapTo(remapResult); 48 SourceRemapper.Result remapResult = remapper.remap((token, movedToken) -> remapToken(tokenStore, project, token, movedToken, translator));
49 SourceIndex remappedIndex = obfuscatedIndex.remapTo(remapResult);
50 return new DecompiledClassSource(this.classEntry, this.obfuscatedIndex, remappedIndex, tokenStore);
46 } 51 }
47 52
48 private String remapToken(EnigmaProject project, Token token, Token movedToken, Translator translator) { 53 private String remapToken(TokenStore target, EnigmaProject project, Token token, Token movedToken, Translator translator) {
49 EntryReference<Entry<?>, Entry<?>> reference = obfuscatedIndex.getReference(token); 54 EntryReference<Entry<?>, Entry<?>> reference = obfuscatedIndex.getReference(token);
50 55
51 Entry<?> entry = reference.getNameableEntry(); 56 Entry<?> entry = reference.getNameableEntry();
@@ -53,16 +58,16 @@ public class DecompiledClassSource {
53 58
54 if (project.isRenamable(reference)) { 59 if (project.isRenamable(reference)) {
55 if (!translatedEntry.isObfuscated()) { 60 if (!translatedEntry.isObfuscated()) {
56 highlightToken(movedToken, translatedEntry.getType()); 61 target.add(translatedEntry.getType(), movedToken);
57 return translatedEntry.getValue().getSourceRemapName(); 62 return translatedEntry.getValue().getSourceRemapName();
58 } else { 63 } else {
59 Optional<String> proposedName = proposeName(project, entry); 64 Optional<String> proposedName = proposeName(project, entry);
60 if (proposedName.isPresent()) { 65 if (proposedName.isPresent()) {
61 highlightToken(movedToken, RenamableTokenType.PROPOSED); 66 target.add(RenamableTokenType.PROPOSED, movedToken);
62 return proposedName.get(); 67 return proposedName.get();
63 } 68 }
64 69
65 highlightToken(movedToken, RenamableTokenType.OBFUSCATED); 70 target.add(RenamableTokenType.OBFUSCATED, movedToken);
66 } 71 }
67 } 72 }
68 73
@@ -113,12 +118,12 @@ public class DecompiledClassSource {
113 return remappedIndex; 118 return remappedIndex;
114 } 119 }
115 120
116 public Map<RenamableTokenType, Collection<Token>> getHighlightedTokens() { 121 public TokenStore getTokenStore() {
117 return highlightedTokens; 122 return this.highlightedTokens;
118 } 123 }
119 124
120 private void highlightToken(Token token, RenamableTokenType highlightType) { 125 public Map<RenamableTokenType, ? extends Collection<Token>> getHighlightedTokens() {
121 highlightedTokens.computeIfAbsent(highlightType, t -> new ArrayList<>()).add(token); 126 return this.highlightedTokens.getByType();
122 } 127 }
123 128
124 public int getObfuscatedOffset(int deobfOffset) { 129 public int getObfuscatedOffset(int deobfOffset) {
diff --git a/enigma/src/main/java/cuchaz/enigma/source/Token.java b/enigma/src/main/java/cuchaz/enigma/source/Token.java
index fad733fa..7d729abb 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/Token.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/Token.java
@@ -23,9 +23,12 @@ public class Token implements Comparable<Token> {
23 this.text = text; 23 this.text = text;
24 } 24 }
25 25
26 public int length() {
27 return this.end - this.start;
28 }
29
26 public int getRenameOffset(String to) { 30 public int getRenameOffset(String to) {
27 int length = this.end - this.start; 31 return to.length() - this.length();
28 return to.length() - length;
29 } 32 }
30 33
31 public void rename(StringBuffer source, String to) { 34 public void rename(StringBuffer source, String to) {
diff --git a/enigma/src/main/java/cuchaz/enigma/source/TokenStore.java b/enigma/src/main/java/cuchaz/enigma/source/TokenStore.java
new file mode 100644
index 00000000..f32d918c
--- /dev/null
+++ b/enigma/src/main/java/cuchaz/enigma/source/TokenStore.java
@@ -0,0 +1,71 @@
1package cuchaz.enigma.source;
2
3import java.util.*;
4
5public final class TokenStore {
6
7 private static final TokenStore EMPTY = new TokenStore(Collections.emptyNavigableSet(), Collections.emptyMap(), null);
8
9 private final NavigableSet<Token> tokens;
10 private final Map<RenamableTokenType, NavigableSet<Token>> byType;
11 private final String obfSource;
12
13 private TokenStore(NavigableSet<Token> tokens, Map<RenamableTokenType, NavigableSet<Token>> byType, String obfSource) {
14 this.tokens = tokens;
15 this.byType = byType;
16 this.obfSource = obfSource;
17 }
18
19 public static TokenStore create(SourceIndex obfuscatedIndex) {
20 EnumMap<RenamableTokenType, NavigableSet<Token>> map = new EnumMap<>(RenamableTokenType.class);
21 for (RenamableTokenType value : RenamableTokenType.values()) {
22 map.put(value, new TreeSet<>(Comparator.comparing(t -> t.start)));
23 }
24 return new TokenStore(new TreeSet<>(Comparator.comparing(t -> t.start)), Collections.unmodifiableMap(map), obfuscatedIndex.getSource());
25 }
26
27 public static TokenStore empty() {
28 return TokenStore.EMPTY;
29 }
30
31 public void add(RenamableTokenType type, Token token) {
32 this.tokens.add(token);
33 this.byType.get(type).add(token);
34 }
35
36 public boolean isCompatible(TokenStore other) {
37 return this.obfSource != null && other.obfSource != null &&
38 this.obfSource.equals(other.obfSource) &&
39 this.tokens.size() == other.tokens.size();
40 }
41
42 public int mapPosition(TokenStore to, int position) {
43 if (!this.isCompatible(to)) return 0;
44
45 int newPos = position;
46 Iterator<Token> thisIter = this.tokens.iterator();
47 Iterator<Token> toIter = to.tokens.iterator();
48 while (thisIter.hasNext()) {
49 Token token = thisIter.next();
50 Token newToken = toIter.next();
51
52 if (position < token.start) break;
53
54 // if we're inside the token and the text changed,
55 // snap the cursor to the beginning
56 if (!token.text.equals(newToken.text) && position < token.end) {
57 newPos = newToken.start;
58 break;
59 }
60
61 newPos += newToken.length() - token.length();
62 }
63
64 return newPos;
65 }
66
67 public Map<RenamableTokenType, NavigableSet<Token>> getByType() {
68 return byType;
69 }
70
71}