mirror of
https://github.com/mauiaaron/apple2.git
synced 2024-06-01 13:41:28 +00:00
Persist disk image 'was_gzipped' state
- 'was_gzipped' state is persisted in .apple2.json preferences - This removes all file-renaming codepaths at the cost of temporarily nonconformance to naming convesion (e.g., disk images inserted read/write are still called 'foo.dsk.gz' although they are not compressed) - Unclean shutdown of emulator leaves any disk images that were inserted read/write in their non-gzipped state (regardless of file name extension) on disk until emulator restarted and disk images explictly ejected or clean shudown. App removal after unclean shutdown will potentially leave mis-named disk images lingering. - Emulator will handle edge cases of non-gzipped disk images with '.gz' extension and gzipped disk images without '.gz' extension
This commit is contained in:
parent
399daf16fa
commit
b300e60e2a
|
@ -143,8 +143,8 @@ public class Apple2Activity extends Activity /*implements Apple2DiskChooserActiv
|
|||
boolean switchingToPortrait = Apple2VideoSettingsMenu.SETTINGS.applyLandscapeMode(this);
|
||||
Apple2Preferences.sync(this, null);
|
||||
|
||||
Apple2DisksMenu.insertDisk((String) Apple2Preferences.getJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_A), /*driveA:*/true, /*isReadOnly:*/(boolean) Apple2Preferences.getJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_A_RO));
|
||||
Apple2DisksMenu.insertDisk((String) Apple2Preferences.getJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_B), /*driveA:*/false, /*isReadOnly:*/(boolean) Apple2Preferences.getJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_B_RO));
|
||||
Apple2DisksMenu.insertDisk((String) Apple2Preferences.getJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_A), /*driveA:*/true, /*isReadOnly:*/(boolean) Apple2Preferences.getJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_A_RO), /*onLaunch:*/true);
|
||||
Apple2DisksMenu.insertDisk((String) Apple2Preferences.getJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_B), /*driveA:*/false, /*isReadOnly:*/(boolean) Apple2Preferences.getJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_B_RO), /*onLaunch:*/true);
|
||||
|
||||
showSplashScreen(!firstTime);
|
||||
|
||||
|
|
|
@ -115,6 +115,17 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
return true;
|
||||
}
|
||||
},
|
||||
CURRENT_DISK_PATH_A_GZ {
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "driveAInsertedDiskGZ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
CURRENT_DISK_PATH_B {
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
|
@ -132,6 +143,17 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
return "driveBInsertedDiskRO";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
CURRENT_DISK_PATH_B_GZ {
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "driveBInsertedDiskGZ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
|
@ -260,7 +282,7 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
return path;
|
||||
}
|
||||
|
||||
public static void insertDisk(String imageName, boolean isDriveA, boolean isReadOnly) {
|
||||
public static void insertDisk(String imageName, boolean isDriveA, boolean isReadOnly, boolean onLaunch) {
|
||||
try {
|
||||
JSONObject map = new JSONObject();
|
||||
|
||||
|
@ -271,13 +293,8 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
////map.put("fd", fd);
|
||||
} else {
|
||||
File file = new File(imageName);
|
||||
|
||||
if (!file.exists()) {
|
||||
imageName = addGzipExtension(imageName);
|
||||
file = new File(imageName);
|
||||
if (!file.exists()) {
|
||||
throw new RuntimeException("cannot insert : " + imageName);
|
||||
}
|
||||
throw new RuntimeException("cannot insert : " + imageName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,9 +309,22 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
map.put("disk", imageName);
|
||||
map.put("drive", isDriveA ? "0" : "1");
|
||||
map.put("readOnly", isReadOnly ? "true" : "false");
|
||||
if (onLaunch) {
|
||||
boolean wasGzipped = (boolean) (isDriveA ? Apple2Preferences.getJSONPref(SETTINGS.CURRENT_DISK_PATH_A_GZ) : Apple2Preferences.getJSONPref(SETTINGS.CURRENT_DISK_PATH_B_GZ));
|
||||
map.put("wasGzipped", wasGzipped ? "true" : "false");
|
||||
}
|
||||
|
||||
String jsonString = nativeChooseDisk(map.toString());
|
||||
|
||||
map = new JSONObject(jsonString);
|
||||
boolean inserted = map.getBoolean("inserted");
|
||||
if (inserted) {
|
||||
boolean wasGzipped = map.getBoolean("wasGzipped");
|
||||
Apple2Preferences.setJSONPref(isDriveA ? Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_A_GZ : Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_B_GZ, wasGzipped);
|
||||
} else {
|
||||
ejectDisk(isDriveA);
|
||||
}
|
||||
|
||||
} catch (Throwable t) {
|
||||
Log.d(TAG, "OOPS: " + t);
|
||||
}
|
||||
|
@ -355,7 +385,7 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
boolean isDriveA = driveA.isChecked();
|
||||
boolean diskReadOnly = readOnly.isChecked();
|
||||
|
||||
insertDisk(imagePath, isDriveA, diskReadOnly);
|
||||
insertDisk(imagePath, isDriveA, diskReadOnly, /*onLaunch:*/false);
|
||||
|
||||
dialog.dismiss();
|
||||
mActivity.dismissAllMenus();
|
||||
|
@ -407,20 +437,6 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
return (suffix.equalsIgnoreCase(".dsk.gz") || suffix.equalsIgnoreCase(".nib.gz"));
|
||||
}
|
||||
|
||||
public static boolean isGzipExtension(String name) {
|
||||
int len = name.length();
|
||||
String ext = name.substring(len - 3, len);
|
||||
return ext.equals(".gz");
|
||||
}
|
||||
|
||||
public static String removeGzipExtention(String name) {
|
||||
return isGzipExtension(name) ? name.substring(0, name.length() - 3) : name;
|
||||
}
|
||||
|
||||
public static String addGzipExtension(String name) {
|
||||
return isGzipExtension(name) ? name : name + ".gz";
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// internals ...
|
||||
|
||||
|
|
|
@ -364,23 +364,24 @@ public class Apple2MainMenu {
|
|||
////int fd = Apple2DiskChooserActivity.openFileDescriptor(diskPath, /*isReadOnly:*/readOnly);
|
||||
////map.put(fdKeys[i], fd);
|
||||
} else {
|
||||
boolean isGzip = Apple2DisksMenu.isGzipExtension(diskPath);
|
||||
boolean exists = new File(diskPath).exists();
|
||||
|
||||
if (!exists) {
|
||||
diskPath = isGzip ? Apple2DisksMenu.removeGzipExtention(diskPath) : Apple2DisksMenu.addGzipExtension(diskPath);
|
||||
isGzip = !isGzip;
|
||||
exists = new File(diskPath).exists();
|
||||
if (!exists) {
|
||||
Log.e(TAG, "Did not find path(s) for drive #" + i + " specified in emulator.state file : " + diskPath);
|
||||
}
|
||||
Log.e(TAG, "Did not find path(s) for drive #" + i + " specified in emulator.state file : " + diskPath);
|
||||
}
|
||||
|
||||
Apple2Preferences.setJSONPref(i == 0 ? Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_A : Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_B, diskPath);
|
||||
}
|
||||
}
|
||||
|
||||
jsonString = mActivity.loadState(map.toString());
|
||||
map = new JSONObject(jsonString);
|
||||
|
||||
{
|
||||
boolean wasGzippedA = map.getBoolean("wasGzippedA");
|
||||
Apple2Preferences.setJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_A_GZ, wasGzippedA);
|
||||
}
|
||||
{
|
||||
boolean wasGzippedB = map.getBoolean("wasGzippedB");
|
||||
Apple2Preferences.setJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_B_GZ, wasGzippedB);
|
||||
}
|
||||
|
||||
// FIXME TODO : what to do if state load failed?
|
||||
|
||||
|
|
|
@ -407,8 +407,16 @@ jstring Java_org_deadc0de_apple2ix_Apple2DisksMenu_nativeChooseDisk(JNIEnv *env,
|
|||
inserted = false;
|
||||
} else {
|
||||
video_animations->animation_showDiskChosen(drive);
|
||||
// possibly override was_gzipped, if specified in args ...
|
||||
bool wasGzipped = false;
|
||||
if (json_mapParseBoolValue(jsonData, "wasGzipped", &wasGzipped)) {
|
||||
disk6.disk[drive].was_gzipped = wasGzipped;
|
||||
}
|
||||
}
|
||||
|
||||
// remember if image was gzipped
|
||||
prefs_setBoolValue(PREF_DOMAIN_VM, drive == 0 ? PREF_DISK_DRIVEA_GZ : PREF_DISK_DRIVEB_GZ, disk6.disk[drive].was_gzipped); // HACK FIXME TODO ... refactor : this is erased on the Java side when we resume emulation
|
||||
json_mapSetBoolValue(jsonData, "wasGzipped", disk6.disk[drive].was_gzipped);
|
||||
json_mapSetBoolValue(jsonData, "inserted", inserted);
|
||||
|
||||
if (createdFd) {
|
||||
|
@ -452,26 +460,6 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeSaveState(JNIEnv *env, jcla
|
|||
(*env)->ReleaseStringUTFChars(env, jPath, path);
|
||||
}
|
||||
|
||||
static int _insert_path(char *path, bool readOnly) {
|
||||
int fd = -1;
|
||||
|
||||
if (strlen(path) > 0) {
|
||||
TEMP_FAILURE_RETRY(fd = open(path, readOnly ? O_RDONLY : O_RDWR));
|
||||
if (fd == -1) {
|
||||
ASPRINTF(&path, "%s.gz", path);
|
||||
if (path != NULL) {
|
||||
TEMP_FAILURE_RETRY(fd = open(path, readOnly ? O_RDONLY : O_RDWR));
|
||||
if (fd == -1) {
|
||||
LOG("OOPS could not open disk path %s", path);
|
||||
}
|
||||
}
|
||||
FREE(path);
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
jstring Java_org_deadc0de_apple2ix_Apple2Activity_nativeLoadState(JNIEnv *env, jclass cls, jstring jJsonString) {
|
||||
assert(cpu_isPaused() && "considered dangerous to load state when CPU thread is running");
|
||||
|
||||
|
@ -498,7 +486,10 @@ jstring Java_org_deadc0de_apple2ix_Apple2Activity_nativeLoadState(JNIEnv *env, j
|
|||
json_mapCopyStringValue(jsonData, "diskA", &pathA);
|
||||
json_unescapeSlashes(&pathA);
|
||||
|
||||
fdA = _insert_path(pathA, readOnlyA);
|
||||
TEMP_FAILURE_RETRY(fdA = open(pathA, readOnlyA ? O_RDONLY : O_RDWR));
|
||||
if (fdA == -1) {
|
||||
LOG("OOPS could not open disk path %s", pathA);
|
||||
}
|
||||
createdFdA = fdA > 0;
|
||||
|
||||
FREE(pathA);
|
||||
|
@ -514,16 +505,23 @@ jstring Java_org_deadc0de_apple2ix_Apple2Activity_nativeLoadState(JNIEnv *env, j
|
|||
json_mapCopyStringValue(jsonData, "diskB", &pathB);
|
||||
json_unescapeSlashes(&pathB);
|
||||
|
||||
fdB = _insert_path(pathB, readOnlyB);
|
||||
TEMP_FAILURE_RETRY(fdB = open(pathB, readOnlyB ? O_RDONLY : O_RDWR));
|
||||
if (fdB == -1) {
|
||||
LOG("OOPS could not open disk path %s", pathB);
|
||||
}
|
||||
createdFdB = fdB > 0;
|
||||
|
||||
FREE(pathB);
|
||||
}
|
||||
|
||||
bool loadStateSuccess = true;
|
||||
if (!emulator_loadState(path, (int)fdA, (int)fdB)) {
|
||||
if (emulator_loadState(path, (int)fdA, (int)fdB)) {
|
||||
json_mapSetBoolValue(jsonData, "wasGzippedA", disk6.disk[0].was_gzipped);
|
||||
json_mapSetBoolValue(jsonData, "wasGzippedB", disk6.disk[1].was_gzipped);
|
||||
} else {
|
||||
loadStateSuccess = false;
|
||||
LOG("OOPS, could not load emulator state");
|
||||
// FIXME TODO : should show invalid state animation here ...
|
||||
}
|
||||
|
||||
if (createdFdA) {
|
||||
|
|
13
src/disk.c
13
src/disk.c
|
@ -1007,6 +1007,8 @@ void disk6_flush(int drive) {
|
|||
|
||||
__sync_synchronize();
|
||||
|
||||
LOG("FLUSHING disk image I/O");
|
||||
|
||||
int ret = -1;
|
||||
TEMP_FAILURE_RETRY(ret = msync(disk6.disk[drive].raw_image_data, disk6.disk[drive].whole_len, MS_SYNC));
|
||||
if (ret) {
|
||||
|
@ -1093,8 +1095,7 @@ bool disk6_saveState(StateHelper_s *helper) {
|
|||
}
|
||||
LOG("SAVE stepper_phases[%lu] = %02x", i, stepper_phases);
|
||||
|
||||
// Save unused placeholder -- backwards compatibility
|
||||
state = 0x0;
|
||||
state = disk6.disk[i].was_gzipped;
|
||||
if (!helper->save(fd, &state, 1)) {
|
||||
break;
|
||||
}
|
||||
|
@ -1235,10 +1236,16 @@ static bool _disk6_loadState(StateHelper_s *helper, JSON_ref *json) {
|
|||
stepper_phases = state & 0x3; // HACK NOTE : this is unnecessarily encoded twice ...
|
||||
}
|
||||
|
||||
// load placeholder
|
||||
// format A2V3+ : was_gzipped, (otherwise placeholder)
|
||||
if (!helper->load(fd, &state, 1)) {
|
||||
break;
|
||||
}
|
||||
if (helper->version >= 3) {
|
||||
disk6.disk[i].was_gzipped = (state != 0);
|
||||
|
||||
// remember if image was gzipped
|
||||
prefs_setBoolValue(PREF_DOMAIN_VM, i == 0 ? PREF_DISK_DRIVEA_GZ : PREF_DISK_DRIVEB_GZ, disk6.disk[i].was_gzipped);
|
||||
}
|
||||
|
||||
if (!helper->load(fd, &state, 1)) {
|
||||
break;
|
||||
|
|
11
src/misc.c
11
src/misc.c
|
@ -17,7 +17,8 @@
|
|||
|
||||
#define SAVE_MAGICK "A2VM"
|
||||
#define SAVE_MAGICK2 "A2V2"
|
||||
#define SAVE_VERSION 2
|
||||
#define SAVE_MAGICK3 "A2V3"
|
||||
#define SAVE_VERSION 3
|
||||
#define SAVE_MAGICK_LEN sizeof(SAVE_MAGICK)
|
||||
|
||||
typedef struct module_ctor_node_s {
|
||||
|
@ -112,10 +113,12 @@ static int _load_magick(int fd) {
|
|||
|
||||
// check header
|
||||
|
||||
if (memcmp(magick, SAVE_MAGICK, SAVE_MAGICK_LEN) == 0) {
|
||||
return 1;
|
||||
if (memcmp(magick, SAVE_MAGICK3, SAVE_MAGICK_LEN) == 0) {
|
||||
return 3;
|
||||
} else if (memcmp(magick, SAVE_MAGICK2, SAVE_MAGICK_LEN) == 0) {
|
||||
return 2;
|
||||
} else if (memcmp(magick, SAVE_MAGICK, SAVE_MAGICK_LEN) == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
ERRLOG("bad header magick in emulator save state file");
|
||||
|
@ -138,7 +141,7 @@ bool emulator_saveState(const char * const path) {
|
|||
assert(fd != 0 && "crazy platform");
|
||||
|
||||
// save header
|
||||
if (!_save_state(fd, (const uint8_t *)SAVE_MAGICK2, SAVE_MAGICK_LEN)) {
|
||||
if (!_save_state(fd, (const uint8_t *)SAVE_MAGICK3, SAVE_MAGICK_LEN)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,8 @@
|
|||
// vm
|
||||
#define PREF_CPU_SCALE "cpuScale"
|
||||
#define PREF_CPU_SCALE_ALT "cpuScaleAlt"
|
||||
#define PREF_DISK_DRIVEA_GZ "driveAInsertedDiskGZ"
|
||||
#define PREF_DISK_DRIVEB_GZ "driveBInsertedDiskGZ"
|
||||
|
||||
typedef void (*prefs_change_callback_f)(const char * _NONNULL domain);
|
||||
|
||||
|
|
|
@ -227,17 +227,6 @@ int test_setup_boot_disk(const char *fileName, int readonly) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t len = strlen(disk);
|
||||
disk[len-3] = '\0'; // try again without '.gz' extension
|
||||
TEMP_FAILURE_RETRY(fd = open(disk, readonly ? O_RDONLY : O_RDWR));
|
||||
if (fd != -1) {
|
||||
err = disk6_insert(fd, /*drive:*/0, disk, readonly) != NULL;
|
||||
TEMP_FAILURE_RETRY(close(fd));
|
||||
if (!err) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
path = &paths[0];
|
||||
|
|
Loading…
Reference in New Issue
Block a user