From facd73d345ab21ff9372164d6ebc475f72d81902 Mon Sep 17 00:00:00 2001 From: Brendan Robert Date: Sun, 23 Aug 2015 18:22:34 -0500 Subject: [PATCH] Refactored code a little, cheats are now editable and support javascript expressions. Almost done now! :-D --- src/main/java/jace/cheat/DynamicCheat.java | 99 +++++++++++++ src/main/java/jace/cheat/MemoryCell.java | 75 ++++++++++ src/main/java/jace/cheat/MetaCheat.java | 121 +++------------ src/main/java/jace/ui/MetacheatUI.java | 163 ++++++--------------- src/main/java/jace/ui/Watch.java | 121 +++++++++++++++ src/main/resources/fxml/Metacheat.fxml | 14 +- 6 files changed, 367 insertions(+), 226 deletions(-) create mode 100644 src/main/java/jace/cheat/DynamicCheat.java create mode 100644 src/main/java/jace/cheat/MemoryCell.java create mode 100644 src/main/java/jace/ui/Watch.java diff --git a/src/main/java/jace/cheat/DynamicCheat.java b/src/main/java/jace/cheat/DynamicCheat.java new file mode 100644 index 0000000..f46cf9e --- /dev/null +++ b/src/main/java/jace/cheat/DynamicCheat.java @@ -0,0 +1,99 @@ +package jace.cheat; + +import jace.core.RAMEvent; +import jace.core.RAMListener; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.util.Callback; +import javax.script.ScriptException; + +/** + * + * @author blurry + */ +public class DynamicCheat extends RAMListener { + int id; + IntegerProperty addr; + StringProperty expression; + BooleanProperty active; + StringProperty name; + Callback expressionCallback; + + public DynamicCheat(int address, String expr) { + super(RAMEvent.TYPE.ANY, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY); + id = (int) (Math.random() * 10000000); + addr = new SimpleIntegerProperty(address); + expression = new SimpleStringProperty(expr); + active = new SimpleBooleanProperty(true); + name = new SimpleStringProperty("Untitled"); + expression.addListener((param, oldValue, newValue) -> { + expressionCallback = parseExpression(newValue); + }); + expressionCallback = parseExpression(expr); + doConfig(); + } + + @Override + protected void doConfig() { + if (addr != null) { + setScopeStart(addr.getValue()); + } + } + + @Override + protected void doEvent(RAMEvent e) { + if (active.get() && expressionCallback != null) { + Integer newVal = expressionCallback.call(e); + if (newVal != null) { + e.setNewValue(newVal); + } else { + active.set(false); + expressionCallback = null; + } + } + } + + public BooleanProperty activeProperty() { + return active; + } + + public StringProperty nameProperty() { + return name; + } + + public IntegerProperty addressProperty() { + return addr; + } + + public StringProperty expressionProperty() { + return expression; + } + + private Callback parseExpression(String expr) { + String functionName = "processCheat" + id; + String functionBody = "function " + functionName + "(old,val){" + (expr.contains("return") ? expr : "return " + expr) + "}"; + try { + MetaCheat.NASHORN_ENGINE.eval(functionBody); + return (RAMEvent e) -> { + try { + Object result = MetaCheat.NASHORN_INVOCABLE.invokeFunction(functionName, e.getOldValue(), e.getNewValue()); + if (result instanceof Number) { + return ((Number) result).intValue(); + } else { + System.err.println("Not able to handle non-numeric return value: " + result.getClass()); + return null; + } + } catch (ScriptException | NoSuchMethodException ex) { + return null; + } + }; + } catch (ScriptException ex) { + return null; + } + } + +} diff --git a/src/main/java/jace/cheat/MemoryCell.java b/src/main/java/jace/cheat/MemoryCell.java new file mode 100644 index 0000000..1979c65 --- /dev/null +++ b/src/main/java/jace/cheat/MemoryCell.java @@ -0,0 +1,75 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package jace.cheat; + +import java.util.ArrayList; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +/** + * + * @author blurry + */ +public class MemoryCell implements Comparable { + public static ChangeListener listener; + public int address; + public IntegerProperty value = new SimpleIntegerProperty(); + public IntegerProperty readCount = new SimpleIntegerProperty(); + public IntegerProperty execCount = new SimpleIntegerProperty(); + public IntegerProperty writeCount = new SimpleIntegerProperty(); + public ObservableList readInstructions = FXCollections.observableList(new ArrayList<>()); + public ObservableList writeInstructions = FXCollections.observableList(new ArrayList<>()); + private int x; + private int y; + private int width; + private int height; + + public static void setListener(ChangeListener l) { + listener = l; + } + + public MemoryCell() { + ChangeListener changeListener = (ObservableValue val, Number oldVal, Number newVal) -> { + if (listener != null) { + listener.changed(null, this, this); + } + }; + value.addListener(changeListener); + } + + public void setRect(int x, int y, int w, int h) { + this.x = x; + this.y = y; + this.width = w; + this.height = h; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + @Override + public int compareTo(MemoryCell o) { + return address - o.address; + } + +} diff --git a/src/main/java/jace/cheat/MetaCheat.java b/src/main/java/jace/cheat/MetaCheat.java index 209ec55..043dbc2 100644 --- a/src/main/java/jace/cheat/MetaCheat.java +++ b/src/main/java/jace/cheat/MetaCheat.java @@ -8,23 +8,25 @@ import jace.core.RAMEvent; import jace.core.RAMListener; import jace.state.State; import jace.ui.MetacheatUI; -import java.util.ArrayList; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javafx.beans.property.BooleanProperty; -import javafx.beans.property.IntegerProperty; import javafx.beans.property.Property; import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; -import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; public class MetaCheat extends Cheats { + static final ScriptEngine NASHORN_ENGINE = new ScriptEngineManager().getEngineByName("nashorn"); + static Invocable NASHORN_INVOCABLE = (Invocable) NASHORN_ENGINE; + public static enum SearchType { VALUE, TEXT, CHANGE } @@ -33,62 +35,6 @@ public class MetaCheat extends Cheats { NO_CHANGE, ANY_CHANGE, LESS, GREATER, AMOUNT } - public static class MemoryCell implements Comparable { - - public static ChangeListener listener; - public int address; - public IntegerProperty value = new SimpleIntegerProperty(); - public IntegerProperty readCount = new SimpleIntegerProperty(); - public IntegerProperty execCount = new SimpleIntegerProperty(); - public IntegerProperty writeCount = new SimpleIntegerProperty(); - public ObservableList readInstructions = FXCollections.observableList(new ArrayList<>()); - public ObservableList writeInstructions = FXCollections.observableList(new ArrayList<>()); - private int x; - private int y; - private int width; - private int height; - - public static void setListener(ChangeListener l) { - listener = l; - } - - public MemoryCell() { - ChangeListener changeListener = (ObservableValue val, Number oldVal, Number newVal) -> { - if (listener != null) { - listener.changed(null, this, this); - } - }; - value.addListener(changeListener); - } - - public void setRect(int x, int y, int w, int h) { - this.x = x; - this.y = y; - this.width = w; - this.height = h; - } - - public int getX() { - return x; - } - - public int getY() { - return y; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - @Override - public int compareTo(MemoryCell o) { - return address - o.address; - } - } public static class SearchResult { @@ -106,42 +52,6 @@ public class MetaCheat extends Cheats { } } - public static class Cheat extends RAMListener { - IntegerProperty addr; - IntegerProperty val; - BooleanProperty active; - - public Cheat(int address, int value) { - super(RAMEvent.TYPE.ANY, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY); - addr = new SimpleIntegerProperty(address); - val = new SimpleIntegerProperty(value); - active = new SimpleBooleanProperty(true); - setScopeStart(address); - } - - @Override - protected void doConfig() { - } - - @Override - protected void doEvent(RAMEvent e) { - if (active.get()) { - e.setNewValue(val.get()); - } - } - - public BooleanProperty activeProperty() { - return active; - } - - public IntegerProperty addressProperty() { - return addr; - } - - public IntegerProperty valueProperty() { - return val; - } - } MetacheatUI ui; @@ -159,7 +69,7 @@ public class MetaCheat extends Cheats { private final BooleanProperty signedProperty = new SimpleBooleanProperty(false); private final StringProperty searchValueProperty = new SimpleStringProperty("0"); private final StringProperty changeByProperty = new SimpleStringProperty("0"); - private final ObservableList cheatList = FXCollections.observableArrayList(); + private final ObservableList cheatList = FXCollections.observableArrayList(); private final ObservableList resultList = FXCollections.observableArrayList(); private final ObservableList snapshotList = FXCollections.observableArrayList(); @@ -194,14 +104,14 @@ public class MetaCheat extends Cheats { } if (s.matches("(\\+|-)?[0-9]+")) { return Integer.parseInt(s); - } else if (s.matches("(\\+|-)?[0-9a-fA-F]+")) { - return Integer.parseInt(s.toUpperCase(), 16); - } else if (s.matches("(\\+|-)?(x|$)[0-9a-fA-F]+")) { + } else { String upper = s.toUpperCase(); boolean positive = true; if (upper.startsWith("-")) { positive = false; + upper = upper.substring(1); } + for (int i = 0; i < upper.length(); i++) { char c = upper.charAt(i); if ((c >= '0' && c <= '9') || (c >= 'A' & c <= 'F')) { @@ -220,10 +130,15 @@ public class MetaCheat extends Cheats { void registerListeners() { } - public void addCheat(Cheat cheat) { + public void addCheat(DynamicCheat cheat) { cheat.activeProperty().set(true); cheatList.add(cheat); computer.getMemory().addListener(cheat); + cheat.addressProperty().addListener((prop, oldVal, newVal)->{ + computer.getMemory().removeListener(cheat); + cheat.doConfig(); + computer.getMemory().addListener(cheat); + }); } @Override @@ -282,7 +197,7 @@ public class MetaCheat extends Cheats { return changeByProperty; } - public ObservableList getCheats() { + public ObservableList getCheats() { return cheatList; } @@ -384,7 +299,7 @@ public class MetaCheat extends Cheats { public void tick() { if (fadeCounter-- <= 0) { fadeCounter = FADE_TIMER_VALUE; - memoryCells.values().stream().forEach((cell) -> { + memoryCells.values().stream().forEach((jace.cheat.MemoryCell cell) -> { boolean change = false; if (cell.execCount.get() > 0) { cell.execCount.set(Math.max(0, cell.execCount.get() - fadeRate)); diff --git a/src/main/java/jace/ui/MetacheatUI.java b/src/main/java/jace/ui/MetacheatUI.java index 0dabbcb..912c1ed 100644 --- a/src/main/java/jace/ui/MetacheatUI.java +++ b/src/main/java/jace/ui/MetacheatUI.java @@ -2,16 +2,14 @@ package jace.ui; import com.sun.glass.ui.Application; import jace.Emulator; +import jace.cheat.DynamicCheat; +import jace.cheat.MemoryCell; import jace.cheat.MetaCheat; -import jace.cheat.MetaCheat.Cheat; import jace.cheat.MetaCheat.SearchChangeType; import jace.cheat.MetaCheat.SearchType; import jace.core.RAMListener; import jace.state.State; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.ScheduledExecutorService; @@ -19,7 +17,6 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javafx.application.Platform; -import javafx.beans.property.BooleanProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; @@ -35,22 +32,21 @@ import javafx.scene.control.ListView; import javafx.scene.control.RadioButton; import javafx.scene.control.ScrollPane; import javafx.scene.control.TabPane; +import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.control.Toggle; import javafx.scene.control.ToggleGroup; import javafx.scene.control.Tooltip; +import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.control.cell.TextFieldTableCell; import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Background; -import javafx.scene.layout.BackgroundFill; -import javafx.scene.layout.CornerRadii; import javafx.scene.layout.StackPane; import javafx.scene.layout.TilePane; -import javafx.scene.layout.VBox; import javafx.scene.paint.Color; -import javafx.scene.text.Font; -import javafx.scene.text.TextAlignment; +import javafx.util.converter.DefaultStringConverter; +import javafx.util.converter.IntegerStringConverter; public class MetacheatUI { @@ -127,7 +123,7 @@ public class MetacheatUI { private ListView snapshotsListView; @FXML - private TableView cheatsTableView; + private TableView cheatsTableView; @FXML void addCheat(ActionEvent event) { @@ -271,12 +267,36 @@ public class MetacheatUI { searchStartAddressField.textProperty().addListener(addressRangeListener); searchEndAddressField.textProperty().addListener(addressRangeListener); - + RAMListener l; - - cheatsTableView.getColumns().get(0).setCellValueFactory(new PropertyValueFactory<>("active")); - cheatsTableView.getColumns().get(1).setCellValueFactory(new PropertyValueFactory<>("address")); - cheatsTableView.getColumns().get(2).setCellValueFactory(new PropertyValueFactory<>("value")); + + TableColumn activeColumn = (TableColumn) cheatsTableView.getColumns().get(0); + activeColumn.setCellValueFactory(new PropertyValueFactory<>("active")); + activeColumn.setCellFactory((TableColumn param) -> new CheckBoxTableCell<>()); + + TableColumn nameColumn = (TableColumn) cheatsTableView.getColumns().get(1); + nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); + nameColumn.setCellFactory((TableColumn param) -> new TextFieldTableCell<>(new DefaultStringConverter())); + + TableColumn addrColumn = (TableColumn) cheatsTableView.getColumns().get(2); + addrColumn.setCellValueFactory(new PropertyValueFactory<>("address")); + addrColumn.setCellFactory((TableColumn param) -> { + return new TextFieldTableCell<>(new IntegerStringConverter(){ + @Override + public String toString(Integer value) { + return "$"+Integer.toHexString(value); + } + + @Override + public Integer fromString(String value) { + return cheatEngine.parseInt(value); + } + }); + }); + + TableColumn exprColumn = (TableColumn) cheatsTableView.getColumns().get(3); + exprColumn.setCellValueFactory(new PropertyValueFactory<>("expression")); + exprColumn.setCellFactory((TableColumn param) -> new TextFieldTableCell<>(new DefaultStringConverter())); } MetaCheat cheatEngine = null; @@ -303,7 +323,7 @@ public class MetacheatUI { public int memoryViewColumns; public int memoryViewRows; - public static Set redrawNodes = new ConcurrentSkipListSet<>(); + public static Set redrawNodes = new ConcurrentSkipListSet<>(); ScheduledExecutorService animationTimer = null; ScheduledFuture animationFuture = null; StackPane pane = new StackPane(); @@ -322,7 +342,7 @@ public class MetacheatUI { int col = (int) (x / MEMORY_BOX_TOTAL_SIZE); int row = (int) (y / MEMORY_BOX_TOTAL_SIZE); int addr = cheatEngine.getStartAddress() + row * memoryViewColumns + col; - Watch watch = new Watch(addr); + Watch watch = new Watch(addr, this); Label addWatch = new Label("Watch >>"); addWatch.setOnMouseClicked((mouseEvent) -> { @@ -353,9 +373,9 @@ public class MetacheatUI { private void processMemoryViewUpdates() { Application.invokeLater(() -> { GraphicsContext context = memoryViewCanvas.getGraphicsContext2D(); - Set draw = new HashSet<>(redrawNodes); + Set draw = new HashSet<>(redrawNodes); redrawNodes.clear(); - draw.stream().forEach((cell) -> { + draw.stream().forEach((jace.cheat.MemoryCell cell) -> { if (showValuesCheckbox.isSelected()) { int val = cell.value.get() & 0x0ff; context.setFill(Color.rgb(val, val, val)); @@ -401,11 +421,11 @@ public class MetacheatUI { for (int addr = cheatEngine.getStartAddress(); addr <= cheatEngine.getEndAddress(); addr++) { int col = (addr - cheatEngine.getStartAddress()) % memoryViewColumns; int row = (addr - cheatEngine.getStartAddress()) / memoryViewColumns; - MetaCheat.MemoryCell cell = cheatEngine.getMemoryCell(addr); + MemoryCell cell = cheatEngine.getMemoryCell(addr); cell.setRect(col * MEMORY_BOX_TOTAL_SIZE, row * MEMORY_BOX_TOTAL_SIZE, MEMORY_BOX_SIZE, MEMORY_BOX_SIZE); redrawNodes.add(cell); } - MetaCheat.MemoryCell.setListener((prop, oldCell, newCell) -> { + MemoryCell.setListener((javafx.beans.value.ObservableValue prop, jace.cheat.MemoryCell oldCell, jace.cheat.MemoryCell newCell) -> { redrawNodes.add(newCell); }); @@ -446,7 +466,7 @@ public class MetacheatUI { } private Watch addWatch(int addr) { - Watch watch = new Watch(addr); + Watch watch = new Watch(addr, this); watch.setPadding(new Insets(5)); watch.setOpaqueInsets(new Insets(10)); @@ -470,100 +490,7 @@ public class MetacheatUI { } private void addCheat(int addr, int val) { - cheatEngine.addCheat(new Cheat(addr, val)); + cheatEngine.addCheat(new DynamicCheat(addr, String.valueOf(val))); } - private static final int GRAPH_WIDTH = 50; - private static final double GRAPH_HEIGHT = 50; - - private class Watch extends VBox { - - int address; - ScheduledFuture redraw; - Canvas graph; - List samples = Collections.synchronizedList(new ArrayList<>()); - int value = 0; - BooleanProperty holding = null; - - public Watch(int address) { - super(); - this.address = address; - redraw = animationTimer.scheduleAtFixedRate(this::redraw, FRAME_RATE, FRAME_RATE, TimeUnit.MILLISECONDS); - - setBackground(new Background(new BackgroundFill(Color.NAVY, CornerRadii.EMPTY, Insets.EMPTY))); - Label addrLabel = new Label("$" + Integer.toHexString(address)); - addrLabel.setTextAlignment(TextAlignment.CENTER); - addrLabel.setMinWidth(GRAPH_WIDTH); - addrLabel.setFont(new Font(Font.getDefault().getFamily(), 14)); - addrLabel.setTextFill(Color.WHITE); - graph = new Canvas(GRAPH_WIDTH, GRAPH_HEIGHT); - getChildren().add(addrLabel); - getChildren().add(graph); - - CheckBox hold = new CheckBox("Hold"); - holding = hold.selectedProperty(); - holding.addListener((prop, oldVal, newVal) -> this.updateHold()); - - getChildren().add(hold); - hold.setTextFill(Color.WHITE); - } - - public int getValue() { - return value; - } - - public void redraw() { - int val = cheatEngine.getMemoryCell(address).value.get() & 0x0ff; - if (!holding.get()) { - value = val; - } - if (samples.size() >= GRAPH_WIDTH) { - samples.remove(0); - } - samples.add(val); - - Platform.runLater(() -> { - GraphicsContext g = graph.getGraphicsContext2D(); - g.setFill(Color.BLACK); - g.fillRect(0, 0, GRAPH_WIDTH, GRAPH_HEIGHT); - - if (samples.size() > 1) { - g.setLineWidth(1); - g.setStroke(Color.LAWNGREEN); - int y = (int) (GRAPH_HEIGHT - ((samples.get(0) / 255.0) * GRAPH_HEIGHT)); - g.beginPath(); - g.moveTo(0, y); - for (int i = 1; i < samples.size(); i++) { - y = (int) (GRAPH_HEIGHT - ((samples.get(i) / 255.0) * GRAPH_HEIGHT)); - g.lineTo(i, y); - } - g.stroke(); - } - g.beginPath(); - g.setStroke(Color.WHITE); - g.strokeText(String.valueOf(val), GRAPH_WIDTH - 25, GRAPH_HEIGHT - 5); - }); - } - - RAMListener holdListener; - - public BooleanProperty holdingProperty() { - return holding; - } - - private void updateHold() { - if (!holding.get()) { - cheatEngine.removeListener(holdListener); - holdListener = null; - } else { - value = Emulator.computer.memory.readRaw(address) & 0x0ff; - holdListener = cheatEngine.forceValue(value, address); - } - } - - public void disconnect() { - holding.set(false); - redraw.cancel(false); - } - } } diff --git a/src/main/java/jace/ui/Watch.java b/src/main/java/jace/ui/Watch.java new file mode 100644 index 0000000..42c9e27 --- /dev/null +++ b/src/main/java/jace/ui/Watch.java @@ -0,0 +1,121 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package jace.ui; + +import jace.Emulator; +import jace.core.RAMListener; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import javafx.application.Platform; +import javafx.beans.property.BooleanProperty; +import javafx.geometry.Insets; +import javafx.scene.canvas.Canvas; +import javafx.scene.canvas.GraphicsContext; +import javafx.scene.control.CheckBox; +import javafx.scene.control.Label; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundFill; +import javafx.scene.layout.CornerRadii; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.text.Font; +import javafx.scene.text.TextAlignment; + +/** + * + * @author blurry + */ +class Watch extends VBox { + private static final int GRAPH_WIDTH = 50; + private static final double GRAPH_HEIGHT = 50; + int address; + ScheduledFuture redraw; + Canvas graph; + List samples = Collections.synchronizedList(new ArrayList<>()); + int value = 0; + BooleanProperty holding = null; + private final MetacheatUI outer; + + public Watch(int address, final MetacheatUI outer) { + super(); + this.outer = outer; + this.address = address; + redraw = outer.animationTimer.scheduleAtFixedRate(this::redraw, MetacheatUI.FRAME_RATE, MetacheatUI.FRAME_RATE, TimeUnit.MILLISECONDS); + setBackground(new Background(new BackgroundFill(Color.NAVY, CornerRadii.EMPTY, Insets.EMPTY))); + Label addrLabel = new Label("$" + Integer.toHexString(address)); + addrLabel.setTextAlignment(TextAlignment.CENTER); + addrLabel.setMinWidth(GRAPH_WIDTH); + addrLabel.setFont(new Font(Font.getDefault().getFamily(), 14)); + addrLabel.setTextFill(Color.WHITE); + graph = new Canvas(GRAPH_WIDTH, GRAPH_HEIGHT); + getChildren().add(addrLabel); + getChildren().add(graph); + CheckBox hold = new CheckBox("Hold"); + holding = hold.selectedProperty(); + holding.addListener((prop, oldVal, newVal) -> this.updateHold()); + getChildren().add(hold); + hold.setTextFill(Color.WHITE); + } + + public int getValue() { + return value; + } + + public void redraw() { + int val = outer.cheatEngine.getMemoryCell(address).value.get() & 0x0ff; + if (!holding.get()) { + value = val; + } + if (samples.size() >= GRAPH_WIDTH) { + samples.remove(0); + } + samples.add(val); + Platform.runLater(() -> { + GraphicsContext g = graph.getGraphicsContext2D(); + g.setFill(Color.BLACK); + g.fillRect(0, 0, GRAPH_WIDTH, GRAPH_HEIGHT); + if (samples.size() > 1) { + g.setLineWidth(1); + g.setStroke(Color.LAWNGREEN); + int y = (int) (GRAPH_HEIGHT - ((samples.get(0) / 255.0) * GRAPH_HEIGHT)); + g.beginPath(); + g.moveTo(0, y); + for (int i = 1; i < samples.size(); i++) { + y = (int) (GRAPH_HEIGHT - ((samples.get(i) / 255.0) * GRAPH_HEIGHT)); + g.lineTo(i, y); + } + g.stroke(); + } + g.beginPath(); + g.setStroke(Color.WHITE); + g.strokeText(String.valueOf(val), GRAPH_WIDTH - 25, GRAPH_HEIGHT - 5); + }); + } + RAMListener holdListener; + + public BooleanProperty holdingProperty() { + return holding; + } + + private void updateHold() { + if (!holding.get()) { + outer.cheatEngine.removeListener(holdListener); + holdListener = null; + } else { + value = Emulator.computer.memory.readRaw(address) & 0x0ff; + holdListener = outer.cheatEngine.forceValue(value, address); + } + } + + public void disconnect() { + holding.set(false); + redraw.cancel(false); + } + +} diff --git a/src/main/resources/fxml/Metacheat.fxml b/src/main/resources/fxml/Metacheat.fxml index 90496e7..d524b53 100644 --- a/src/main/resources/fxml/Metacheat.fxml +++ b/src/main/resources/fxml/Metacheat.fxml @@ -31,7 +31,7 @@ - + @@ -198,12 +198,16 @@
- + - - - + + + + + + +