Added upgrade automation which imports saved games and replaces disk copy

This commit is contained in:
Brendan Robert 2017-12-29 13:46:27 -06:00
parent 4376bb5378
commit 9cfe66d68b
7 changed files with 286 additions and 109 deletions

View File

@ -18,10 +18,10 @@
*/
package jace;
import com.sun.javafx.tk.quantum.OverlayWarning;
import jace.apple2e.MOS65C02;
import jace.apple2e.RAM128k;
import jace.apple2e.SoftSwitches;
import jace.config.ConfigurableField;
import jace.config.ConfigurationUIController;
import jace.config.InvokableAction;
import jace.config.Reconfigurable;
@ -82,6 +82,12 @@ public class EmulatorUILogic implements Reconfigurable {
};
}
@ConfigurableField(
category = "General",
name = "Show Drives"
)
public boolean showDrives = false;
public static void updateCPURegisters(MOS65C02 cpu) {
// DebuggerPanel debuggerPanel = Emulator.getFrame().getDebuggerPanel();
// debuggerPanel.valueA.setText(Integer.toHexString(cpu.A));

View File

@ -191,10 +191,13 @@ public class JaceUIController {
private List<MediaConsumer> getMediaConsumers() {
List<MediaConsumer> consumers = new ArrayList<>();
for (Optional<Card> card : computer.memory.getAllCards()) {
card.filter(c -> c instanceof MediaConsumerParent).ifPresent(parent -> {
consumers.addAll(Arrays.asList(((MediaConsumerParent) parent).getConsumers()));
});
consumers.add(Emulator.computer.getUpgradeHandler());
if (Emulator.logic.showDrives) {
for (Optional<Card> card : computer.memory.getAllCards()) {
card.filter(c -> c instanceof MediaConsumerParent).ifPresent(parent -> {
consumers.addAll(Arrays.asList(((MediaConsumerParent) parent).getConsumers()));
});
}
}
return consumers;
}
@ -254,24 +257,25 @@ public class JaceUIController {
public void removeMouseListener(EventHandler<MouseEvent> handler) {
appleScreen.removeEventHandler(MouseEvent.ANY, handler);
}
Label currentNotification = null;
public void displayNotification(String message) {
Label oldNotification = currentNotification;
Label notification = new Label(message);
currentNotification = notification;
notification.setEffect(new DropShadow(2.0, Color.BLACK));
notification.setTextFill(Color.WHITE);
notification.setBackground(new Background(new BackgroundFill(Color.rgb(0,0,80, 0.7), new CornerRadii(5.0), new Insets(-5.0))));
Application.invokeLater(() -> {
notification.setBackground(new Background(new BackgroundFill(Color.rgb(0, 0, 80, 0.7), new CornerRadii(5.0), new Insets(-5.0))));
Application.invokeLater(() -> {
stackPane.getChildren().remove(oldNotification);
stackPane.getChildren().add(notification);
});
notificationExecutor.schedule(()->{
Application.invokeLater(() -> {
notificationExecutor.schedule(() -> {
Application.invokeLater(() -> {
stackPane.getChildren().remove(notification);
});
});
}, 4, TimeUnit.SECONDS);
}
}

View File

@ -6,7 +6,6 @@
package jace;
import jace.config.Configuration;
import jace.core.RAM;
import jace.core.RAMEvent;
import jace.core.RAMListener;
import jace.core.Utility;
@ -17,17 +16,10 @@ import jace.hardware.CardRamworks;
import jace.hardware.PassportMidiInterface;
import jace.hardware.massStorage.CardMassStorage;
import jace.lawless.LawlessHacks;
import jace.lawless.LawlessImageTool;
import jace.lawless.LawlessVideo;
import jace.library.DiskType;
import jace.library.MediaEntry;
import jace.library.MediaEntry.MediaFile;
import jace.ui.MetacheatUI;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.StandardCopyOption;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
@ -168,87 +160,6 @@ public class LawlessLegends extends Application {
Emulator.computer.cheatEngine.setValue(LawlessHacks.class);
Configuration.buildTree();
Emulator.computer.reconfigure();
RAM memory = Emulator.computer.memory;
// Insert game disk image
MediaEntry e1 = new MediaEntry();
e1.author = "8 Bit Bunch";
e1.name = "Lawless Legends";
e1.type = DiskType.LARGE;
MediaFile f1 = new MediaEntry.MediaFile();
f1.path = getGamePath("game.2mg");
if (f1.path != null && f1.path.exists()) {
memory.getCard(7).ifPresent(card -> {
try {
((CardMassStorage) card).currentDrive.insertMedia(e1, f1);
} catch (IOException ex) {
Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, null, ex);
}
});
}
// Insert utility disk image
MediaEntry e2 = new MediaEntry();
e2.author = "8 Bit Bunch";
e2.name = "Lawless Legends Utilities";
e2.type = DiskType.FLOPPY140_DO;
MediaFile f2 = new MediaEntry.MediaFile();
f2.path = getGamePath("utilities.dsk");
if (f2.path != null && f2.path.exists()) {
memory.getCard(6).ifPresent(card -> {
try {
((CardDiskII) card).getConsumers()[0].insertMedia(e2, f2);
} catch (IOException ex) {
Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, null, ex);
}
});
}
}
private File getGamePath(String filename) {
File base = getApplicationStoragePath();
File target = new File(base, filename);
if (!target.exists()) {
copyResource(filename, target);
}
return target;
}
private File getApplicationStoragePath() {
String path = System.getenv("APPDATA");
if (path == null) {
path = System.getProperty("user.home");
}
if (path == null) {
path = ".";
}
File base = new File(path);
File appPath = new File(base, "lawless-legends");
appPath.mkdirs();
return appPath;
}
private void copyResource(String filename, File target) {
File localResource = new File(".", filename);
InputStream in = null;
if (localResource.exists()) {
try {
in = new FileInputStream(localResource);
} catch (FileNotFoundException ex) {
Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
in = getClass().getClassLoader().getResourceAsStream("jace/data/" + filename);
}
if (in != null) {
try {
java.nio.file.Files.copy(in, target.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, "Unable to find resource {0}", filename);
}
((LawlessImageTool) Emulator.computer.getUpgradeHandler()).loadGame();
}
}

View File

@ -18,6 +18,7 @@
*/
package jace.core;
import jace.Emulator;
import jace.apple2e.SoftSwitches;
import jace.config.InvokableAction;
import jace.config.Reconfigurable;
@ -68,6 +69,7 @@ public class Keyboard implements Reconfigurable {
return "kbd";
}
static byte currentKey = 0;
public boolean shiftPressed = false;
public static void clearStrobe() {
currentKey = (byte) (currentKey & 0x07f);
@ -102,6 +104,7 @@ public class Keyboard implements Reconfigurable {
registerKeyHandler(new KeyHandler(code) {
@Override
public boolean handleKeyUp(KeyEvent e) {
Emulator.computer.getKeyboard().shiftPressed = e.isShiftDown();
if (action == null || !action.notifyOnRelease()) {
return false;
}
@ -125,6 +128,7 @@ public class Keyboard implements Reconfigurable {
@Override
public boolean handleKeyDown(KeyEvent e) {
// System.out.println("Key down: "+method.toString());
Emulator.computer.getKeyboard().shiftPressed = e.isShiftDown();
Object returnValue = null;
try {
if (method.getParameterCount() > 0) {
@ -248,6 +252,7 @@ public class Keyboard implements Reconfigurable {
default:
}
Emulator.computer.getKeyboard().shiftPressed = e.isShiftDown();
if (e.isShiftDown()) {
c = fixShiftedChar(c);
}

View File

@ -33,18 +33,24 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
/**
* This is a set of helper functions which do not belong anywhere else.
* Functions vary from introspection, discovery, and string/pattern matching.
@ -52,7 +58,9 @@ import javafx.scene.paint.Color;
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public class Utility {
static Reflections reflections = new Reflections("jace");
public static Set<Class> findAllSubclasses(Class clazz) {
return reflections.getSubTypesOf(clazz);
}
@ -135,6 +143,7 @@ public class Utility {
}
private static boolean isHeadless = false;
public static void setHeadlessMode(boolean headless) {
isHeadless = headless;
}
@ -142,7 +151,7 @@ public class Utility {
public static boolean isHeadlessMode() {
return isHeadless;
}
public static Optional<Image> loadIcon(String filename) {
if (isHeadless) {
return Optional.empty();
@ -181,6 +190,20 @@ public class Utility {
return Optional.of(label);
}
public static void confirm(String title, String message, Runnable accept) {
Platform.runLater(() -> {
Alert confirm = new Alert(Alert.AlertType.CONFIRMATION);
confirm.setContentText(message);
confirm.setTitle(title);
Optional<ButtonType> response = confirm.showAndWait();
response.ifPresent(b -> {
if (b.getButtonData().isDefaultButton()) {
(new Thread(accept)).start();
}
});
});
}
// public static void runModalProcess(String title, final Runnable runnable) {
//// final JDialog frame = new JDialog(Emulator.getFrame());
// final JProgressBar progressBar = new JProgressBar();
@ -201,7 +224,6 @@ public class Utility {
// frame.dispose();
// }).start();
// }
public static class RankingComparator implements Comparator<String> {
String match;

View File

@ -1,11 +1,14 @@
package jace.lawless;
import jace.Emulator;
import jace.apple2e.Apple2e;
import jace.apple2e.RAM128k;
import jace.apple2e.SoftSwitches;
import jace.apple2e.VideoNTSC;
import jace.config.ConfigurableField;
import jace.core.Card;
import jace.core.Video;
import jace.library.MediaConsumer;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
@ -23,7 +26,8 @@ public class LawlessComputer extends Apple2e {
byte[] bootScreen = null;
boolean performedBootAnimation = false;
LawlessImageTool gameDiskHandler = new LawlessImageTool();
public LawlessComputer() {
super();
// Fill text page 1 with spaces
@ -118,7 +122,7 @@ public class LawlessComputer extends Apple2e {
}
}
List<Runnable> vblCallbacks = Collections.synchronizedList(new ArrayList<Runnable>());
List<Runnable> vblCallbacks = Collections.synchronizedList(new ArrayList<>());
public void waitForVBL() throws InterruptedException {
waitForVBL(0);
@ -141,8 +145,7 @@ public class LawlessComputer extends Apple2e {
public void notifyVBLStateChanged(boolean state) {
super.notifyVBLStateChanged(state);
if (state) {
Runnable r;
while (!vblCallbacks.isEmpty()) {
while (vblCallbacks != null && !vblCallbacks.isEmpty()) {
vblCallbacks.remove(0).run();
}
}
@ -178,4 +181,7 @@ public class LawlessComputer extends Apple2e {
return bootScreen;
}
public MediaConsumer getUpgradeHandler() {
return gameDiskHandler;
}
}

View File

@ -0,0 +1,223 @@
package jace.lawless;
import jace.Emulator;
import jace.LawlessLegends;
import jace.apple2e.RAM128k;
import jace.core.Keyboard;
import jace.core.RAM;
import jace.core.Utility;
import jace.hardware.massStorage.CardMassStorage;
import jace.library.DiskType;
import jace.library.MediaConsumer;
import jace.library.MediaEntry;
import jace.library.MediaEntry.MediaFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.StandardCopyOption;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.scene.control.Label;
/**
*
* @author brobert
*/
public class LawlessImageTool implements MediaConsumer {
Optional<Label> icon = Optional.empty();
MediaEntry gameMediaEntry;
MediaFile gameMediaFile;
public LawlessImageTool() {
icon = Utility.loadIconLabel("game_icon.png");
icon.ifPresent(i -> {
i.setText("Upgrade Game");
});
}
@Override
public Optional<Label> getIcon() {
return icon;
}
@Override
public void setIcon(Optional<Label> i) {
icon = i;
}
@Override
public void insertMedia(MediaEntry e, MediaEntry.MediaFile f) throws IOException {
Utility.confirm("Upgrade Game",
"This will upgrade your game and attempt to copy your progress. "
+ "If this is unsuccessful you might have to start a new game. Proceed?",
() -> performGameUpgrade(e, f));
}
@Override
public MediaEntry getMediaEntry() {
return gameMediaEntry;
}
@Override
public MediaFile getMediaFile() {
return gameMediaFile;
}
@Override
public boolean isAccepted(MediaEntry e, MediaEntry.MediaFile f) {
return e.type == DiskType.FLOPPY800;
}
@Override
public void eject() {
// Do nothing
}
private void insertHardDisk(int drive, MediaEntry entry, MediaFile file) {
RAM memory = Emulator.computer.memory;
memory.getCard(7).ifPresent(card -> {
try {
((CardMassStorage) card).getConsumers()[drive].insertMedia(entry, file);
} catch (IOException ex) {
Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, null, ex);
}
});
if (drive == 0) {
gameMediaEntry = entry;
gameMediaFile = file;
}
}
private void ejectHardDisk(int drive) {
RAM memory = Emulator.computer.memory;
memory.getCard(7).ifPresent(card -> {
((CardMassStorage) card).getConsumers()[drive].eject();
});
if (drive == 0) {
gameMediaEntry = null;
gameMediaFile = null;
}
}
public void loadGame() {
// Insert game disk image
MediaEntry e = new MediaEntry();
e.author = "8 Bit Bunch";
e.name = "Lawless Legends";
e.type = DiskType.LARGE;
MediaFile f = new MediaEntry.MediaFile();
f.path = getGamePath("game.2mg");
if (f.path != null && f.path.exists()) {
insertHardDisk(0, e, f);
}
}
private File getGamePath(String filename) {
File base = getApplicationStoragePath();
File target = new File(base, filename);
if (!target.exists()) {
copyResource(filename, target);
}
return target;
}
private File getApplicationStoragePath() {
String path = System.getenv("APPDATA");
if (path == null) {
path = System.getProperty("user.home");
}
if (path == null) {
path = ".";
}
File base = new File(path);
File appPath = new File(base, "lawless-legends");
appPath.mkdirs();
return appPath;
}
private void copyResource(String filename, File target) {
File localResource = new File(".", filename);
InputStream in = null;
if (localResource.exists()) {
try {
in = new FileInputStream(localResource);
} catch (FileNotFoundException ex) {
Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
in = getClass().getClassLoader().getResourceAsStream("jace/data/" + filename);
}
if (in != null) {
try {
java.nio.file.Files.copy(in, target.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, "Unable to find resource {0}", filename);
}
}
private void performGameUpgrade(MediaEntry e, MediaFile f) {
try {
System.out.println("Game upgrade starting");
if (!waitForText("I)mport", 1)) {
Emulator.computer.coldStart();
if (!waitForText("I)mport", 1000)) {
throw new Exception("Unable to detect upgrade prompt - Upgrade aborted.");
}
}
System.out.println("Menu Propmt detected");
Keyboard.pasteFromString("i");
if (!waitForText("Insert disk for import", 100)) {
throw new Exception("Unable to detect first insert prompt - Upgrade aborted.");
}
System.out.println("First Propmt detected");
Keyboard.pasteFromString(" ");
if (!waitForText("Game imported", 100)) {
throw new Exception("Unable to detect second insert prompt - Upgrade aborted.");
}
System.out.println("Performing upgrade");
File target = getMediaFile().path;
ejectHardDisk(0);
java.nio.file.Files.copy(f.path.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
f.path = target;
insertHardDisk(0, e, f);
Keyboard.pasteFromString(" ");
System.out.println("Upgrade completed");
} catch (Exception ex) {
Logger.getLogger(LawlessImageTool.class.getName()).log(Level.SEVERE, null, ex);
Utility.gripe(ex.getMessage());
}
}
private boolean waitForText(String message, int timeout) throws InterruptedException {
LawlessComputer compy = Emulator.computer;
RAM128k mem = (RAM128k) compy.getMemory();
while (timeout-- > 0) {
StringBuilder allText = new StringBuilder();
for (int i=0x0400; i < 0x07ff; i++) {
allText.append((char) (mem.getMainMemory().readByte(i) & 0x07f));
}
if (allText.toString().contains(message)) {
return true;
} else {
compy.waitForVBL();
}
}
return false;
}
}