mirror of
https://github.com/badvision/jace.git
synced 2025-02-18 00:30:45 +00:00
Start of MetaCheat revamp effort
This commit is contained in:
parent
e7f731900a
commit
eec3fdbcf7
@ -8,6 +8,7 @@ package jace;
|
|||||||
import jace.core.RAMEvent;
|
import jace.core.RAMEvent;
|
||||||
import jace.core.RAMListener;
|
import jace.core.RAMListener;
|
||||||
import jace.core.Utility;
|
import jace.core.Utility;
|
||||||
|
import jace.ui.MetacheatUI;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@ -16,7 +17,9 @@ import javafx.application.Platform;
|
|||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import javafx.stage.StageStyle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -25,6 +28,7 @@ import javafx.stage.Stage;
|
|||||||
public class JaceApplication extends Application {
|
public class JaceApplication extends Application {
|
||||||
|
|
||||||
static JaceApplication singleton;
|
static JaceApplication singleton;
|
||||||
|
|
||||||
Stage primaryStage;
|
Stage primaryStage;
|
||||||
JaceUIController controller;
|
JaceUIController controller;
|
||||||
|
|
||||||
@ -68,6 +72,30 @@ public class JaceApplication extends Application {
|
|||||||
return singleton;
|
return singleton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stage cheatStage;
|
||||||
|
private MetacheatUI cheatController;
|
||||||
|
|
||||||
|
public MetacheatUI showMetacheat() {
|
||||||
|
if (cheatController == null) {
|
||||||
|
cheatStage = new Stage(StageStyle.DECORATED);
|
||||||
|
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/Metacheat.fxml"));
|
||||||
|
fxmlLoader.setResources(null);
|
||||||
|
try {
|
||||||
|
VBox node = fxmlLoader.load();
|
||||||
|
cheatController = fxmlLoader.getController();
|
||||||
|
Scene s = new Scene(node);
|
||||||
|
cheatStage.setScene(s);
|
||||||
|
cheatStage.setTitle("Jace: MetaCheat");
|
||||||
|
cheatStage.getIcons().add(Utility.loadIcon("woz_figure.gif"));
|
||||||
|
} catch (IOException exception) {
|
||||||
|
throw new RuntimeException(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
cheatStage.show();
|
||||||
|
return cheatController;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param args the command line arguments
|
* @param args the command line arguments
|
||||||
*/
|
*/
|
||||||
@ -76,19 +104,20 @@ public class JaceApplication extends Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the computer and make sure it runs through the expected rom routine for cold boot
|
* Start the computer and make sure it runs through the expected rom routine
|
||||||
|
* for cold boot
|
||||||
*/
|
*/
|
||||||
private void bootWatchdog() {
|
private void bootWatchdog() {
|
||||||
romStarted = false;
|
romStarted = false;
|
||||||
RAMListener startListener = Emulator.computer.getMemory().
|
RAMListener startListener = Emulator.computer.getMemory().
|
||||||
observe(RAMEvent.TYPE.EXECUTE, 0x0FA62, (e)-> {
|
observe(RAMEvent.TYPE.EXECUTE, 0x0FA62, (e) -> {
|
||||||
romStarted = true;
|
romStarted = true;
|
||||||
});
|
});
|
||||||
Emulator.computer.coldStart();
|
Emulator.computer.coldStart();
|
||||||
try {
|
try {
|
||||||
Thread.sleep(250);
|
Thread.sleep(250);
|
||||||
if (!romStarted) {
|
if (!romStarted) {
|
||||||
Logger.getLogger(getClass().getName()).log(Level.WARNING,"Boot not detected, performing a cold start");
|
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Boot not detected, performing a cold start");
|
||||||
Emulator.computer.coldStart();
|
Emulator.computer.coldStart();
|
||||||
}
|
}
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
package jace;
|
package jace;
|
||||||
|
|
||||||
import com.sun.glass.ui.Application;
|
import com.sun.glass.ui.Application;
|
||||||
|
import jace.cheat.MetaCheat;
|
||||||
import jace.core.Card;
|
import jace.core.Card;
|
||||||
import jace.core.Computer;
|
import jace.core.Computer;
|
||||||
import jace.library.MediaCache;
|
import jace.library.MediaCache;
|
||||||
@ -48,6 +49,7 @@ import javafx.scene.paint.Color;
|
|||||||
* @author blurry
|
* @author blurry
|
||||||
*/
|
*/
|
||||||
public class JaceUIController {
|
public class JaceUIController {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private URL location;
|
private URL location;
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ public class Apple2e extends Computer {
|
|||||||
if (motherboard != null && motherboard.isRunning()) {
|
if (motherboard != null && motherboard.isRunning()) {
|
||||||
motherboard.suspend();
|
motherboard.suspend();
|
||||||
}
|
}
|
||||||
motherboard = new Motherboard(this, motherboard);
|
setMotherboard(new Motherboard(this, motherboard));
|
||||||
reconfigure();
|
reconfigure();
|
||||||
motherboard.reconfigure();
|
motherboard.reconfigure();
|
||||||
}
|
}
|
||||||
@ -169,6 +169,10 @@ public class Apple2e extends Computer {
|
|||||||
resume();
|
resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Cheats getActiveCheatEngine() {
|
||||||
|
return activeCheatEngine;
|
||||||
|
}
|
||||||
|
|
||||||
private void insertCard(Class<? extends Card> type, int slot) throws NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
|
private void insertCard(Class<? extends Card> type, int slot) throws NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
|
||||||
if (getMemory().getCard(slot).isPresent()) {
|
if (getMemory().getCard(slot).isPresent()) {
|
||||||
if (getMemory().getCard(slot).get().getClass().equals(type)) {
|
if (getMemory().getCard(slot).get().getClass().equals(type)) {
|
||||||
@ -355,13 +359,12 @@ public class Apple2e extends Computer {
|
|||||||
motherboard.resume();
|
motherboard.resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// public boolean isRunning() {
|
||||||
protected boolean isRunning() {
|
// if (motherboard == null) {
|
||||||
if (motherboard == null) {
|
// return false;
|
||||||
return false;
|
// }
|
||||||
}
|
// return motherboard.isRunning() && !motherboard.isPaused;
|
||||||
return motherboard.isRunning() && !motherboard.isPaused;
|
// }
|
||||||
}
|
|
||||||
private List<RAMListener> hints = new ArrayList<>();
|
private List<RAMListener> hints = new ArrayList<>();
|
||||||
|
|
||||||
ScheduledExecutorService animationTimer = new ScheduledThreadPoolExecutor(1);
|
ScheduledExecutorService animationTimer = new ScheduledThreadPoolExecutor(1);
|
||||||
|
@ -24,6 +24,7 @@ import jace.core.Computer;
|
|||||||
import jace.core.RAM;
|
import jace.core.RAM;
|
||||||
import jace.core.RAMEvent.TYPE;
|
import jace.core.RAMEvent.TYPE;
|
||||||
import jace.state.Stateful;
|
import jace.state.Stateful;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a full implementation of a MOS-65c02 processor, including the BBR,
|
* This is a full implementation of a MOS-65c02 processor, including the BBR,
|
||||||
@ -1036,27 +1037,25 @@ public class MOS65C02 extends CPU {
|
|||||||
}
|
}
|
||||||
int pc = getProgramCounter();
|
int pc = getProgramCounter();
|
||||||
|
|
||||||
// RAM ram = cpu.getMemory();
|
String traceEntry = null;
|
||||||
// int op = 0x00ff & cpu.getMemory().read(pc, false);
|
if (isTraceEnabled() || isLogEnabled() || warnAboutExtendedOpcodes) {
|
||||||
|
traceEntry = getState().toUpperCase() + " " + Integer.toString(pc, 16) + " : " + disassemble();
|
||||||
|
if (isTraceEnabled()) {
|
||||||
|
Logger.getLogger(getClass().getName()).info(traceEntry);
|
||||||
|
}
|
||||||
|
if (isLogEnabled()) {
|
||||||
|
log(traceEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
// This makes it possible to trap the memory read of an opcode, when PC == Address, you know it is executing that opcode.
|
// This makes it possible to trap the memory read of an opcode, when PC == Address, you know it is executing that opcode.
|
||||||
int op = 0x00ff & getMemory().read(pc, TYPE.EXECUTE, true, false);
|
int op = 0x00ff & getMemory().read(pc, TYPE.EXECUTE, true, false);
|
||||||
OPCODE opcode = opcodes[op];
|
OPCODE opcode = opcodes[op];
|
||||||
if (isTraceEnabled() || isLogEnabled() || (warnAboutExtendedOpcodes && opcode != null && opcode.isExtendedOpcode)) {
|
if (traceEntry != null && warnAboutExtendedOpcodes && opcode != null && opcode.isExtendedOpcode) {
|
||||||
String t = getState().toUpperCase() + " " + Integer.toString(pc, 16) + " : " + disassemble();
|
|
||||||
if (warnAboutExtendedOpcodes && opcode != null && opcode.isExtendedOpcode) {
|
|
||||||
System.out.println(">>EXTENDED OPCODE DETECTED " + Integer.toHexString(opcode.code) + "<<");
|
System.out.println(">>EXTENDED OPCODE DETECTED " + Integer.toHexString(opcode.code) + "<<");
|
||||||
System.out.println(t);
|
System.out.println(traceEntry);
|
||||||
if (isLogEnabled()) {
|
if (isLogEnabled()) {
|
||||||
log(">>EXTENDED OPCODE DETECTED " + Integer.toHexString(opcode.code) + "<<");
|
log(">>EXTENDED OPCODE DETECTED " + Integer.toHexString(opcode.code) + "<<");
|
||||||
log(t);
|
log(traceEntry);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (isTraceEnabled()) {
|
|
||||||
System.out.println(t);
|
|
||||||
}
|
|
||||||
if (isLogEnabled()) {
|
|
||||||
log(t);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (opcode == null) {
|
if (opcode == null) {
|
||||||
|
240
src/main/java/jace/cheat/MetaCheat.java
Normal file
240
src/main/java/jace/cheat/MetaCheat.java
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
package jace.cheat;
|
||||||
|
|
||||||
|
import jace.Emulator;
|
||||||
|
import jace.JaceApplication;
|
||||||
|
import jace.core.Computer;
|
||||||
|
import jace.core.RAM;
|
||||||
|
import jace.core.RAMListener;
|
||||||
|
import jace.state.State;
|
||||||
|
import jace.ui.MetacheatUI;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.Property;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
|
||||||
|
public class MetaCheat extends Cheats {
|
||||||
|
|
||||||
|
public static enum SearchType {
|
||||||
|
VALUE, TEXT, CHANGE
|
||||||
|
}
|
||||||
|
|
||||||
|
public static enum SearchChangeType {
|
||||||
|
NO_CHANGE, ANY_CHANGE, LESS, GREATER, AMOUNT
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SearchResult {
|
||||||
|
|
||||||
|
int address;
|
||||||
|
int lastObservedValue = 0;
|
||||||
|
|
||||||
|
private SearchResult(int address, int val) {
|
||||||
|
this.address = address;
|
||||||
|
lastObservedValue = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Integer.toHexString(address) + ": " + lastObservedValue + " (" + Integer.toHexString(lastObservedValue) + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MetacheatUI ui;
|
||||||
|
private int startAddress = 0;
|
||||||
|
private int endAddress = 0x0ffff;
|
||||||
|
private final StringProperty startAddressProperty = new SimpleStringProperty("0");
|
||||||
|
private final StringProperty endAddressProperty = new SimpleStringProperty("FFFF");
|
||||||
|
private boolean byteSized = false;
|
||||||
|
private SearchType searchType = SearchType.VALUE;
|
||||||
|
private SearchChangeType searchChangeType = SearchChangeType.NO_CHANGE;
|
||||||
|
private final BooleanProperty signedProperty = new SimpleBooleanProperty(false);
|
||||||
|
private final StringProperty searchValueProperty = new SimpleStringProperty("0");
|
||||||
|
private final StringProperty changeByProperty = new SimpleStringProperty("0");
|
||||||
|
private final ObservableList<RAMListener> cheatList = FXCollections.observableArrayList();
|
||||||
|
private final ObservableList<SearchResult> resultList = FXCollections.observableArrayList();
|
||||||
|
private final ObservableList<State> snapshotList = FXCollections.observableArrayList();
|
||||||
|
|
||||||
|
public MetaCheat(Computer computer) {
|
||||||
|
super(computer);
|
||||||
|
addNumericValidator(startAddressProperty);
|
||||||
|
addNumericValidator(endAddressProperty);
|
||||||
|
addNumericValidator(searchValueProperty);
|
||||||
|
addNumericValidator(changeByProperty);
|
||||||
|
startAddressProperty.addListener((prop, oldVal, newVal) -> {
|
||||||
|
startAddress = parseInt(newVal);
|
||||||
|
});
|
||||||
|
endAddressProperty.addListener((prop, oldVal, newVal) -> {
|
||||||
|
endAddress = parseInt(newVal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addNumericValidator(StringProperty stringProperty) {
|
||||||
|
stringProperty.addListener((ObservableValue<? extends String> prop, String oldVal, String newVal) -> {
|
||||||
|
if (newVal == null || newVal.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!newVal.matches("(\\+|-)?(x|$)?[0-9a-fA-F]*")) {
|
||||||
|
stringProperty.set("");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public int parseInt(String s) throws NumberFormatException {
|
||||||
|
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]+")) {
|
||||||
|
String upper = s.toUpperCase();
|
||||||
|
boolean positive = true;
|
||||||
|
if (upper.startsWith("-")) {
|
||||||
|
positive = false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < upper.length(); i++) {
|
||||||
|
char c = upper.charAt(i);
|
||||||
|
if ((c >= '0' && c <= '9') || (c >= 'A' & c <= 'F')) {
|
||||||
|
int value = Integer.parseInt(s.substring(i), 16);
|
||||||
|
if (!positive) {
|
||||||
|
value *= -1;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NumberFormatException("Could not interpret int value " + s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void registerListeners() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getDeviceName() {
|
||||||
|
return "MetaCheat";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void detach() {
|
||||||
|
super.detach();
|
||||||
|
ui.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attach() {
|
||||||
|
ui = JaceApplication.getApplication().showMetacheat();
|
||||||
|
ui.registerMetacheatEngine(this);
|
||||||
|
super.attach();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStartAddress() {
|
||||||
|
return startAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEndAddress() {
|
||||||
|
return endAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setByteSized(boolean b) {
|
||||||
|
byteSized = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSearchType(SearchType searchType) {
|
||||||
|
this.searchType = searchType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSearchChangeType(SearchChangeType searchChangeType) {
|
||||||
|
this.searchChangeType = searchChangeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<Boolean> signedProperty() {
|
||||||
|
return signedProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> searchValueProperty() {
|
||||||
|
return searchValueProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> searchChangeByProperty() {
|
||||||
|
return changeByProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableList<RAMListener> getCheats() {
|
||||||
|
return cheatList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableList<SearchResult> getSearchResults() {
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableList<State> getSnapshots() {
|
||||||
|
return snapshotList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> startAddressProperty() {
|
||||||
|
return startAddressProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Property<String> endAddressProperty() {
|
||||||
|
return endAddressProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void newSearch() {
|
||||||
|
RAM memory = Emulator.computer.getMemory();
|
||||||
|
resultList.clear();
|
||||||
|
int compare = parseInt(searchValueProperty.get());
|
||||||
|
for (int i = 0; i < 0x10000; i++) {
|
||||||
|
boolean signed = signedProperty.get();
|
||||||
|
int val
|
||||||
|
= byteSized
|
||||||
|
? signed ? memory.readRaw(i) : memory.readRaw(i) & 0x0ff
|
||||||
|
: signed ? memory.readWordRaw(i) : memory.readWordRaw(i) & 0x0ffff;
|
||||||
|
if (!searchType.equals(SearchType.VALUE) || val == compare) {
|
||||||
|
SearchResult result = new SearchResult(i, val);
|
||||||
|
resultList.add(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void performSearch() {
|
||||||
|
RAM memory = Emulator.computer.getMemory();
|
||||||
|
boolean signed = signedProperty.get();
|
||||||
|
resultList.removeIf((SearchResult result) -> {
|
||||||
|
int val = byteSized
|
||||||
|
? signed ? memory.readRaw(result.address) : memory.readRaw(result.address) & 0x0ff
|
||||||
|
: signed ? memory.readWordRaw(result.address) : memory.readWordRaw(result.address) & 0x0ffff;
|
||||||
|
int last = result.lastObservedValue;
|
||||||
|
result.lastObservedValue = val;
|
||||||
|
switch (searchType) {
|
||||||
|
case VALUE:
|
||||||
|
int compare = parseInt(searchValueProperty.get());
|
||||||
|
return compare != val;
|
||||||
|
case CHANGE:
|
||||||
|
switch (searchChangeType) {
|
||||||
|
case AMOUNT:
|
||||||
|
int amount = parseInt(searchChangeByProperty().getValue());
|
||||||
|
return (val - last) != amount;
|
||||||
|
case GREATER:
|
||||||
|
return val <= last;
|
||||||
|
case ANY_CHANGE:
|
||||||
|
return val == last;
|
||||||
|
case LESS:
|
||||||
|
return val >= last;
|
||||||
|
case NO_CHANGE:
|
||||||
|
return val != last;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TEXT:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -33,6 +33,7 @@ import java.util.logging.Logger;
|
|||||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||||
*/
|
*/
|
||||||
public abstract class CPU extends Device {
|
public abstract class CPU extends Device {
|
||||||
|
private static final Logger LOG = Logger.getLogger(CPU.class.getName());
|
||||||
|
|
||||||
public CPU(Computer computer) {
|
public CPU(Computer computer) {
|
||||||
super(computer);
|
super(computer);
|
||||||
@ -74,14 +75,12 @@ public abstract class CPU extends Device {
|
|||||||
public void dumpTrace() {
|
public void dumpTrace() {
|
||||||
computer.pause();
|
computer.pause();
|
||||||
ArrayList<String> newLog = new ArrayList<>();
|
ArrayList<String> newLog = new ArrayList<>();
|
||||||
ArrayList<String> log = traceLog;
|
ArrayList<String> oldLog = traceLog;
|
||||||
traceLog = newLog;
|
traceLog = newLog;
|
||||||
computer.resume();
|
computer.resume();
|
||||||
System.out.println("Most recent " + traceLength + " instructions:");
|
LOG.log(Level.INFO, "Most recent {0} instructions:", traceLength);
|
||||||
log.stream().forEach((s) -> {
|
oldLog.stream().forEach(LOG::info);
|
||||||
System.out.println(s);
|
oldLog.clear();
|
||||||
});
|
|
||||||
traceLog.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDebug(Debugger d) {
|
public void setDebug(Debugger d) {
|
||||||
|
@ -24,6 +24,9 @@ import jace.config.Reconfigurable;
|
|||||||
import jace.state.StateManager;
|
import jace.state.StateManager;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a very generic stub of a Computer and provides a generic set of
|
* This is a very generic stub of a Computer and provides a generic set of
|
||||||
@ -45,6 +48,7 @@ public abstract class Computer implements Reconfigurable {
|
|||||||
@ConfigurableField(category = "advanced", name = "State management", shortName = "rewind", description = "This enables rewind support, but consumes a lot of memory when active.")
|
@ConfigurableField(category = "advanced", name = "State management", shortName = "rewind", description = "This enables rewind support, but consumes a lot of memory when active.")
|
||||||
public boolean enableStateManager;
|
public boolean enableStateManager;
|
||||||
public final SoundMixer mixer;
|
public final SoundMixer mixer;
|
||||||
|
final private BooleanProperty runningProperty = new SimpleBooleanProperty(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of Computer
|
* Creates a new instance of Computer
|
||||||
@ -63,6 +67,22 @@ public abstract class Computer implements Reconfigurable {
|
|||||||
return motherboard;
|
return motherboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChangeListener<Boolean> runningPropertyListener = (prop, oldVal, newVal) -> runningProperty.set(newVal);
|
||||||
|
public void setMotherboard(Motherboard m) {
|
||||||
|
if (motherboard != null && motherboard.isRunning()) {
|
||||||
|
motherboard.suspend();
|
||||||
|
}
|
||||||
|
motherboard = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty getRunningProperty() {
|
||||||
|
return runningProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRunning() {
|
||||||
|
return getRunningProperty().get();
|
||||||
|
}
|
||||||
|
|
||||||
public void notifyVBLStateChanged(boolean state) {
|
public void notifyVBLStateChanged(boolean state) {
|
||||||
for (Optional<Card> c : getMemory().cards) {
|
for (Optional<Card> c : getMemory().cards) {
|
||||||
c.ifPresent(card -> card.notifyVBLStateChanged(state));
|
c.ifPresent(card -> card.notifyVBLStateChanged(state));
|
||||||
@ -120,10 +140,9 @@ public abstract class Computer implements Reconfigurable {
|
|||||||
category = "general",
|
category = "general",
|
||||||
alternatives = "Full reset;reset emulator",
|
alternatives = "Full reset;reset emulator",
|
||||||
consumeKeyEvent = true,
|
consumeKeyEvent = true,
|
||||||
defaultKeyMapping = {"Ctrl+Shift+Backspace","Ctrl+Shift+Delete"})
|
defaultKeyMapping = {"Ctrl+Shift+Backspace", "Ctrl+Shift+Delete"})
|
||||||
public void invokeColdStart() {
|
public void invokeColdStart() {
|
||||||
if (!romLoaded) {
|
if (!romLoaded) {
|
||||||
System.out.println("Computer booted before rom was loaded");
|
|
||||||
Thread delayedStart = new Thread(() -> {
|
Thread delayedStart = new Thread(() -> {
|
||||||
while (!romLoaded) {
|
while (!romLoaded) {
|
||||||
Thread.yield();
|
Thread.yield();
|
||||||
@ -135,6 +154,7 @@ public abstract class Computer implements Reconfigurable {
|
|||||||
coldStart();
|
coldStart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void coldStart();
|
public abstract void coldStart();
|
||||||
|
|
||||||
@InvokableAction(
|
@InvokableAction(
|
||||||
@ -142,32 +162,33 @@ public abstract class Computer implements Reconfigurable {
|
|||||||
description = "Process user-initatiated reboot (ctrl+apple+reset)",
|
description = "Process user-initatiated reboot (ctrl+apple+reset)",
|
||||||
category = "general",
|
category = "general",
|
||||||
alternatives = "reboot;reset;three-finger-salute",
|
alternatives = "reboot;reset;three-finger-salute",
|
||||||
defaultKeyMapping = {"Ctrl+Ignore Alt+Ignore Meta+Backspace","Ctrl+Ignore Alt+Ignore Meta+Delete"})
|
defaultKeyMapping = {"Ctrl+Ignore Alt+Ignore Meta+Backspace", "Ctrl+Ignore Alt+Ignore Meta+Delete"})
|
||||||
public void invokeWarmStart() {
|
public void invokeWarmStart() {
|
||||||
warmStart();
|
warmStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void warmStart();
|
public abstract void warmStart();
|
||||||
|
|
||||||
public Keyboard getKeyboard() {
|
public Keyboard getKeyboard() {
|
||||||
return this.keyboard;
|
return this.keyboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract boolean isRunning();
|
|
||||||
|
|
||||||
protected abstract void doPause();
|
protected abstract void doPause();
|
||||||
|
|
||||||
protected abstract void doResume();
|
protected abstract void doResume();
|
||||||
|
|
||||||
@InvokableAction(name = "Pause", description = "Stops the computer, allowing reconfiguration of core elements", alternatives = "freeze;halt", defaultKeyMapping = {"meta+pause","alt+pause"})
|
@InvokableAction(name = "Pause", description = "Stops the computer, allowing reconfiguration of core elements", alternatives = "freeze;halt", defaultKeyMapping = {"meta+pause", "alt+pause"})
|
||||||
public boolean pause() {
|
public boolean pause() {
|
||||||
boolean result = isRunning();
|
boolean result = getRunningProperty().get();
|
||||||
doPause();
|
doPause();
|
||||||
|
getRunningProperty().set(false);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@InvokableAction(name = "Resume", description = "Resumes the computer if it was previously paused", alternatives = "unpause;unfreeze;resume", defaultKeyMapping = {"meta+shift+pause","alt+shift+pause"})
|
@InvokableAction(name = "Resume", description = "Resumes the computer if it was previously paused", alternatives = "unpause;unfreeze;resume", defaultKeyMapping = {"meta+shift+pause", "alt+shift+pause"})
|
||||||
public void resume() {
|
public void resume() {
|
||||||
doResume();
|
doResume();
|
||||||
|
getRunningProperty().set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -20,6 +20,8 @@ package jace.core;
|
|||||||
|
|
||||||
import jace.state.Stateful;
|
import jace.state.Stateful;
|
||||||
import jace.config.Reconfigurable;
|
import jace.config.Reconfigurable;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Device is a very simple abstraction of any emulation component. A device
|
* Device is a very simple abstraction of any emulation component. A device
|
||||||
@ -37,6 +39,8 @@ import jace.config.Reconfigurable;
|
|||||||
@Stateful
|
@Stateful
|
||||||
public abstract class Device implements Reconfigurable {
|
public abstract class Device implements Reconfigurable {
|
||||||
protected Computer computer;
|
protected Computer computer;
|
||||||
|
private Device() {
|
||||||
|
}
|
||||||
public Device(Computer computer) {
|
public Device(Computer computer) {
|
||||||
this.computer = computer;
|
this.computer = computer;
|
||||||
}
|
}
|
||||||
@ -45,10 +49,14 @@ public abstract class Device implements Reconfigurable {
|
|||||||
@Stateful
|
@Stateful
|
||||||
private int waitCycles = 0;
|
private int waitCycles = 0;
|
||||||
@Stateful
|
@Stateful
|
||||||
private boolean run = true;
|
private final BooleanProperty run = new SimpleBooleanProperty(true);
|
||||||
@Stateful
|
@Stateful
|
||||||
public boolean isPaused = false;
|
public boolean isPaused = false;
|
||||||
|
|
||||||
|
public BooleanProperty getRunningProperty() {
|
||||||
|
return run;
|
||||||
|
}
|
||||||
|
|
||||||
public void addWaitCycles(int wait) {
|
public void addWaitCycles(int wait) {
|
||||||
waitCycles += wait;
|
waitCycles += wait;
|
||||||
}
|
}
|
||||||
@ -65,7 +73,7 @@ public abstract class Device implements Reconfigurable {
|
|||||||
waitCycles--;
|
waitCycles--;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!run) {
|
if (!run.get()) {
|
||||||
// System.out.println("Device stopped: " + getName());
|
// System.out.println("Device stopped: " + getName());
|
||||||
isPaused = true;
|
isPaused = true;
|
||||||
return;
|
return;
|
||||||
@ -82,13 +90,13 @@ public abstract class Device implements Reconfigurable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRunning() {
|
public boolean isRunning() {
|
||||||
return run;
|
return run.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setRun(boolean run) {
|
public synchronized void setRun(boolean run) {
|
||||||
// System.out.println(Thread.currentThread().getName() + (run ? " resuming " : " suspending ")+ getDeviceName());
|
// System.out.println(Thread.currentThread().getName() + (run ? " resuming " : " suspending ")+ getDeviceName());
|
||||||
isPaused = false;
|
isPaused = false;
|
||||||
this.run = run;
|
this.run.set(run);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract String getDeviceName();
|
protected abstract String getDeviceName();
|
||||||
|
@ -44,11 +44,6 @@ public class PlaybackEngine extends Computer {
|
|||||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isRunning() {
|
|
||||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPause() {
|
protected void doPause() {
|
||||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
@ -93,7 +88,7 @@ public class PlaybackEngine extends Computer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isRunning() {
|
public boolean isRunning() {
|
||||||
return motherboard.isRunning();
|
return motherboard.isRunning();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
275
src/main/java/jace/ui/MetacheatUI.java
Normal file
275
src/main/java/jace/ui/MetacheatUI.java
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
package jace.ui;
|
||||||
|
|
||||||
|
import com.sun.glass.ui.Application;
|
||||||
|
import jace.Emulator;
|
||||||
|
import jace.cheat.MetaCheat;
|
||||||
|
import jace.cheat.MetaCheat.SearchChangeType;
|
||||||
|
import jace.cheat.MetaCheat.SearchType;
|
||||||
|
import jace.core.RAMEvent;
|
||||||
|
import jace.core.RAMListener;
|
||||||
|
import jace.state.State;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.CheckBox;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ListView;
|
||||||
|
import javafx.scene.control.RadioButton;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.control.SingleSelectionModel;
|
||||||
|
import javafx.scene.control.Tab;
|
||||||
|
import javafx.scene.control.TabPane;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.control.Toggle;
|
||||||
|
import javafx.scene.control.ToggleGroup;
|
||||||
|
import javafx.scene.layout.TilePane;
|
||||||
|
|
||||||
|
public class MetacheatUI {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button pauseButton;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField searchStartAddressField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField searchEndAddressField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ScrollPane memoryViewPane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TabPane searchTypesTabPane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField searchValueField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private RadioButton searchTypeByte;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ToggleGroup searchSize;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private RadioButton searchTypeWord;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox searchTypeSigned;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private RadioButton searchChangeNoneOption;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ToggleGroup changeSearchType;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private RadioButton searchChangeAnyOption;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private RadioButton searchChangeLessOption;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private RadioButton searchChangeGreaterOption;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private RadioButton searchChangeByOption;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField searchChangeByField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label searchStatusLabel;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ListView<MetaCheat.SearchResult> searchResultsListView;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TilePane watchesPane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ListView<State> snapshotsListView;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableView<RAMListener> cheatsTableView;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void addCheat(ActionEvent event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void createSnapshot(ActionEvent event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void deleteCheat(ActionEvent event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void deleteSnapshot(ActionEvent event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void diffSnapshots(ActionEvent event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void loadCheats(ActionEvent event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void newSearch(ActionEvent event) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
cheatEngine.newSearch();
|
||||||
|
updateSearchStats();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void pauseClicked(ActionEvent event) {
|
||||||
|
Application.invokeLater(() -> {
|
||||||
|
if (Emulator.computer.isRunning()) {
|
||||||
|
Emulator.computer.pause();
|
||||||
|
} else {
|
||||||
|
Emulator.computer.resume();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void saveCheats(ActionEvent event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void search(ActionEvent event) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
cheatEngine.performSearch();
|
||||||
|
updateSearchStats();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void zoomIn(ActionEvent event) {
|
||||||
|
changeZoom(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void zoomOut(ActionEvent event) {
|
||||||
|
changeZoom(-0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void initialize() {
|
||||||
|
assert pauseButton != null : "fx:id=\"pauseButton\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchStartAddressField != null : "fx:id=\"searchStartAddressField\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchEndAddressField != null : "fx:id=\"searchEndAddressField\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert memoryViewPane != null : "fx:id=\"memoryViewPane\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchTypesTabPane != null : "fx:id=\"searchTypesTabPane\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchValueField != null : "fx:id=\"searchValueField\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchTypeByte != null : "fx:id=\"searchTypeByte\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchSize != null : "fx:id=\"searchSize\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchTypeWord != null : "fx:id=\"searchTypeWord\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchTypeSigned != null : "fx:id=\"searchTypeSigned\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchChangeNoneOption != null : "fx:id=\"searchChangeNoneOption\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert changeSearchType != null : "fx:id=\"changeSearchType\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchChangeAnyOption != null : "fx:id=\"searchChangeAnyOption\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchChangeLessOption != null : "fx:id=\"searchChangeLessOption\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchChangeGreaterOption != null : "fx:id=\"searchChangeGreaterOption\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchChangeByOption != null : "fx:id=\"searchChangeByOption\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchChangeByField != null : "fx:id=\"searchChangeByField\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchStatusLabel != null : "fx:id=\"searchStatusLabel\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert searchResultsListView != null : "fx:id=\"searchResultsListView\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert watchesPane != null : "fx:id=\"watchesPane\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert snapshotsListView != null : "fx:id=\"snapshotsListView\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
assert cheatsTableView != null : "fx:id=\"cheatsTableView\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
||||||
|
|
||||||
|
Emulator.computer.getRunningProperty().addListener((val, oldVal, newVal) -> pauseButton.setText(newVal ? "Pause" : "Resume"));
|
||||||
|
|
||||||
|
searchTypesTabPane.getTabs().get(0).setUserData(SearchType.VALUE);
|
||||||
|
searchTypesTabPane.getTabs().get(1).setUserData(SearchType.CHANGE);
|
||||||
|
searchTypesTabPane.getTabs().get(2).setUserData(SearchType.TEXT);
|
||||||
|
searchTypesTabPane.getSelectionModel().selectedItemProperty().addListener((prop, oldVal, newVal) -> {
|
||||||
|
System.out.println("Tab selected: " + newVal.getText());
|
||||||
|
if (cheatEngine != null) {
|
||||||
|
cheatEngine.setSearchType((SearchType) newVal.getUserData());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchChangeAnyOption.setUserData(SearchChangeType.ANY_CHANGE);
|
||||||
|
searchChangeByOption.setUserData(SearchChangeType.AMOUNT);
|
||||||
|
searchChangeGreaterOption.setUserData(SearchChangeType.GREATER);
|
||||||
|
searchChangeLessOption.setUserData(SearchChangeType.LESS);
|
||||||
|
searchChangeNoneOption.setUserData(SearchChangeType.NO_CHANGE);
|
||||||
|
changeSearchType.selectedToggleProperty().addListener((ObservableValue<? extends Toggle> val, Toggle oldVal, Toggle newVal) -> {
|
||||||
|
if (cheatEngine != null) {
|
||||||
|
cheatEngine.setSearchChangeType((SearchChangeType) newVal.getUserData());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchTypeByte.setUserData(true);
|
||||||
|
searchTypeWord.setUserData(false);
|
||||||
|
searchSize.selectedToggleProperty().addListener((ObservableValue<? extends Toggle> val, Toggle oldVal, Toggle newVal) -> {
|
||||||
|
if (cheatEngine != null) {
|
||||||
|
cheatEngine.setByteSized((boolean) newVal.getUserData());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaCheat cheatEngine = null;
|
||||||
|
|
||||||
|
public void registerMetacheatEngine(MetaCheat engine) {
|
||||||
|
cheatEngine = engine;
|
||||||
|
|
||||||
|
cheatsTableView.setItems(cheatEngine.getCheats());
|
||||||
|
searchResultsListView.setItems(cheatEngine.getSearchResults());
|
||||||
|
snapshotsListView.setItems(cheatEngine.getSnapshots());
|
||||||
|
searchTypeSigned.selectedProperty().bindBidirectional(cheatEngine.signedProperty());
|
||||||
|
searchStartAddressField.textProperty().bindBidirectional(cheatEngine.startAddressProperty());
|
||||||
|
searchEndAddressField.textProperty().bindBidirectional(cheatEngine.endAddressProperty());
|
||||||
|
searchValueField.textProperty().bindBidirectional(cheatEngine.searchValueProperty());
|
||||||
|
searchChangeByField.textProperty().bindBidirectional(cheatEngine.searchChangeByProperty());
|
||||||
|
|
||||||
|
engine.addCheat(RAMEvent.TYPE.ANY, this::processMemoryEvent, 0, 0x0ffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void changeZoom(double amount) {
|
||||||
|
double zoom = memoryViewPane.getScaleX();
|
||||||
|
zoom += amount;
|
||||||
|
memoryViewPane.setScaleX(zoom);
|
||||||
|
memoryViewPane.setScaleY(zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processMemoryEvent(RAMEvent e) {
|
||||||
|
if (e.getAddress() < cheatEngine.getStartAddress() || e.getAddress() > cheatEngine.getEndAddress()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void detach() {
|
||||||
|
cheatsTableView.setItems(FXCollections.emptyObservableList());
|
||||||
|
searchResultsListView.setItems(FXCollections.emptyObservableList());
|
||||||
|
searchTypeSigned.selectedProperty().unbind();
|
||||||
|
searchStartAddressField.textProperty().unbind();
|
||||||
|
searchStartAddressField.textProperty().unbind();
|
||||||
|
searchEndAddressField.textProperty().unbind();
|
||||||
|
searchValueField.textProperty().unbind();
|
||||||
|
searchChangeByField.textProperty().unbind();
|
||||||
|
cheatEngine = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSearchStats() {
|
||||||
|
int size = cheatEngine.getSearchResults().size();
|
||||||
|
searchStatusLabel.setText(size + (size == 1 ? " result" : " results") + " found.");
|
||||||
|
}
|
||||||
|
}
|
208
src/main/resources/fxml/Metacheat.fxml
Normal file
208
src/main/resources/fxml/Metacheat.fxml
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.text.*?>
|
||||||
|
<?import javafx.geometry.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="492.0" prefWidth="702.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jace.ui.MetacheatUI">
|
||||||
|
<children>
|
||||||
|
<ToolBar prefHeight="40.0" prefWidth="200.0" VBox.vgrow="NEVER">
|
||||||
|
<items>
|
||||||
|
<Button fx:id="pauseButton" mnemonicParsing="false" onAction="#pauseClicked" text="Pause" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#zoomIn" text="Zoom in" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#zoomOut" text="Zoom out" />
|
||||||
|
<Label text="Start:" />
|
||||||
|
<TextField fx:id="searchStartAddressField" prefHeight="26.0" prefWidth="50.0" text="0000" />
|
||||||
|
<Label text="End:" />
|
||||||
|
<TextField fx:id="searchEndAddressField" prefHeight="26.0" prefWidth="48.0" text="FFFF" />
|
||||||
|
</items>
|
||||||
|
</ToolBar>
|
||||||
|
<SplitPane dividerPositions="0.6304347826086957" prefHeight="363.0" prefWidth="600.0" VBox.vgrow="ALWAYS">
|
||||||
|
<items>
|
||||||
|
<ScrollPane fx:id="memoryViewPane" prefHeight="450.0" prefWidth="391.0" />
|
||||||
|
<Accordion>
|
||||||
|
<panes>
|
||||||
|
<TitledPane prefHeight="200.0" prefWidth="200.0" text="Search">
|
||||||
|
<content>
|
||||||
|
<VBox prefHeight="200.0" prefWidth="100.0">
|
||||||
|
<children>
|
||||||
|
<TabPane fx:id="searchTypesTabPane" prefHeight="147.0" prefWidth="233.0" tabClosingPolicy="UNAVAILABLE" VBox.vgrow="NEVER">
|
||||||
|
<tabs>
|
||||||
|
<Tab text="Value">
|
||||||
|
<content>
|
||||||
|
<VBox prefHeight="200.0" prefWidth="100.0">
|
||||||
|
<children>
|
||||||
|
<FlowPane prefHeight="43.0" prefWidth="233.0">
|
||||||
|
<children>
|
||||||
|
<Label text="Search for value:">
|
||||||
|
<padding>
|
||||||
|
<Insets right="3.0" />
|
||||||
|
</padding>
|
||||||
|
</Label>
|
||||||
|
<TextField fx:id="searchValueField" prefHeight="26.0" prefWidth="75.0" />
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets top="3.0" />
|
||||||
|
</padding>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="4.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</FlowPane>
|
||||||
|
<Separator prefWidth="200.0" />
|
||||||
|
<Label text="These affect Change searches also.">
|
||||||
|
<font>
|
||||||
|
<Font name="System Italic" size="13.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<HBox prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<RadioButton fx:id="searchTypeByte" mnemonicParsing="false" selected="true" text="Byte">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets left="2.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
<toggleGroup>
|
||||||
|
<ToggleGroup fx:id="searchSize" />
|
||||||
|
</toggleGroup>
|
||||||
|
</RadioButton>
|
||||||
|
<RadioButton fx:id="searchTypeWord" mnemonicParsing="false" text="Word" toggleGroup="$searchSize">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets />
|
||||||
|
</HBox.margin>
|
||||||
|
<padding>
|
||||||
|
<Insets left="2.0" right="2.0" />
|
||||||
|
</padding>
|
||||||
|
</RadioButton>
|
||||||
|
<CheckBox fx:id="searchTypeSigned" mnemonicParsing="false" text="Signed" />
|
||||||
|
</children>
|
||||||
|
<opaqueInsets>
|
||||||
|
<Insets />
|
||||||
|
</opaqueInsets>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="3.0" top="3.0" />
|
||||||
|
</padding>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets left="5.0" />
|
||||||
|
</padding>
|
||||||
|
</VBox>
|
||||||
|
</content>
|
||||||
|
</Tab>
|
||||||
|
<Tab text="Change">
|
||||||
|
<content>
|
||||||
|
<VBox nodeOrientation="LEFT_TO_RIGHT" prefHeight="200.0" prefWidth="100.0">
|
||||||
|
<children>
|
||||||
|
<RadioButton fx:id="searchChangeNoneOption" mnemonicParsing="false" text="No changes since last search">
|
||||||
|
<opaqueInsets>
|
||||||
|
<Insets />
|
||||||
|
</opaqueInsets>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="3.0" top="2.0" />
|
||||||
|
</padding>
|
||||||
|
<toggleGroup>
|
||||||
|
<ToggleGroup fx:id="changeSearchType" />
|
||||||
|
</toggleGroup>
|
||||||
|
</RadioButton>
|
||||||
|
<RadioButton fx:id="searchChangeAnyOption" mnemonicParsing="false" text="Any changes since last search" toggleGroup="$changeSearchType">
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="3.0" />
|
||||||
|
</padding>
|
||||||
|
</RadioButton>
|
||||||
|
<RadioButton fx:id="searchChangeLessOption" mnemonicParsing="false" text="Less than last search" toggleGroup="$changeSearchType">
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="3.0" />
|
||||||
|
</padding>
|
||||||
|
</RadioButton>
|
||||||
|
<RadioButton fx:id="searchChangeGreaterOption" mnemonicParsing="false" text="Greater than last search" toggleGroup="$changeSearchType" />
|
||||||
|
<FlowPane prefHeight="200.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<RadioButton fx:id="searchChangeByOption" mnemonicParsing="false" text="Change by: " toggleGroup="$changeSearchType" />
|
||||||
|
<TextField fx:id="searchChangeByField" prefHeight="26.0" prefWidth="76.0" />
|
||||||
|
</children>
|
||||||
|
</FlowPane>
|
||||||
|
</children>
|
||||||
|
<opaqueInsets>
|
||||||
|
<Insets />
|
||||||
|
</opaqueInsets>
|
||||||
|
<padding>
|
||||||
|
<Insets left="2.0" />
|
||||||
|
</padding>
|
||||||
|
</VBox>
|
||||||
|
</content>
|
||||||
|
</Tab>
|
||||||
|
<Tab text="Text" />
|
||||||
|
</tabs>
|
||||||
|
</TabPane>
|
||||||
|
<ToolBar prefHeight="40.0" prefWidth="200.0" VBox.vgrow="NEVER">
|
||||||
|
<items>
|
||||||
|
<Button mnemonicParsing="false" onAction="#newSearch" text="New Search" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#search" text="Search" />
|
||||||
|
</items>
|
||||||
|
</ToolBar>
|
||||||
|
<HBox prefHeight="19.0" prefWidth="235.0">
|
||||||
|
<children>
|
||||||
|
<Label text="Results:" HBox.hgrow="NEVER" />
|
||||||
|
<Label fx:id="searchStatusLabel" prefHeight="16.0" prefWidth="181.0" textAlignment="RIGHT" wrapText="true" HBox.hgrow="ALWAYS" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<ListView fx:id="searchResultsListView" prefHeight="125.0" prefWidth="233.0" VBox.vgrow="ALWAYS" />
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</content>
|
||||||
|
</TitledPane>
|
||||||
|
<TitledPane animated="false" text="Watches">
|
||||||
|
<content>
|
||||||
|
<TilePane fx:id="watchesPane" prefHeight="200.0" prefWidth="200.0" />
|
||||||
|
</content>
|
||||||
|
</TitledPane>
|
||||||
|
<TitledPane animated="false" text="Snapshots">
|
||||||
|
<content>
|
||||||
|
<BorderPane minWidth="-Infinity">
|
||||||
|
<bottom>
|
||||||
|
<ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
|
||||||
|
<items>
|
||||||
|
<Button mnemonicParsing="false" onAction="#createSnapshot" text="Create" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#deleteSnapshot" text="Delete" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#diffSnapshots" text="Diff" />
|
||||||
|
</items>
|
||||||
|
</ToolBar>
|
||||||
|
</bottom>
|
||||||
|
<center>
|
||||||
|
<ListView fx:id="snapshotsListView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
|
||||||
|
</center>
|
||||||
|
</BorderPane>
|
||||||
|
</content>
|
||||||
|
</TitledPane>
|
||||||
|
<TitledPane animated="false" text="Cheats">
|
||||||
|
<content>
|
||||||
|
<BorderPane prefHeight="200.0" prefWidth="200.0">
|
||||||
|
<bottom>
|
||||||
|
<ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
|
||||||
|
<items>
|
||||||
|
<Button mnemonicParsing="false" onAction="#addCheat" text="Add" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#deleteCheat" text="Delete" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#loadCheats" text="Load" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#saveCheats" text="Save" />
|
||||||
|
</items>
|
||||||
|
</ToolBar>
|
||||||
|
</bottom>
|
||||||
|
<center>
|
||||||
|
<TableView fx:id="cheatsTableView" prefHeight="321.0" prefWidth="207.0" BorderPane.alignment="CENTER">
|
||||||
|
<columns>
|
||||||
|
<TableColumn prefWidth="35.0" text="On" />
|
||||||
|
<TableColumn prefWidth="63.0" text="Address" />
|
||||||
|
<TableColumn prefWidth="127.0" text="Effect" />
|
||||||
|
</columns>
|
||||||
|
</TableView>
|
||||||
|
</center>
|
||||||
|
</BorderPane>
|
||||||
|
</content>
|
||||||
|
</TitledPane>
|
||||||
|
</panes>
|
||||||
|
</Accordion>
|
||||||
|
</items>
|
||||||
|
</SplitPane>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
Loading…
x
Reference in New Issue
Block a user