diff --git a/CMakeLists.txt b/CMakeLists.txt
index c19576986a..dd092d6631 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -190,6 +190,13 @@ if (APPLE)
# MACOSX_BUNDLE_ICON_FILE "icon.icns"
)
+ if (NOT IOS)
+ set_target_properties(dingusppc PROPERTIES
+ MACOSX_BUNDLE_INFO_PLIST "${PROJECT_SOURCE_DIR}/cmake/DingusPPCInfo.plist.in"
+ XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUNDLE_ID}"
+ )
+ endif()
+
find_library(COCOA_LIBRARY Cocoa)
find_library(IOKIT_LIBRARY IOKit)
find_library(COREAUDIO_LIBRARY CoreAudio)
diff --git a/cmake/DingusPPCInfo.plist.in b/cmake/DingusPPCInfo.plist.in
new file mode 100644
index 0000000000..c3001e9d43
--- /dev/null
+++ b/cmake/DingusPPCInfo.plist.in
@@ -0,0 +1,70 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleDocumentTypes
+
+
+ CFBundleTypeName
+ CD-ROM image
+ CFBundleTypeRole
+ Viewer
+ LSHandlerRank
+ Alternate
+ LSItemContentTypes
+
+ public.iso-image
+ com.roxio.disk-image-toast
+
+
+
+ CFBundleExecutable
+ ${MACOSX_BUNDLE_EXECUTABLE_NAME}
+ CFBundleGetInfoString
+ ${MACOSX_BUNDLE_INFO_STRING}
+ CFBundleIconFile
+ ${MACOSX_BUNDLE_ICON_FILE}
+ CFBundleIdentifier
+ ${MACOSX_BUNDLE_GUI_IDENTIFIER}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleLongVersionString
+ ${MACOSX_BUNDLE_LONG_VERSION_STRING}
+ CFBundleName
+ ${MACOSX_BUNDLE_BUNDLE_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ ${MACOSX_BUNDLE_SHORT_VERSION_STRING}
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ ${MACOSX_BUNDLE_BUNDLE_VERSION}
+ CSResourcesFileMapped
+
+ NSHumanReadableCopyright
+ ${MACOSX_BUNDLE_COPYRIGHT}
+ UTImportedTypeDeclarations
+
+
+ UTTypeConformsTo
+
+ public.disk-image
+
+ UTTypeDescription
+ Toast disk image
+ UTTypeIdentifier
+ com.roxio.disk-image-toast
+ UTTypeTagSpecification
+
+ public.filename-extension
+
+ toast
+
+
+
+
+
+
diff --git a/core/hostevents_sdl.cpp b/core/hostevents_sdl.cpp
index 91de0aac55..b0859e5382 100644
--- a/core/hostevents_sdl.cpp
+++ b/core/hostevents_sdl.cpp
@@ -23,6 +23,7 @@ along with this program. If not, see .
#include
#include
#include
+#include
#include
#include
@@ -322,6 +323,33 @@ void EventManager::poll_events() {
}
break;
+ case SDL_DROPFILE: {
+ const char* path = event.drop.file;
+ CdromInsertionResult result = gMachineObj
+ ? gMachineObj->insert_cdrom_image(path)
+ : CdromInsertionResult::NO_DRIVE;
+
+ switch (result) {
+ case CdromInsertionResult::SUCCESS:
+ LOG_F(INFO, "Inserted CD-ROM image: %s", path);
+ break;
+ case CdromInsertionResult::NO_DRIVE:
+ LOG_F(ERROR, "Cannot insert CD-ROM image; no CD-ROM drive is available: %s",
+ path);
+ break;
+ case CdromInsertionResult::MEDIA_PRESENT:
+ LOG_F(ERROR, "Cannot insert CD-ROM image; eject the current media first: %s",
+ path);
+ break;
+ case CdromInsertionResult::IMAGE_OPEN_FAILED:
+ LOG_F(ERROR, "Cannot insert CD-ROM image: %s", path);
+ break;
+ }
+
+ SDL_free(event.drop.file);
+ }
+ break;
+
default:
unhandled_events++;
}
diff --git a/devices/common/ata/atapicdrom.cpp b/devices/common/ata/atapicdrom.cpp
index b3ca23d8b4..d43750ae50 100644
--- a/devices/common/ata/atapicdrom.cpp
+++ b/devices/common/ata/atapicdrom.cpp
@@ -71,7 +71,8 @@ int AtapiCdrom::device_postinit() {
std::string cdr_image_path = GET_STR_PROP("cdr_img");
if (!cdr_image_path.empty()) {
- this->insert_image(cdr_image_path);
+ if (!this->insert_image(cdr_image_path))
+ return -1;
}
return 0;
diff --git a/devices/common/scsi/scsi.h b/devices/common/scsi/scsi.h
index c6844529bd..e026871723 100644
--- a/devices/common/scsi/scsi.h
+++ b/devices/common/scsi/scsi.h
@@ -175,6 +175,14 @@ enum ScsiCommand : uint8_t {
READ_CD = 0xBE,
};
+/** START STOP UNIT command flags in CDB byte 4. */
+namespace ScsiStartStopUnit {
+ enum : uint8_t {
+ START = 1 << 0,
+ LOAD_EJECT = 1 << 1,
+ };
+}
+
enum ScsiSense : uint8_t {
NO_SENSE = 0x0,
RECOVERED = 0x1,
@@ -200,8 +208,10 @@ enum ScsiError : uint8_t {
INVALID_CDB = 0x24,
INVALID_LUN = 0x25,
WRITE_PROTECT = 0x27,
+ MEDIUM_CHANGED = 0x28,
SAVING_NOT_SUPPORTED = 0x39,
MEDIUM_NOT_PRESENT = 0x3A,
+ REMOVAL_PREVENTED = 0x53,
};
/** SCSI device types used in INQUIRY. */
diff --git a/devices/common/scsi/scsiblockcmds.cpp b/devices/common/scsi/scsiblockcmds.cpp
index adda7c62f4..96d19347a5 100644
--- a/devices/common/scsi/scsiblockcmds.cpp
+++ b/devices/common/scsi/scsiblockcmds.cpp
@@ -179,11 +179,11 @@ int ScsiBlockCmds::write() {
}
int ScsiBlockCmds::start_stop_unit() {
- if (this->cdb_ptr[4] & 1) {
- if (this->cdb_ptr[4] & 2)
+ if (this->cdb_ptr[4] & ScsiStartStopUnit::START) {
+ if (this->cdb_ptr[4] & ScsiStartStopUnit::LOAD_EJECT)
LOG_F(INFO, "START_STOP_UNIT: medium load requested");
} else {
- if (this->cdb_ptr[4] & 2) {
+ if (this->cdb_ptr[4] & ScsiStartStopUnit::LOAD_EJECT) {
LOG_F(INFO, "START_STOP_UNIT: medium eject requested");
}
}
diff --git a/devices/common/scsi/scsibus.cpp b/devices/common/scsi/scsibus.cpp
index 432ddef29b..4e341fa0d9 100644
--- a/devices/common/scsi/scsibus.cpp
+++ b/devices/common/scsi/scsibus.cpp
@@ -354,7 +354,8 @@ void ScsiBus::attach_scsi_devices(const std::string bus_suffix)
gMachineObj->add_device(scsi_device_name,
std::unique_ptr(scsi_device));
this->register_device(scsi_id, scsi_device);
- scsi_device->insert_image(path);
+ if (!scsi_device->insert_image(path))
+ ABORT_F("Could not insert CD-ROM image, %s", path.c_str());
}
else {
LOG_F(ERROR, "%s: Too many devices. CD-ROM \"%s\" was not added.",
diff --git a/devices/common/scsi/scsicdrom.h b/devices/common/scsi/scsicdrom.h
index a46a75c8ec..ef9a693240 100644
--- a/devices/common/scsi/scsicdrom.h
+++ b/devices/common/scsi/scsicdrom.h
@@ -44,6 +44,7 @@ class ScsiCdrom : public ScsiPhysDevice, public ScsiCdromCmds {
protected:
bool is_device_ready() override { return this->is_ready; }
+ uint8_t not_ready_reason() override { return ScsiError::MEDIUM_NOT_PRESENT; }
void mode_select_6(uint8_t param_len);
diff --git a/devices/common/scsi/scsicdromcmds.cpp b/devices/common/scsi/scsicdromcmds.cpp
index 481a298957..ef6097de3a 100644
--- a/devices/common/scsi/scsicdromcmds.cpp
+++ b/devices/common/scsi/scsicdromcmds.cpp
@@ -62,6 +62,43 @@ void ScsiCdromCmds::process_command() {
phy_impl->switch_phase(next_phase);
}
+int ScsiCdromCmds::test_unit_ready() {
+ if (this->take_media_change()) {
+ this->sense_key = ScsiSense::UNIT_ATTENTION;
+ this->asc = ScsiError::MEDIUM_CHANGED;
+ this->ascq = 0;
+ phy_impl->set_status(ScsiStatus::CHECK_CONDITION, this->sense_key);
+ return ScsiPhase::STATUS;
+ }
+
+ return ScsiCommonCmds::test_unit_ready();
+}
+
+int ScsiCdromCmds::start_stop_unit() {
+ bool load_eject = this->cdb_ptr[4] & ScsiStartStopUnit::LOAD_EJECT;
+ bool start = this->cdb_ptr[4] & ScsiStartStopUnit::START;
+
+ if (!load_eject)
+ return ScsiPhase::STATUS;
+
+ if (start) {
+ LOG_F(INFO, "START_STOP_UNIT: medium load requested");
+ return ScsiPhase::STATUS;
+ }
+
+ if (this->drive_locked) {
+ LOG_F(INFO, "START_STOP_UNIT: medium eject prevented");
+ this->field_ptr_valid = false;
+ this->bit_ptr_valid = false;
+ this->illegal_request(ScsiError::REMOVAL_PREVENTED, 2, false);
+ } else {
+ LOG_F(INFO, "START_STOP_UNIT: medium eject requested");
+ this->eject_image();
+ }
+
+ return ScsiPhase::STATUS;
+}
+
int ScsiCdromCmds:: read_toc() {
uint8_t start_track, session_num;
int tot_tracks, resp_len = 0;
diff --git a/devices/common/scsi/scsicdromcmds.h b/devices/common/scsi/scsicdromcmds.h
index 85a91db571..28fa36f3d1 100644
--- a/devices/common/scsi/scsicdromcmds.h
+++ b/devices/common/scsi/scsicdromcmds.h
@@ -35,6 +35,8 @@ class ScsiCdromCmds : public ScsiBlockCmds, public CdromDrive {
protected:
virtual void process_command() override;
+ int test_unit_ready() override;
+ int start_stop_unit() override;
int get_apple_page_49(uint8_t subpage, uint8_t ctrl,
uint8_t *out_ptr, int avail_len);
diff --git a/devices/storage/blockstoragedevice.cpp b/devices/storage/blockstoragedevice.cpp
index f37ef47abf..c427ac76f7 100644
--- a/devices/storage/blockstoragedevice.cpp
+++ b/devices/storage/blockstoragedevice.cpp
@@ -39,16 +39,18 @@ BlockStorageDevice::BlockStorageDevice(const uint32_t cache_blocks,
}
BlockStorageDevice::~BlockStorageDevice() {
- this->img_file.close();
+ if (this->is_ready)
+ this->img_file.close();
}
int BlockStorageDevice::set_host_file(std::string file_path) {
- this->is_ready = false;
-
if (!this->img_file.open(file_path))
return -1;
+ this->is_ready = false;
this->size_bytes = this->img_file.size();
+ this->remain_size = 0;
+ this->write_size = 0;
this->set_fpos(0);
@@ -66,10 +68,15 @@ int BlockStorageDevice::set_block_size(const int blk_size) {
return -1;
}
- this->cache_size = this->cache_blocks * this->raw_blk_size;
+ uint32_t new_cache_size = this->cache_blocks * this->raw_blk_size;
+ // Preserve pointers held by an in-progress transfer when the cache geometry
+ // does not change during a media replacement.
+ if (new_cache_size != this->cache_size) {
+ this->cache_size = new_cache_size;
- // allocate data cache and fill it with zeroes
- this->data_cache = std::unique_ptr(new char[this->cache_size] ());
+ // allocate data cache and fill it with zeroes
+ this->data_cache = std::unique_ptr(new char[this->cache_size] ());
+ }
return 0;
}
diff --git a/devices/storage/cdromdrive.cpp b/devices/storage/cdromdrive.cpp
index 18379e1114..9b3943bcd1 100644
--- a/devices/storage/cdromdrive.cpp
+++ b/devices/storage/cdromdrive.cpp
@@ -32,10 +32,18 @@ CdromDrive::CdromDrive() : BlockStorageDevice(31, CDR_STD_DATA_SIZE, 0xfffffffe)
this->is_writeable = false;
}
-void CdromDrive::insert_image(std::string filename) {
- if (this->set_host_file(filename) < 0)
- ABORT_F("Could not open CD-ROM image file, %s", filename.c_str());
+bool CdromDrive::insert_image(const std::string& filename, bool notify_guest) {
+ if (this->medium_present()) {
+ LOG_F(ERROR, "Cannot insert CD-ROM image while media is present");
+ return false;
+ }
+
+ if (this->set_host_file(filename) < 0) {
+ LOG_F(ERROR, "Could not open CD-ROM image file, %s", filename.c_str());
+ return false;
+ }
+ this->data_offset = 0;
this->detect_raw_image();
// create single track descriptor
@@ -45,6 +53,30 @@ void CdromDrive::insert_image(std::string filename) {
// create Lead-out descriptor containing all data
this->tracks[1] = {LEAD_OUT_TRK_NUM, /*.trk_num*/ 0x14, /*.adr_ctrl*/
static_cast(this->size_blocks + 1) /*.start_lba*/};
+
+ this->media_changed |= notify_guest;
+ return true;
+}
+
+bool CdromDrive::eject_image() {
+ if (!this->is_ready)
+ return false;
+
+ this->is_ready = false;
+ this->img_file.close();
+
+ this->size_bytes = 0;
+ this->size_blocks = 0;
+ this->cur_fpos = 0;
+ this->write_size = 0;
+ this->remain_size = 0;
+ this->raw_blk_size = this->block_size;
+ this->data_offset = 0;
+ this->num_tracks = 0;
+ this->media_changed = false;
+ std::memset(this->tracks, 0, sizeof(this->tracks));
+
+ return true;
}
bool CdromDrive::detect_raw_image() {
diff --git a/devices/storage/cdromdrive.h b/devices/storage/cdromdrive.h
index 4fbfb32783..5b8ac9ef28 100644
--- a/devices/storage/cdromdrive.h
+++ b/devices/storage/cdromdrive.h
@@ -81,7 +81,14 @@ class CdromDrive : public BlockStorageDevice {
bool medium_present() { return this->is_ready; }
- void insert_image(std::string filename);
+ bool insert_image(const std::string& filename, bool notify_guest = false);
+ bool eject_image();
+
+ bool take_media_change() {
+ bool changed = this->media_changed;
+ this->media_changed = false;
+ return changed;
+ }
protected:
uint8_t hex_to_bcd(const uint8_t val);
@@ -101,6 +108,7 @@ class CdromDrive : public BlockStorageDevice {
uint8_t sw_lock_sup = 1; // drive supports locking/unlocking via SW
uint8_t sw_eject_sup = 1; // drive supports ejecting via SW
uint8_t drive_locked = 0; // 1 - drive is currently locked
+ bool media_changed = false;
uint8_t prevent_jump = 0; // prevent jumper not present
uint8_t more_support = 0;
uint16_t max_rd_speed = 706; // defaults to 4x
diff --git a/machines/machinebase.cpp b/machines/machinebase.cpp
index e6d07f29e3..c74f6ab4ea 100644
--- a/machines/machinebase.cpp
+++ b/machines/machinebase.cpp
@@ -20,6 +20,7 @@ along with this program. If not, see .
*/
#include
+#include
#include
#include
@@ -98,6 +99,28 @@ HWComponent* MachineBase::get_comp_by_type(HWCompType type) {
}
}
+CdromInsertionResult MachineBase::insert_cdrom_image(const std::string& path,
+ uint32_t drive) {
+ uint32_t drive_index = 0;
+
+ for (auto& device : this->device_map) {
+ auto* cdrom = dynamic_cast(device.second.get());
+ if (!cdrom)
+ continue;
+
+ if (drive_index++ == drive) {
+ if (cdrom->medium_present())
+ return CdromInsertionResult::MEDIA_PRESENT;
+
+ return cdrom->insert_image(path, true)
+ ? CdromInsertionResult::SUCCESS
+ : CdromInsertionResult::IMAGE_OPEN_FAILED;
+ }
+ }
+
+ return CdromInsertionResult::NO_DRIVE;
+}
+
int MachineBase::postinit_devices()
{
// Allow additional devices to be registered by device_postinit, in which
diff --git a/machines/machinebase.h b/machines/machinebase.h
index a7c177e466..60d1536831 100644
--- a/machines/machinebase.h
+++ b/machines/machinebase.h
@@ -27,6 +27,7 @@ along with this program. If not, see .
#ifndef MACHINE_BASE_H
#define MACHINE_BASE_H
+#include
#include