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.h b/core/hostevents.h
index d9b10888c0..2d1ef8ee1f 100644
--- a/core/hostevents.h
+++ b/core/hostevents.h
@@ -25,6 +25,7 @@ along with this program. If not, see .
#include
#include
+#include
class WindowEvent {
public:
@@ -119,6 +120,20 @@ class GamepadEvent {
uint8_t button;
};
+enum class CdromInsertionResult {
+ SUCCESS,
+ NO_DRIVE,
+ MEDIA_PRESENT,
+ IMAGE_OPEN_FAILED,
+};
+
+class CdromImageEvent {
+public:
+ std::string image_path;
+ CdromInsertionResult result = CdromInsertionResult::NO_DRIVE;
+ bool handled = false;
+};
+
class EventManager {
public:
static EventManager* get_instance() {
@@ -131,6 +146,9 @@ class EventManager {
void poll_events();
void set_keyboard_locale(uint32_t keyboard_id);
void post_keyboard_state_events();
+ void post_cdrom_event(CdromImageEvent& event) {
+ _cdrom_signal.emit(event);
+ }
template
void add_window_handler(T *inst, void (T::*func)(const WindowEvent&)) {
@@ -152,6 +170,11 @@ class EventManager {
_gamepad_signal.connect_method(inst, func);
}
+ template
+ void add_cdrom_handler(T* inst, void (T::*func)(CdromImageEvent&)) {
+ _cdrom_signal.connect_method(inst, func);
+ }
+
template
void add_post_handler(T *inst, void (T::*func)()) {
_post_signal.connect_method(inst, func);
@@ -162,6 +185,7 @@ class EventManager {
_mouse_signal.disconnect_all();
_keyboard_signal.disconnect_all();
_gamepad_signal.disconnect_all();
+ _cdrom_signal.disconnect_all();
_post_signal.disconnect_all();
}
@@ -179,6 +203,7 @@ class EventManager {
CoreSignal _mouse_signal;
CoreSignal _keyboard_signal;
CoreSignal _gamepad_signal;
+ CoreSignal _cdrom_signal;
CoreSignal<> _post_signal;
uint64_t events_captured = 0;
diff --git a/core/hostevents_sdl.cpp b/core/hostevents_sdl.cpp
index 91de0aac55..8c9ea07a56 100644
--- a/core/hostevents_sdl.cpp
+++ b/core/hostevents_sdl.cpp
@@ -322,6 +322,35 @@ void EventManager::poll_events() {
}
break;
+ case SDL_DROPFILE: {
+ const char* path = event.drop.file;
+ // TODO: Distinguish CD-ROM and floppy images once dynamic
+ // floppy insertion is supported.
+ CdromImageEvent cdrom_event{};
+ cdrom_event.image_path = path;
+ this->post_cdrom_event(cdrom_event);
+
+ switch (cdrom_event.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/ata/atapicdrom.h b/devices/common/ata/atapicdrom.h
index e5631dd377..faa427c759 100644
--- a/devices/common/ata/atapicdrom.h
+++ b/devices/common/ata/atapicdrom.h
@@ -38,7 +38,7 @@ class AtapiCdrom : public AtapiBaseDevice, public ScsiCdromCmds {
return std::unique_ptr(new AtapiCdrom("ATAPI-CDROM"));
}
- bool is_device_ready() override { return this->is_ready; }
+ bool is_device_ready() override { return this->medium_present(); }
uint8_t not_ready_reason() override { return ScsiError::MEDIUM_NOT_PRESENT; }
int device_postinit() override;
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..58186c8904 100644
--- a/devices/common/scsi/scsiblockcmds.cpp
+++ b/devices/common/scsi/scsiblockcmds.cpp
@@ -179,17 +179,20 @@ 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) {
- LOG_F(INFO, "START_STOP_UNIT: medium eject requested");
- }
+ if (this->cdb_ptr[4] & ScsiStartStopUnit::LOAD_EJECT)
+ this->eject_medium();
}
return ScsiPhase::STATUS;
}
+void ScsiBlockCmds::eject_medium() {
+ LOG_F(INFO, "START_STOP_UNIT: medium eject requested");
+}
+
int ScsiBlockCmds::read_capacity() {
uint32_t lba = this->get_lba();
diff --git a/devices/common/scsi/scsiblockcmds.h b/devices/common/scsi/scsiblockcmds.h
index 464b945eb4..fe83b73d2c 100644
--- a/devices/common/scsi/scsiblockcmds.h
+++ b/devices/common/scsi/scsiblockcmds.h
@@ -63,6 +63,7 @@ class ScsiBlockCmds : public ScsiCommonCmds {
protected:
virtual void process_command() override;
+ virtual void eject_medium();
uint32_t get_lba();
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..bdf42bd272 100644
--- a/devices/common/scsi/scsicdrom.h
+++ b/devices/common/scsi/scsicdrom.h
@@ -43,7 +43,8 @@ class ScsiCdrom : public ScsiPhysDevice, public ScsiCdromCmds {
}
protected:
- bool is_device_ready() override { return this->is_ready; }
+ bool is_device_ready() override { return this->medium_present(); }
+ 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..67946c8f40 100644
--- a/devices/common/scsi/scsicdromcmds.cpp
+++ b/devices/common/scsi/scsicdromcmds.cpp
@@ -62,6 +62,30 @@ void ScsiCdromCmds::process_command() {
phy_impl->switch_phase(next_phase);
}
+int ScsiCdromCmds::test_unit_ready() {
+ if (this->consume_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();
+}
+
+void ScsiCdromCmds::eject_medium() {
+ 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();
+ }
+}
+
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..f3cd2bffe0 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;
+ void eject_medium() override;
int get_apple_page_49(uint8_t subpage, uint8_t ctrl,
uint8_t *out_ptr, int avail_len);
diff --git a/devices/common/scsi/scsihd.cpp b/devices/common/scsi/scsihd.cpp
index 0e3013f447..fde42cba56 100644
--- a/devices/common/scsi/scsihd.cpp
+++ b/devices/common/scsi/scsihd.cpp
@@ -56,7 +56,7 @@ ScsiHardDisk::ScsiHardDisk(std::string name, int my_id) : ScsiPhysDevice(name, m
}
void ScsiHardDisk::insert_image(std::string filename) {
- if (this->set_host_file(filename) < 0)
+ if (!this->attach_image(filename))
ABORT_F("%s: could not open image file %s", this->name.c_str(), filename.c_str());
this->is_writeable = true;
diff --git a/devices/common/scsi/scsihd.h b/devices/common/scsi/scsihd.h
index 908c783df9..65ff6cf834 100644
--- a/devices/common/scsi/scsihd.h
+++ b/devices/common/scsi/scsihd.h
@@ -40,7 +40,7 @@ class ScsiHardDisk : public ScsiPhysDevice, public BlockStorageDevice, public Sc
void process_command() override;
protected:
- bool is_device_ready() override { return this->is_ready; }
+ bool is_device_ready() override { return this->image_attached(); }
void mode_select_6(uint8_t param_len);
diff --git a/devices/storage/blockstoragedevice.cpp b/devices/storage/blockstoragedevice.cpp
index f37ef47abf..599113ecba 100644
--- a/devices/storage/blockstoragedevice.cpp
+++ b/devices/storage/blockstoragedevice.cpp
@@ -39,22 +39,45 @@ BlockStorageDevice::BlockStorageDevice(const uint32_t cache_blocks,
}
BlockStorageDevice::~BlockStorageDevice() {
- this->img_file.close();
+ this->detach_image();
}
-int BlockStorageDevice::set_host_file(std::string file_path) {
- this->is_ready = false;
-
+bool BlockStorageDevice::attach_image(const std::string& file_path) {
if (!this->img_file.open(file_path))
- return -1;
+ return false;
- this->size_bytes = this->img_file.size();
+ this->is_ready = false;
+ this->size_bytes = this->img_file.size();
+ this->remain_size = 0;
+ this->write_size = 0;
+ this->data_offset = 0;
this->set_fpos(0);
this->is_ready = true;
- return this->set_block_size(this->block_size);
+ if (this->set_block_size(this->block_size) < 0) {
+ this->detach_image();
+ return false;
+ }
+
+ return true;
+}
+
+bool BlockStorageDevice::detach_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;
+
+ return true;
}
int BlockStorageDevice::set_block_size(const int blk_size) {
diff --git a/devices/storage/blockstoragedevice.h b/devices/storage/blockstoragedevice.h
index ed4fa6158a..db061e6e95 100644
--- a/devices/storage/blockstoragedevice.h
+++ b/devices/storage/blockstoragedevice.h
@@ -36,7 +36,10 @@ class BlockStorageDevice {
const uint64_t max_blocks=UINT64_MAX);
~BlockStorageDevice();
- int set_host_file(std::string file_path);
+ bool attach_image(const std::string& file_path);
+ bool detach_image();
+ bool image_attached() const { return this->is_ready; }
+
int set_block_size(const int blk_size);
int set_fpos(const uint64_t lba);
diff --git a/devices/storage/cdromdrive.cpp b/devices/storage/cdromdrive.cpp
index 18379e1114..a6549e6474 100644
--- a/devices/storage/cdromdrive.cpp
+++ b/devices/storage/cdromdrive.cpp
@@ -21,6 +21,7 @@ along with this program. If not, see .
/** @file Virtual CD-ROM device implementation. */
+#include
#include
#include
@@ -29,12 +30,46 @@ along with this program. If not, see .
#include
CdromDrive::CdromDrive() : BlockStorageDevice(31, CDR_STD_DATA_SIZE, 0xfffffffe) {
+ EventManager::get_instance()->add_cdrom_handler(
+ this, &CdromDrive::insert_image_event_handler);
+
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());
+void CdromDrive::insert_image_event_handler(CdromImageEvent& event) {
+ if (event.handled)
+ return;
+
+ if (this->medium_present()) {
+ event.result = CdromInsertionResult::MEDIA_PRESENT;
+ return;
+ }
+
+ event.handled = true;
+ if (this->insert_image(event.image_path)) {
+ this->media_change_pending = true;
+ event.result = CdromInsertionResult::SUCCESS;
+ } else {
+ event.result = CdromInsertionResult::IMAGE_OPEN_FAILED;
+ }
+}
+
+bool CdromDrive::consume_media_change() {
+ bool pending = this->media_change_pending;
+ this->media_change_pending = false;
+ return pending;
+}
+
+bool CdromDrive::insert_image(const std::string& filename) {
+ if (this->medium_present()) {
+ LOG_F(ERROR, "Cannot insert CD-ROM image while media is present");
+ return false;
+ }
+
+ if (!this->attach_image(filename)) {
+ LOG_F(ERROR, "Could not open CD-ROM image file, %s", filename.c_str());
+ return false;
+ }
this->detect_raw_image();
@@ -45,6 +80,19 @@ 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*/};
+
+ return true;
+}
+
+bool CdromDrive::eject_image() {
+ if (!this->detach_image())
+ return false;
+
+ this->num_tracks = 0;
+ this->media_change_pending = 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..66ac2914f7 100644
--- a/devices/storage/cdromdrive.h
+++ b/devices/storage/cdromdrive.h
@@ -74,16 +74,22 @@ constexpr auto CDROM_MAX_TRACKS = 100;
constexpr auto LEAD_OUT_TRK_NUM = 0xAA;
constexpr auto CDR_STD_DATA_SIZE = 2048;
+class CdromImageEvent;
+
class CdromDrive : public BlockStorageDevice {
public:
CdromDrive();
virtual ~CdromDrive() = default;
- bool medium_present() { return this->is_ready; }
+ bool medium_present() const { return this->image_attached(); }
- void insert_image(std::string filename);
+ bool insert_image(const std::string& filename);
+ bool eject_image();
protected:
+ void insert_image_event_handler(CdromImageEvent& event);
+ bool consume_media_change();
+
uint8_t hex_to_bcd(const uint8_t val);
AddrMsf lba_to_msf(const int lba);
bool detect_raw_image();
@@ -101,6 +107,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_change_pending = false; // pending guest notification
uint8_t prevent_jump = 0; // prevent jumper not present
uint8_t more_support = 0;
uint16_t max_rd_speed = 706; // defaults to 4x