Refactored code a little, cheats are now editable and support javascript expressions. Almost done now! :-D

This commit is contained in:
Brendan Robert 2015-08-23 18:22:34 -05:00
parent bed635c7fc
commit facd73d345
6 changed files with 367 additions and 226 deletions

View File

@ -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<RAMEvent, Integer> 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<RAMEvent, Integer> 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;
}
}
}

View File

@ -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<MemoryCell> {
public static ChangeListener<MemoryCell> 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<Integer> readInstructions = FXCollections.observableList(new ArrayList<>());
public ObservableList<Integer> writeInstructions = FXCollections.observableList(new ArrayList<>());
private int x;
private int y;
private int width;
private int height;
public static void setListener(ChangeListener<MemoryCell> l) {
listener = l;
}
public MemoryCell() {
ChangeListener<Number> changeListener = (ObservableValue<? extends Number> 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;
}
}

View File

@ -8,23 +8,25 @@ import jace.core.RAMEvent;
import jace.core.RAMListener; import jace.core.RAMListener;
import jace.state.State; import jace.state.State;
import jace.ui.MetacheatUI; import jace.ui.MetacheatUI;
import java.util.ArrayList;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class MetaCheat extends Cheats { 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 { public static enum SearchType {
VALUE, TEXT, CHANGE VALUE, TEXT, CHANGE
} }
@ -33,62 +35,6 @@ public class MetaCheat extends Cheats {
NO_CHANGE, ANY_CHANGE, LESS, GREATER, AMOUNT NO_CHANGE, ANY_CHANGE, LESS, GREATER, AMOUNT
} }
public static class MemoryCell implements Comparable<MemoryCell> {
public static ChangeListener<MemoryCell> 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<Integer> readInstructions = FXCollections.observableList(new ArrayList<>());
public ObservableList<Integer> writeInstructions = FXCollections.observableList(new ArrayList<>());
private int x;
private int y;
private int width;
private int height;
public static void setListener(ChangeListener<MemoryCell> l) {
listener = l;
}
public MemoryCell() {
ChangeListener<Number> changeListener = (ObservableValue<? extends Number> 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 { 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; MetacheatUI ui;
@ -159,7 +69,7 @@ public class MetaCheat extends Cheats {
private final BooleanProperty signedProperty = new SimpleBooleanProperty(false); private final BooleanProperty signedProperty = new SimpleBooleanProperty(false);
private final StringProperty searchValueProperty = new SimpleStringProperty("0"); private final StringProperty searchValueProperty = new SimpleStringProperty("0");
private final StringProperty changeByProperty = new SimpleStringProperty("0"); private final StringProperty changeByProperty = new SimpleStringProperty("0");
private final ObservableList<Cheat> cheatList = FXCollections.observableArrayList(); private final ObservableList<DynamicCheat> cheatList = FXCollections.observableArrayList();
private final ObservableList<SearchResult> resultList = FXCollections.observableArrayList(); private final ObservableList<SearchResult> resultList = FXCollections.observableArrayList();
private final ObservableList<State> snapshotList = FXCollections.observableArrayList(); private final ObservableList<State> snapshotList = FXCollections.observableArrayList();
@ -194,14 +104,14 @@ public class MetaCheat extends Cheats {
} }
if (s.matches("(\\+|-)?[0-9]+")) { if (s.matches("(\\+|-)?[0-9]+")) {
return Integer.parseInt(s); return Integer.parseInt(s);
} else if (s.matches("(\\+|-)?[0-9a-fA-F]+")) { } else {
return Integer.parseInt(s.toUpperCase(), 16);
} else if (s.matches("(\\+|-)?(x|$)[0-9a-fA-F]+")) {
String upper = s.toUpperCase(); String upper = s.toUpperCase();
boolean positive = true; boolean positive = true;
if (upper.startsWith("-")) { if (upper.startsWith("-")) {
positive = false; positive = false;
upper = upper.substring(1);
} }
for (int i = 0; i < upper.length(); i++) { for (int i = 0; i < upper.length(); i++) {
char c = upper.charAt(i); char c = upper.charAt(i);
if ((c >= '0' && c <= '9') || (c >= 'A' & c <= 'F')) { if ((c >= '0' && c <= '9') || (c >= 'A' & c <= 'F')) {
@ -220,10 +130,15 @@ public class MetaCheat extends Cheats {
void registerListeners() { void registerListeners() {
} }
public void addCheat(Cheat cheat) { public void addCheat(DynamicCheat cheat) {
cheat.activeProperty().set(true); cheat.activeProperty().set(true);
cheatList.add(cheat); cheatList.add(cheat);
computer.getMemory().addListener(cheat); computer.getMemory().addListener(cheat);
cheat.addressProperty().addListener((prop, oldVal, newVal)->{
computer.getMemory().removeListener(cheat);
cheat.doConfig();
computer.getMemory().addListener(cheat);
});
} }
@Override @Override
@ -282,7 +197,7 @@ public class MetaCheat extends Cheats {
return changeByProperty; return changeByProperty;
} }
public ObservableList<Cheat> getCheats() { public ObservableList<DynamicCheat> getCheats() {
return cheatList; return cheatList;
} }
@ -384,7 +299,7 @@ public class MetaCheat extends Cheats {
public void tick() { public void tick() {
if (fadeCounter-- <= 0) { if (fadeCounter-- <= 0) {
fadeCounter = FADE_TIMER_VALUE; fadeCounter = FADE_TIMER_VALUE;
memoryCells.values().stream().forEach((cell) -> { memoryCells.values().stream().forEach((jace.cheat.MemoryCell cell) -> {
boolean change = false; boolean change = false;
if (cell.execCount.get() > 0) { if (cell.execCount.get() > 0) {
cell.execCount.set(Math.max(0, cell.execCount.get() - fadeRate)); cell.execCount.set(Math.max(0, cell.execCount.get() - fadeRate));

View File

@ -2,16 +2,14 @@ package jace.ui;
import com.sun.glass.ui.Application; import com.sun.glass.ui.Application;
import jace.Emulator; import jace.Emulator;
import jace.cheat.DynamicCheat;
import jace.cheat.MemoryCell;
import jace.cheat.MetaCheat; import jace.cheat.MetaCheat;
import jace.cheat.MetaCheat.Cheat;
import jace.cheat.MetaCheat.SearchChangeType; import jace.cheat.MetaCheat.SearchChangeType;
import jace.cheat.MetaCheat.SearchType; import jace.cheat.MetaCheat.SearchType;
import jace.core.RAMListener; import jace.core.RAMListener;
import jace.state.State; import jace.state.State;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@ -19,7 +17,6 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
@ -35,22 +32,21 @@ import javafx.scene.control.ListView;
import javafx.scene.control.RadioButton; import javafx.scene.control.RadioButton;
import javafx.scene.control.ScrollPane; import javafx.scene.control.ScrollPane;
import javafx.scene.control.TabPane; import javafx.scene.control.TabPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView; import javafx.scene.control.TableView;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.control.Toggle; import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup; import javafx.scene.control.ToggleGroup;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.input.MouseEvent; 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.StackPane;
import javafx.scene.layout.TilePane; import javafx.scene.layout.TilePane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.text.Font; import javafx.util.converter.DefaultStringConverter;
import javafx.scene.text.TextAlignment; import javafx.util.converter.IntegerStringConverter;
public class MetacheatUI { public class MetacheatUI {
@ -127,7 +123,7 @@ public class MetacheatUI {
private ListView<State> snapshotsListView; private ListView<State> snapshotsListView;
@FXML @FXML
private TableView<Cheat> cheatsTableView; private TableView<DynamicCheat> cheatsTableView;
@FXML @FXML
void addCheat(ActionEvent event) { void addCheat(ActionEvent event) {
@ -271,12 +267,36 @@ public class MetacheatUI {
searchStartAddressField.textProperty().addListener(addressRangeListener); searchStartAddressField.textProperty().addListener(addressRangeListener);
searchEndAddressField.textProperty().addListener(addressRangeListener); searchEndAddressField.textProperty().addListener(addressRangeListener);
RAMListener l; RAMListener l;
cheatsTableView.getColumns().get(0).setCellValueFactory(new PropertyValueFactory<>("active")); TableColumn<DynamicCheat, Boolean> activeColumn = (TableColumn<DynamicCheat, Boolean>) cheatsTableView.getColumns().get(0);
cheatsTableView.getColumns().get(1).setCellValueFactory(new PropertyValueFactory<>("address")); activeColumn.setCellValueFactory(new PropertyValueFactory<>("active"));
cheatsTableView.getColumns().get(2).setCellValueFactory(new PropertyValueFactory<>("value")); activeColumn.setCellFactory((TableColumn<DynamicCheat, Boolean> param) -> new CheckBoxTableCell<>());
TableColumn<DynamicCheat, String> nameColumn = (TableColumn<DynamicCheat, String>) cheatsTableView.getColumns().get(1);
nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
nameColumn.setCellFactory((TableColumn<DynamicCheat, String> param) -> new TextFieldTableCell<>(new DefaultStringConverter()));
TableColumn<DynamicCheat, Integer> addrColumn = (TableColumn<DynamicCheat, Integer>) cheatsTableView.getColumns().get(2);
addrColumn.setCellValueFactory(new PropertyValueFactory<>("address"));
addrColumn.setCellFactory((TableColumn<DynamicCheat, Integer> 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<DynamicCheat, String> exprColumn = (TableColumn<DynamicCheat, String>) cheatsTableView.getColumns().get(3);
exprColumn.setCellValueFactory(new PropertyValueFactory<>("expression"));
exprColumn.setCellFactory((TableColumn<DynamicCheat, String> param) -> new TextFieldTableCell<>(new DefaultStringConverter()));
} }
MetaCheat cheatEngine = null; MetaCheat cheatEngine = null;
@ -303,7 +323,7 @@ public class MetacheatUI {
public int memoryViewColumns; public int memoryViewColumns;
public int memoryViewRows; public int memoryViewRows;
public static Set<MetaCheat.MemoryCell> redrawNodes = new ConcurrentSkipListSet<>(); public static Set<MemoryCell> redrawNodes = new ConcurrentSkipListSet<>();
ScheduledExecutorService animationTimer = null; ScheduledExecutorService animationTimer = null;
ScheduledFuture animationFuture = null; ScheduledFuture animationFuture = null;
StackPane pane = new StackPane(); StackPane pane = new StackPane();
@ -322,7 +342,7 @@ public class MetacheatUI {
int col = (int) (x / MEMORY_BOX_TOTAL_SIZE); int col = (int) (x / MEMORY_BOX_TOTAL_SIZE);
int row = (int) (y / MEMORY_BOX_TOTAL_SIZE); int row = (int) (y / MEMORY_BOX_TOTAL_SIZE);
int addr = cheatEngine.getStartAddress() + row * memoryViewColumns + col; int addr = cheatEngine.getStartAddress() + row * memoryViewColumns + col;
Watch watch = new Watch(addr); Watch watch = new Watch(addr, this);
Label addWatch = new Label("Watch >>"); Label addWatch = new Label("Watch >>");
addWatch.setOnMouseClicked((mouseEvent) -> { addWatch.setOnMouseClicked((mouseEvent) -> {
@ -353,9 +373,9 @@ public class MetacheatUI {
private void processMemoryViewUpdates() { private void processMemoryViewUpdates() {
Application.invokeLater(() -> { Application.invokeLater(() -> {
GraphicsContext context = memoryViewCanvas.getGraphicsContext2D(); GraphicsContext context = memoryViewCanvas.getGraphicsContext2D();
Set<MetaCheat.MemoryCell> draw = new HashSet<>(redrawNodes); Set<MemoryCell> draw = new HashSet<>(redrawNodes);
redrawNodes.clear(); redrawNodes.clear();
draw.stream().forEach((cell) -> { draw.stream().forEach((jace.cheat.MemoryCell cell) -> {
if (showValuesCheckbox.isSelected()) { if (showValuesCheckbox.isSelected()) {
int val = cell.value.get() & 0x0ff; int val = cell.value.get() & 0x0ff;
context.setFill(Color.rgb(val, val, val)); context.setFill(Color.rgb(val, val, val));
@ -401,11 +421,11 @@ public class MetacheatUI {
for (int addr = cheatEngine.getStartAddress(); addr <= cheatEngine.getEndAddress(); addr++) { for (int addr = cheatEngine.getStartAddress(); addr <= cheatEngine.getEndAddress(); addr++) {
int col = (addr - cheatEngine.getStartAddress()) % memoryViewColumns; int col = (addr - cheatEngine.getStartAddress()) % memoryViewColumns;
int row = (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); cell.setRect(col * MEMORY_BOX_TOTAL_SIZE, row * MEMORY_BOX_TOTAL_SIZE, MEMORY_BOX_SIZE, MEMORY_BOX_SIZE);
redrawNodes.add(cell); redrawNodes.add(cell);
} }
MetaCheat.MemoryCell.setListener((prop, oldCell, newCell) -> { MemoryCell.setListener((javafx.beans.value.ObservableValue<? extends jace.cheat.MemoryCell> prop, jace.cheat.MemoryCell oldCell, jace.cheat.MemoryCell newCell) -> {
redrawNodes.add(newCell); redrawNodes.add(newCell);
}); });
@ -446,7 +466,7 @@ public class MetacheatUI {
} }
private Watch addWatch(int addr) { private Watch addWatch(int addr) {
Watch watch = new Watch(addr); Watch watch = new Watch(addr, this);
watch.setPadding(new Insets(5)); watch.setPadding(new Insets(5));
watch.setOpaqueInsets(new Insets(10)); watch.setOpaqueInsets(new Insets(10));
@ -470,100 +490,7 @@ public class MetacheatUI {
} }
private void addCheat(int addr, int val) { 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<Integer> 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);
}
}
} }

View File

@ -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<Integer> 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);
}
}

View File

@ -31,7 +31,7 @@
</children> </children>
</StackPane> </StackPane>
</content></ScrollPane> </content></ScrollPane>
<Accordion> <Accordion centerShape="false" scaleShape="false">
<panes> <panes>
<TitledPane prefHeight="200.0" prefWidth="200.0" text="Search"> <TitledPane prefHeight="200.0" prefWidth="200.0" text="Search">
<content> <content>
@ -198,12 +198,16 @@
</ToolBar> </ToolBar>
</bottom> </bottom>
<center> <center>
<TableView fx:id="cheatsTableView" prefHeight="321.0" prefWidth="207.0" BorderPane.alignment="CENTER"> <TableView fx:id="cheatsTableView" editable="true" prefHeight="321.0" prefWidth="207.0" BorderPane.alignment="CENTER">
<columns> <columns>
<TableColumn prefWidth="35.0" text="On" /> <TableColumn prefWidth="26.0" sortable="false" text="On" />
<TableColumn prefWidth="63.0" text="Address" /> <TableColumn prefWidth="74.0" text="Name" />
<TableColumn prefWidth="127.0" text="Effect" /> <TableColumn prefWidth="50.0" text="Addr" />
<TableColumn prefWidth="84.0" text="Effect" />
</columns> </columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView> </TableView>
</center> </center>
</BorderPane> </BorderPane>