Skip to content

Commit c8011a1

Browse files
committed
Adding the desktop implementation of the disk persistence.
1 parent e90f404 commit c8011a1

8 files changed

Lines changed: 333 additions & 16 deletions

File tree

core/src/main/java/emu/jvic/HomeScreen.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040

4141
import emu.jvic.config.AppConfig;
4242
import emu.jvic.config.AppConfigItem;
43+
import emu.jvic.config.AppConfigItem.FileLocation;
4344
import emu.jvic.ui.ConfirmHandler;
4445
import emu.jvic.ui.ConfirmResponseHandler;
4546
import emu.jvic.ui.DialogHandler;
@@ -854,7 +855,7 @@ public void openFileResult(boolean success, String filePath, byte[] fileData) {
854855
AppConfigItem appConfigItem = new AppConfigItem();
855856
appConfigItem.setName("Adhoc VIC Program");
856857
appConfigItem.setFilePath(filePath);
857-
appConfigItem.setFileType("ABSOLUTE");
858+
appConfigItem.setFileLocation(FileLocation.LOCAL);
858859
if (filePath.toUpperCase().contains("NTSC")) {
859860
appConfigItem.setMachineType("NTSC");
860861
} else {

core/src/main/java/emu/jvic/JVic.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.badlogic.gdx.Preferences;
88

99
import emu.jvic.config.AppConfigItem;
10+
import emu.jvic.config.AppConfigItem.FileLocation;
1011
import emu.jvic.memory.RamType;
1112
import emu.jvic.ui.DialogHandler;
1213

@@ -85,6 +86,7 @@ else if (args.containsKey("url")) {
8586
AppConfigItem adhocProgram = new AppConfigItem();
8687
adhocProgram.setName("Adhoc VIC Program");
8788
adhocProgram.setFilePath(getFilePathForProgramUrl(programUrl));
89+
adhocProgram.setFileLocation(FileLocation.EXTERNAL);
8890
if (programUrl.toUpperCase().contains("NTSC")) {
8991
adhocProgram.setMachineType("NTSC");
9092
} else {
@@ -98,6 +100,7 @@ else if (args.containsKey("filePath")) {
98100
AppConfigItem adhocProgram = new AppConfigItem();
99101
adhocProgram.setName("Adhoc VIC Program");
100102
adhocProgram.setFilePath(filePath);
103+
adhocProgram.setFileLocation(FileLocation.ABSOLUTE);
101104
if (filePath.toUpperCase().contains("NTSC")) {
102105
adhocProgram.setMachineType("NTSC");
103106
} else {
@@ -333,7 +336,7 @@ public void fileDropped(String filePath, byte[] fileData) {
333336
AppConfigItem appConfigItem = new AppConfigItem();
334337
appConfigItem.setName("Adhoc VIC Program");
335338
appConfigItem.setFilePath(filePath);
336-
appConfigItem.setFileType("ABSOLUTE");
339+
appConfigItem.setFileLocation(FileLocation.LOCAL);
337340
if (filePath.toUpperCase().contains("NTSC")) {
338341
appConfigItem.setMachineType("NTSC");
339342
} else {

core/src/main/java/emu/jvic/io/disk/persistence/DiskPersistenceSupport.java

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,32 @@ private DiskPersistenceSupport() {
1414
}
1515

1616
public static DiskPersistenceKey createKey(AppConfigItem appConfigItem, byte[] originalDiskImage) {
17-
String programIdSource = getProgramIdSource(appConfigItem);
18-
String readablePrefix = buildReadablePrefix(appConfigItem, programIdSource);
19-
String identityHash = stableHashHex(buildIdentityString(appConfigItem));
17+
return createKey(appConfigItem, originalDiskImage, null);
18+
}
19+
20+
public static DiskPersistenceKey createKey(AppConfigItem appConfigItem,
21+
byte[] originalDiskImage, String normalizedPathOverride) {
22+
String programIdSource = getProgramIdSource(appConfigItem, normalizedPathOverride);
23+
String readablePrefix = buildReadablePrefix(appConfigItem, programIdSource,
24+
normalizedPathOverride);
25+
String identityHash = stableHashHex(buildIdentityString(appConfigItem,
26+
normalizedPathOverride));
2027
String originalDiskHash = stableHashHex(originalDiskImage);
2128
return new DiskPersistenceKey(readablePrefix + "--" + identityHash, originalDiskHash);
2229
}
2330

2431
public static String getProgramIdSource(AppConfigItem appConfigItem) {
32+
return getProgramIdSource(appConfigItem, null);
33+
}
34+
35+
public static String getProgramIdSource(AppConfigItem appConfigItem,
36+
String normalizedPathOverride) {
2537
if (!isBlank(appConfigItem.getGameId())) {
2638
return "gameId";
2739
}
40+
if (!isBlank(normalizedPathOverride)) {
41+
return "filePath";
42+
}
2843
if (!isBlank(appConfigItem.getFilePath())) {
2944
return "filePath";
3045
}
@@ -56,23 +71,29 @@ public static String stableHashHex(String value) {
5671
return toPaddedHex(hash);
5772
}
5873

59-
private static String buildReadablePrefix(AppConfigItem appConfigItem, String programIdSource) {
74+
private static String buildReadablePrefix(AppConfigItem appConfigItem,
75+
String programIdSource, String normalizedPathOverride) {
6076
if ("gameId".equals(programIdSource)) {
6177
return "gid-" + slugify(appConfigItem.getGameId());
6278
}
6379
if ("filePath".equals(programIdSource)) {
64-
return "path-" + slugify(appConfigItem.getFilePath());
80+
return "path-" + slugify(!isBlank(normalizedPathOverride)
81+
? normalizedPathOverride
82+
: appConfigItem.getFilePath());
6583
}
6684
if ("name".equals(programIdSource)) {
6785
return "name-" + slugify(appConfigItem.getName());
6886
}
6987
return "program";
7088
}
7189

72-
private static String buildIdentityString(AppConfigItem appConfigItem) {
90+
private static String buildIdentityString(AppConfigItem appConfigItem,
91+
String normalizedPathOverride) {
7392
StringBuilder value = new StringBuilder(128);
7493
appendPart(value, appConfigItem.getGameId());
75-
appendPart(value, appConfigItem.getFilePath());
94+
appendPart(value, !isBlank(normalizedPathOverride)
95+
? normalizedPathOverride
96+
: appConfigItem.getFilePath());
7697
appendPart(value, appConfigItem.getEntryName());
7798
appendPart(value, appConfigItem.getFileType());
7899
return value.toString();

lwjgl3/src/main/java/emu/jvic/lwjgl3/DesktopJVicRunner.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
import emu.jvic.Program;
2525
import emu.jvic.config.AppConfigItem;
2626
import emu.jvic.cpu.Cpu6502;
27+
import emu.jvic.io.disk.persistence.DiskImagePersistence;
28+
import emu.jvic.io.disk.persistence.DiskImagePersistenceSession;
29+
import emu.jvic.lwjgl3.disk.DesktopDiskImagePersistence;
2730
import emu.jvic.memory.RamType;
2831
import emu.jvic.sound.SoundGenerator;
2932
import emu.jvic.ui.MachineInputProcessor.ScreenSize;
@@ -33,9 +36,12 @@ public class DesktopJVicRunner extends JVicRunner {
3336
private Thread machineThread;
3437

3538
private Machine machine;
39+
40+
private final DiskImagePersistence diskImagePersistence;
3641

3742
public DesktopJVicRunner(KeyboardMatrix keyboardMatrix, PixelData pixelData, SoundGenerator soundGenerator) {
3843
super(keyboardMatrix, pixelData, soundGenerator);
44+
this.diskImagePersistence = new DesktopDiskImagePersistence();
3945
}
4046

4147
@Override
@@ -62,6 +68,18 @@ private void runProgram(AppConfigItem appConfigItem) {
6268
}
6369

6470
private void runProgram(AppConfigItem appConfigItem, Program program) {
71+
if ((program != null) && "DISK".equals(program.getProgramType())) {
72+
diskImagePersistence.resolve(appConfigItem, program.getProgramData(),
73+
persistenceSession -> runProgram(appConfigItem, program,
74+
persistenceSession));
75+
return;
76+
}
77+
78+
runProgram(appConfigItem, program, null);
79+
}
80+
81+
private void runProgram(AppConfigItem appConfigItem, Program program,
82+
DiskImagePersistenceSession persistenceSession) {
6583
MachineType machineType = MachineType.valueOf(appConfigItem.getMachineType());
6684
RamType ramType = RamType.valueOf(appConfigItem.getRam());
6785

@@ -77,7 +95,7 @@ private void runProgram(AppConfigItem appConfigItem, Program program) {
7795
Queue<char[]> autoRunCmdQueue = null;
7896
Callable<Queue<char[]>> autoLoadProgram = machine.init(
7997
basicRom, kernalRom, charRom, dos1541Rom, program, machineType, ramType,
80-
appConfigItem.getPalette());
98+
appConfigItem.getPalette(), persistenceSession);
8199

82100
final int NANOS_PER_FRAME = (1000000000 / machineType.getFramesPerSecond());
83101

lwjgl3/src/main/java/emu/jvic/lwjgl3/DesktopProgramLoader.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import emu.jvic.Program;
2020
import emu.jvic.ProgramLoader;
2121
import emu.jvic.config.AppConfigItem;
22+
import emu.jvic.config.AppConfigItem.FileLocation;
2223

2324
public class DesktopProgramLoader extends ProgramLoader {
2425

@@ -37,12 +38,7 @@ public void fetchProgram(AppConfigItem appConfigItem, Consumer<Program> programC
3738
// Ignore. Nothing to load.
3839
}
3940
else if (!appConfigItem.getFilePath().startsWith("http")) {
40-
FileHandle fileHandle = null;
41-
if ("ABSOLUTE".equals(appConfigItem.getFileType())) {
42-
fileHandle = Gdx.files.absolute(appConfigItem.getFilePath());
43-
} else {
44-
fileHandle = Gdx.files.internal(appConfigItem.getFilePath());
45-
}
41+
FileHandle fileHandle = resolveFileHandle(appConfigItem);
4642
if (fileHandle != null) {
4743
if (fileHandle.exists()) {
4844
data = fileHandle.readBytes();
@@ -179,6 +175,14 @@ else if (isCartFile(data)) {
179175
programConsumer.accept(program);
180176
}
181177

178+
private FileHandle resolveFileHandle(AppConfigItem appConfigItem) {
179+
FileLocation fileLocation = appConfigItem.getFileLocation();
180+
if ((fileLocation == FileLocation.ABSOLUTE) || (fileLocation == FileLocation.LOCAL)) {
181+
return Gdx.files.absolute(appConfigItem.getFilePath());
182+
}
183+
return Gdx.files.internal(appConfigItem.getFilePath());
184+
}
185+
182186
private byte[] loadFullCartProgramData(String entryName, byte[] data, ZipInputStream zis,
183187
AppConfigItem appConfigItem) {
184188
try {
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package emu.jvic.lwjgl3.disk;
2+
3+
import java.io.IOException;
4+
import java.nio.file.Files;
5+
import java.nio.file.Path;
6+
import java.nio.file.Paths;
7+
import java.util.function.Consumer;
8+
9+
import emu.jvic.config.AppConfigItem;
10+
import emu.jvic.config.AppConfigItem.FileLocation;
11+
import emu.jvic.io.disk.persistence.DiskImagePersistence;
12+
import emu.jvic.io.disk.persistence.DiskImagePersistenceSession;
13+
import emu.jvic.io.disk.persistence.DiskPersistenceKey;
14+
import emu.jvic.io.disk.persistence.DiskPersistenceSupport;
15+
import emu.jvic.io.disk.persistence.NoOpDiskImagePersistenceSession;
16+
17+
/**
18+
* Desktop implementation of disk-image persistence using app-data sidecar files.
19+
*/
20+
public class DesktopDiskImagePersistence implements DiskImagePersistence {
21+
22+
private final DesktopPersistencePaths persistencePaths;
23+
24+
public DesktopDiskImagePersistence() {
25+
this(DesktopPersistencePaths.createDefault());
26+
}
27+
28+
public DesktopDiskImagePersistence(DesktopPersistencePaths persistencePaths) {
29+
this.persistencePaths = persistencePaths;
30+
}
31+
32+
@Override
33+
public void resolve(AppConfigItem appConfigItem, byte[] originalDiskImage,
34+
Consumer<DiskImagePersistenceSession> onResolved) {
35+
if (originalDiskImage == null) {
36+
onResolved.accept(new NoOpDiskImagePersistenceSession(null));
37+
return;
38+
}
39+
40+
String normalizedPath = normalizeIdentityPath(appConfigItem);
41+
DiskPersistenceKey key = DiskPersistenceSupport.createKey(appConfigItem,
42+
originalDiskImage, normalizedPath);
43+
String programIdSource = DiskPersistenceSupport.getProgramIdSource(appConfigItem,
44+
normalizedPath);
45+
46+
byte[] startupDiskImage = originalDiskImage;
47+
boolean persistent = false;
48+
49+
try {
50+
Path persistedDiskImageFile = persistencePaths.getDiskImageFile(key);
51+
if (Files.isRegularFile(persistedDiskImageFile)) {
52+
byte[] persistedDiskImage = Files.readAllBytes(persistedDiskImageFile);
53+
if ((persistedDiskImage != null) && (persistedDiskImage.length > 0)) {
54+
startupDiskImage = persistedDiskImage;
55+
persistent = true;
56+
}
57+
}
58+
} catch (IOException e) {
59+
startupDiskImage = originalDiskImage;
60+
persistent = false;
61+
}
62+
63+
onResolved.accept(new DesktopDiskImagePersistenceSession(appConfigItem, key,
64+
persistencePaths, startupDiskImage, persistent,
65+
originalDiskImage.length, programIdSource));
66+
}
67+
68+
private String normalizeIdentityPath(AppConfigItem appConfigItem) {
69+
FileLocation fileLocation = appConfigItem.getFileLocation();
70+
if ((fileLocation != FileLocation.ABSOLUTE) && (fileLocation != FileLocation.LOCAL)) {
71+
return null;
72+
}
73+
74+
String filePath = appConfigItem.getFilePath();
75+
if ((filePath == null) || filePath.trim().isEmpty()) {
76+
return null;
77+
}
78+
79+
try {
80+
return Paths.get(filePath).toRealPath().toString();
81+
} catch (IOException e) {
82+
return Paths.get(filePath).toAbsolutePath().normalize().toString();
83+
}
84+
}
85+
}

0 commit comments

Comments
 (0)