Files
86Box/src/cdrom/cdrom.c

3493 lines
104 KiB
C

/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Generic CD-ROM drive core.
*
* Authors: Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2018-2021 Miran Grca.
*/
#include <inttypes.h>
#ifdef ENABLE_CDROM_LOG
#include <stdarg.h>
#endif
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/config.h>
#include <86box/cdrom.h>
#include <86box/cdrom_image.h>
#include <86box/cdrom_image_writable.h>
#include <86box/cdrom_interface.h>
#ifdef USE_CDROM_MITSUMI
#include <86box/cdrom_mitsumi.h>
#endif
#include <86box/cdrom_mke.h>
#include <86box/log.h>
#include <86box/plat.h>
#include <86box/plat_cdrom_ioctl.h>
#include <86box/scsi.h>
#include <86box/scsi_device.h>
#include <86box/scsi_cdrom.h>
#include <86box/sound.h>
#include <86box/ui.h>
#define RAW_SECTOR_SIZE 2352
#define MIN_SEEK 2000
#define MAX_SEEK 333333
cdrom_t cdrom[CDROM_NUM] = { 0 };
int cdrom_interface_current;
int cdrom_assigned_letters = 0;
#ifdef ENABLE_CDROM_LOG
int cdrom_do_log = ENABLE_CDROM_LOG;
static void
cdrom_log(void *priv, const char *fmt, ...)
{
if (cdrom_do_log) {
va_list ap;
va_start(ap, fmt);
log_out(priv, fmt, ap);
va_end(ap);
}
}
#else
# define cdrom_log(priv, fmt, ...)
#endif
static void process_mode1(cdrom_t *dev, const int cdrom_sector_flags,
uint8_t *b);
static void process_mode2_non_xa(cdrom_t *dev, const int cdrom_sector_flags,
uint8_t *b);
static void process_mode2_xa_form1(cdrom_t *dev, const int cdrom_sector_flags,
uint8_t *b);
static void process_mode2_xa_form2(cdrom_t *dev, const int cdrom_sector_flags,
uint8_t *b);
typedef void (*cdrom_process_data_t)(cdrom_t *dev, const int cdrom_sector_flags,
uint8_t *b);
static cdrom_process_data_t cdrom_process_data[4] = { process_mode1, process_mode2_non_xa,
process_mode2_xa_form1, process_mode2_xa_form2 };
#ifdef ENABLE_CDROM_LOG
static char * cdrom_req_modes[14] = { "Any", "Audio", "Mode 1", "Mode 2",
"CD-I/XA Mode 2 Form 1", "CD-I/XA Mode 2 Form 2", "Unk", "Unk",
"Any Data", "Any Data - 4",
"CD-I/XA Mode 2 Form 1", "CD-I/XA Mode 2 Form 1 - 4",
"Any CD-I/XA Data", "Any CD-I/XA Data - 4" };
static char * cdrom_modes[4] = { "Mode 1", "Mode 2", "CD-I/XA Mode 2 Form 1", "CD-I/XA Mode 2 Form 2" };
#endif
static uint8_t cdrom_mode_masks[14] = { 0x0f, 0x00, 0x01, 0x02, 0x04, 0x08, 0x00, 0x00,
0x05, 0x05, 0x04, 0x04, 0x0c, 0x0c };
static uint8_t status_codes[2][16] = { { 0x13, 0x15, 0x15, 0x15, 0x12, 0x11, 0x13, 0x13,
0x12, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15 },
{ 0x00, 0x00, 0x00, 0x00, 0x15, 0x11, 0x00, 0x00,
0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
static int mult = 1;
static int part = 0;
static int ecc_diff = 288;
static const device_t cdrom_interface_none_device = {
.name = "None",
.internal_name = "none",
.flags = 0,
.local = 0,
.init = NULL,
.close = NULL,
.reset = NULL,
.available = NULL,
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};
static const struct {
const device_t *device;
} controllers[] = {
// clang-format off
{ &cdrom_interface_none_device },
#ifdef USE_CDROM_MITSUMI
{ &mitsumi_cdrom_device },
#endif
{ &mke_cdrom_noncreative_device },
{ &mke_cdrom_device },
{ NULL }
// clang-format on
};
/* Private functions. */
static void
cdrom_generate_name(const int type, char *name, const int internal)
{
char elements[3][512] = { 0 };
memcpy(elements[0], cdrom_drive_types[type].vendor,
strlen(cdrom_drive_types[type].vendor) + 1);
if (internal) for (int i = 0; i < strlen(elements[0]); i++)
if (elements[0][i] == ' ')
elements[0][i] = '_';
if (internal) {
int j = 0;
for (int i = 0; i <= strlen(cdrom_drive_types[type].model); i++)
if (cdrom_drive_types[type].model[i] != ':')
elements[1][j++] = cdrom_drive_types[type].model[i];
} else
memcpy(elements[1], cdrom_drive_types[type].model,
strlen(cdrom_drive_types[type].model) + 1);
char *s = strstr(elements[1], " ");
if (s != NULL)
s[0] = 0x00;
if (internal) for (int i = 0; i < strlen(elements[1]); i++)
if (elements[1][i] == ' ')
elements[1][i] = '_';
memcpy(elements[2], cdrom_drive_types[type].revision,
strlen(cdrom_drive_types[type].revision) + 1);
s = strstr(elements[2], " ");
if (s != NULL)
s[0] = 0x00;
if (internal) for (int i = 0; i < strlen(elements[2]); i++)
if (elements[2][i] == ' ')
elements[2][i] = '_';
if (internal)
sprintf(name, "%s_%s_%s", elements[0], elements[1], elements[2]);
else if (cdrom_drive_types[type].speed == -1)
sprintf(name, "%s %s %s", elements[0], elements[1], elements[2]);
else
sprintf(name, "%s %s %s (%ix)", elements[0], elements[1],
elements[2], cdrom_drive_types[type].speed);
}
static double
cdrom_get_short_seek(const cdrom_t *dev)
{
switch (dev->cur_speed) {
case 0:
log_fatal(dev->log, "0x speed\n");
return 0.0;
case 1:
return 240.0;
case 2:
return 160.0;
case 3:
return 150.0;
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
return 112.0;
case 12:
case 13:
case 14:
case 15:
return 75.0;
case 16:
case 17:
case 18:
case 19:
return 58.0;
case 20:
case 21:
case 22:
case 23:
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
case 48:
return 50.0;
default:
/* 24-32, 52+ */
return 45.0;
}
}
static double
cdrom_get_long_seek(const cdrom_t *dev)
{
switch (dev->cur_speed) {
case 0:
log_fatal(dev->log, "0x speed\n");
return 0.0;
case 1:
return 1446.0;
case 2:
return 1000.0;
case 3:
return 900.0;
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
return 675.0;
case 12:
case 13:
case 14:
case 15:
return 400.0;
case 16:
case 17:
case 18:
case 19:
return 350.0;
case 20:
case 21:
case 22:
case 23:
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
case 48:
return 300.0;
default:
/* 24-32, 52+ */
return 270.0;
}
}
static void
msf_from_bcd(int *m, int *s, int *f)
{
*m = bcd2bin(*m);
*s = bcd2bin(*s);
*f = bcd2bin(*f);
}
static void
msf_to_bcd(int *m, int *s, int *f)
{
*m = bin2bcd(*m);
*s = bin2bcd(*s);
*f = bin2bcd(*f);
}
void
cdrom_compute_ecc_block(cdrom_t *dev, uint8_t *parity, const uint8_t *data,
uint32_t major_count, uint32_t minor_count,
uint32_t major_mult, uint32_t minor_inc, int m2f1)
{
uint32_t size = major_count * minor_count;
for (uint32_t major = 0; major < major_count; ++major) {
uint32_t index = (major >> 1) * major_mult + (major & 1);
uint8_t ecc_a = 0;
uint8_t ecc_b = 0;
for (uint32_t minor = 0; minor < minor_count; ++minor) {
uint8_t temp = data[index];
if (m2f1 && (index < 4))
temp = 0x00;
index += minor_inc;
if (index >= size)
index -= size;
ecc_a ^= temp;
ecc_b ^= temp;
ecc_a = dev->_F_LUT[ecc_a];
}
parity[major] = dev->_B_LUT[dev->_F_LUT[ecc_a] ^ ecc_b];
parity[major + major_count] = parity[major] ^ ecc_b;
}
}
static void
cdrom_generate_ecc_data(cdrom_t *dev, const uint8_t *data, int m2f1)
{
/* Compute ECC P code. */
cdrom_compute_ecc_block(dev, dev->p_parity, data, 86, 24, 2, 86, m2f1);
/* Compute ECC Q code. */
cdrom_compute_ecc_block(dev, dev->q_parity, data, 52, 43, 86, 88, m2f1);
}
static int
cdrom_is_sector_good(cdrom_t *dev, const uint8_t *b, const uint8_t mode2, const uint8_t form)
{
int ret = 1;
if (!dev->no_check && (dev->cd_status != CD_STATUS_DVD) && (!mode2 || (form == 1))) {
if (mode2 && (form == 1)) {
const uint32_t crc = cdrom_crc32(0xffffffff, &(b[16]), 2056) ^ 0xffffffff;
ret = ret && (crc == (*(uint32_t *) &(b[2072])));
} else if (!mode2) {
const uint32_t crc = cdrom_crc32(0xffffffff, b, 2064) ^ 0xffffffff;
ret = ret && (crc == (*(uint32_t *) &(b[2064])));
}
cdrom_generate_ecc_data(dev, &(b[12]), mode2 && (form == 1));
ret = ret && !memcmp(dev->p_parity, &(b[2076]), 172);
ret = ret && !memcmp(dev->q_parity, &(b[2248]), 104);
}
return ret;
}
static int
read_data(cdrom_t *dev, const uint32_t lba, int check)
{
int ret = 1;
int form = 0;
if (dev->cached_sector != lba) {
dev->cached_sector = lba;
ret = dev->ops->read_sector(dev->local,
dev->raw_buffer[dev->cur_buf ^ 1], lba);
if ((ret > 0) && check) {
if (dev->mode2) {
if (dev->raw_buffer[dev->cur_buf ^ 1][0x000f] == 0x01)
/*
Use Mode 1, since evidently specification-violating
discs exist.
*/
dev->mode2 = 0;
else if (dev->raw_buffer[dev->cur_buf ^ 1][0x0012] ==
dev->raw_buffer[dev->cur_buf ^ 1][0x0016])
form = ((dev->raw_buffer[dev->cur_buf ^ 1][0x0012] &
0x20) >> 5) + 1;
} else if (dev->raw_buffer[dev->cur_buf ^ 1][0x000f] == 0x02)
dev->mode2 = 1;
if (!cdrom_is_sector_good(dev, dev->raw_buffer[dev->cur_buf ^ 1], dev->mode2, form))
ret = -1;
}
if (ret <= 0) {
memset(dev->raw_buffer[dev->cur_buf ^ 1], 0x00, 2448);
dev->cached_sector = -1;
}
dev->cur_buf ^= 1;
}
return ret;
}
static void
cdrom_get_subchannel(cdrom_t *dev, const uint32_t lba,
subchannel_t *subc, const int cooked)
{
uint8_t q[16] = { 0 };
if (lba != dev->cached_sector)
dev->cached_sector = -1;
(void) read_data(dev, lba, 0);
for (int i = 0; i < 12; i++)
for (int j = 0; j < 8; j++)
q[i] |= ((dev->raw_buffer[dev->cur_buf][RAW_SECTOR_SIZE +
(i << 3) + j] >> 6) & 0x01) << (7 - j);
if (cooked) {
uint8_t temp = (q[0] >> 4) | ((q[0] & 0xf) << 4);
q[0] = temp;
for (int i = 1; i < 10; i++) {
temp = bcd2bin(q[i]);
q[i] = temp;
}
}
subc->attr = q[0];
subc->track = q[1];
subc->index = q[2];
subc->rel_m = q[3];
subc->rel_s = q[4];
subc->rel_f = q[5];
subc->abs_m = q[7];
subc->abs_s = q[8];
subc->abs_f = q[9];
}
static void
read_toc_identify_sessions(const raw_track_info_t *rti, const int num, unsigned char *b)
{
/* Bytes 2 and 3 = Number of first and last sessions */
b[2] = 0xff;
b[3] = 0x00;
for (int i = (num - 1); i >= 0; i--) {
if (rti[i].session < b[2])
b[2] = rti[i].session;
}
for (int i = 0; i < num; i++) {
if (rti[i].session > b[3])
b[3] = rti[i].session;
}
}
static int
find_track(const raw_track_info_t *trti, const int num, const int first)
{
int ret = -1;
if (first) {
for (int i = 0; i < num; i++)
if ((trti[i].point >= 1) && (trti[i].point <= 99)) {
ret = i;
break;
}
} else {
for (int i = (num - 1); i >= 0; i--)
if ((trti[i].point >= 1) && (trti[i].point <= 99)) {
ret = i;
break;
}
}
return ret;
}
static int
find_last_lead_out(const raw_track_info_t *trti, const int num)
{
int ret = -1;
for (int i = (num - 1); i >= 0; i--)
if (trti[i].point == 0xa2) {
ret = i;
break;
}
return ret;
}
static int
find_specific_track(const raw_track_info_t *trti, const int num, const int track)
{
int ret = -1;
if ((track >= 1) && (track <= 99)) {
for (int i = (num - 1); i >= 0; i--)
if (trti[i].point == track) {
ret = i;
break;
}
}
return ret;
}
static int
read_toc_normal(const cdrom_t *dev, unsigned char *b,
unsigned char start_track, const int msf,
const int sony)
{
uint8_t rti[65536] = { 0 };
uint8_t prti[65536] = { 0 };
const raw_track_info_t *trti = (raw_track_info_t *) rti;
raw_track_info_t * tprti = (raw_track_info_t *) prti;
int num = 0;
int len = 4;
int t = -1;
if ((dev->is_bcd || dev->is_chinon) && (start_track < 0xa0))
start_track = bcd2bin(start_track);
cdrom_log(dev->log, "read_toc_normal(%016" PRIXPTR ", %016" PRIXPTR ", %02X, %i, %i)\n",
(uintptr_t) dev, (uintptr_t) b, start_track, msf, sony);
dev->ops->get_raw_track_info(dev->local, &num, rti);
if (num > 0) {
int j = 0;
for (int i = 0; i < num; i++) {
if ((trti[i].point >= 0x01) && (trti[i].point <= 0x63)) {
tprti[j] = trti[i];
if ((t == -1) && (tprti[j].point >= start_track))
t = j;
cdrom_log(dev->log, "Sorted %03i = Unsorted %03i\n", j, i);
j++;
}
}
/* Bytes 2 and 3 = Number of first and last tracks found before lead out */
b[2] = tprti[0].point;
b[3] = tprti[j - 1].point;
for (int i = (num - 1); i >= 0; i--) {
if (trti[i].point == 0xa2) {
tprti[j] = trti[i];
tprti[j].point = 0xaa;
if ((t == -1) && (tprti[j].point >= start_track))
t = j;
cdrom_log(dev->log, "Sorted %03i = Unsorted %03i\n", j, i);
j++;
break;
}
}
if (t != -1) for (int i = t; i < j; i++) {
#ifdef ENABLE_CDROM_LOG
uint8_t *c = &(b[len]);
#endif
if (!sony)
b[len++] = 0; /* Reserved */
b[len++] = tprti[i].adr_ctl; /* ADR/CTL */
if ((dev->is_bcd || dev->is_chinon) && (tprti[i].point >= 1) &&
(tprti[i].point <= 99))
b[len++] = bin2bcd(tprti[i].point); /* Track number */
else
b[len++] = tprti[i].point; /* Track number */
if (!sony)
b[len++] = 0; /* Reserved */
if (msf) {
b[len++] = 0;
/* NEC CDR-260 speaks BCD. */
if (dev->is_bcd) {
int m = tprti[i].pm;
int s = tprti[i].ps;
int f = tprti[i].pf;
msf_to_bcd(&m, &s, &f);
b[len++] = m;
b[len++] = s;
b[len++] = f;
} else {
b[len++] = tprti[i].pm;
b[len++] = tprti[i].ps;
b[len++] = tprti[i].pf;
}
} else {
const uint32_t temp = MSFtoLBA(tprti[i].pm, tprti[i].ps,
tprti[i].pf) - 150;
b[len++] = temp >> 24;
b[len++] = temp >> 16;
b[len++] = temp >> 8;
b[len++] = temp;
}
#ifdef ENABLE_CDROM_LOG
cdrom_log(dev->log, "Track %02X: %02X %02X %02X %02X %02X %02X %02X %02X\n",
i, c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]);
#endif
}
} else
b[2] = b[3] = 0;
return len;
}
static int
read_toc_session(const cdrom_t *dev, unsigned char *b, const int msf)
{
uint8_t rti[65536] = { 0 };
const raw_track_info_t *t = (raw_track_info_t *) rti;
const raw_track_info_t *first = NULL;
int num = 0;
int len = 4;
dev->ops->get_raw_track_info(dev->local, &num, rti);
/* Bytes 2 and 3 = Number of first and last sessions */
read_toc_identify_sessions((raw_track_info_t *) rti, num, b);
cdrom_log(dev->log, "read_toc_session(%016" PRIXPTR ", %016" PRIXPTR ", %i)\n",
(uintptr_t) dev, (uintptr_t) b, msf);
if (num != 0) {
for (int i = 0; i < num; i++) {
if ((t[i].session == b[3]) && (t[i].point >= 0x01) && (t[i].point <= 0x63)) {
first = &(t[i]);
break;
}
}
if (first != NULL) {
b[len++] = 0x00;
b[len++] = first->adr_ctl;
if ((dev->is_bcd || dev->is_chinon) && (first->point >= 1) &&
(first->point <= 99))
b[len++] = bin2bcd(first->point);
else
b[len++] = first->point;
b[len++] = 0x00;
if (msf) {
b[len++] = 0x00;
/* NEC CDR-260 speaks BCD. */
if (dev->is_bcd) {
int m = first->pm;
int s = first->ps;
int f = first->pf;
msf_to_bcd(&m, &s, &f);
b[len++] = m;
b[len++] = s;
b[len++] = f;
} else {
b[len++] = first->pm;
b[len++] = first->ps;
b[len++] = first->pf;
}
} else {
const uint32_t temp = MSFtoLBA(first->pm, first->ps,
first->pf) - 150;
b[len++] = temp >> 24;
b[len++] = temp >> 16;
b[len++] = temp >> 8;
b[len++] = temp;
}
}
}
if (len == 4)
memset(&(b[len += 8]), 0x00, 8);
return len;
}
static int
read_toc_raw(const cdrom_t *dev, unsigned char *b, const unsigned char start_track)
{
uint8_t rti[65536] = { 0 };
const raw_track_info_t *t = (raw_track_info_t *) rti;
int num = 0;
int len = 4;
/* Bytes 2 and 3 = Number of first and last sessions */
read_toc_identify_sessions((raw_track_info_t *) rti, num, b);
cdrom_log(dev->log, "read_toc_raw(%016" PRIXPTR ", %016" PRIXPTR ", %02X)\n",
(uintptr_t) dev, (uintptr_t) b, start_track);
dev->ops->get_raw_track_info(dev->local, &num, rti);
if (num != 0) for (int i = 0; i < num; i++)
if (t[i].session >= start_track) {
memcpy(&(b[len]), &(t[i]), 11);
if ((dev->is_bcd || dev->is_chinon) && (b[3] >= 1) && (b[3] <= 99))
b[3] = bin2bcd(b[3]);
for (int j = 0; j < 3; j++)
if (dev->is_bcd) {
b[4 + j] = bin2bcd(b[4 + j]);
b[8 + j] = bin2bcd(b[8 + j]);
}
len += 11;
}
return len;
}
static int
track_type_is_valid(UNUSED(const cdrom_t *dev), const int type, const int flags, const int audio,
const int mode2)
{
if (!(flags & 0x70) && (flags & 0xf8)) { /* 0x08/0x80/0x88 are illegal modes */
cdrom_log(dev->log, "[Any Mode] 0x08/0x80/0x88 are illegal modes\n");
return 0;
}
if ((type != 1) && !audio) {
if ((flags & 0x06) == 0x06) {
cdrom_log(dev->log, "[Any Data Mode] Invalid error flags\n");
return 0;
}
if (((flags & 0x700) == 0x300) || ((flags & 0x700) > 0x400)) {
cdrom_log(dev->log, "[Any Data Mode] Invalid subchannel data flags (%02X)\n",
flags & 0x700);
return 0;
}
if ((flags & 0x18) == 0x08) { /* EDC/ECC without user data is an illegal mode */
cdrom_log(dev->log, "[Any Data Mode] EDC/ECC without user data is an "
"illegal mode\n");
return 0;
}
if (((flags & 0xf0) == 0x90) || ((flags & 0xf0) == 0xc0)) { /* 0x90/0x98/0xC0/0xC8 are illegal modes */
cdrom_log(dev->log, "[Any Data Mode] 0x90/0x98/0xC0/0xC8 are illegal modes\n");
return 0;
}
if (((type > 3) && (type != 8)) || (mode2 && (mode2 & 0x03))) {
if ((flags & 0xf0) == 0x30) { /* 0x30/0x38 are illegal modes */
cdrom_log(dev->log, "[Any XA Mode 2] 0x30/0x38 are illegal modes\n");
return 0;
}
if (((flags & 0xf0) == 0xb0) || ((flags & 0xf0) == 0xd0)) { /* 0xBx and 0xDx are illegal modes */
cdrom_log(dev->log, "[Any XA Mode 2] 0xBx and 0xDx are illegal modes\n");
return 0;
}
}
}
return 1;
}
static int
read_audio(cdrom_t *dev, const uint32_t lba, uint8_t *b)
{
const int ret = read_data(dev, lba, 0);
memcpy(b, dev->raw_buffer[dev->cur_buf], 2352);
dev->cdrom_sector_size = 2352;
return ret;
}
static void
process_mode1(cdrom_t *dev, const int cdrom_sector_flags, uint8_t *b)
{
dev->cdrom_sector_size = 0;
if (cdrom_sector_flags & 0x80) {
/* Sync */
cdrom_log(dev->log, "[Mode 1] Sync\n");
memcpy(b, dev->raw_buffer[dev->cur_buf], 12);
dev->cdrom_sector_size += 12;
b += 12;
}
if (cdrom_sector_flags & 0x20) {
/* Header */
cdrom_log(dev->log, "[Mode 1] Header\n");
memcpy(b, dev->raw_buffer[dev->cur_buf] + 12, 4);
dev->cdrom_sector_size += 4;
b += 4;
}
if (cdrom_sector_flags & 0x40) {
/* Sub-header */
if (!(cdrom_sector_flags & 0x10)) {
/* No user data */
cdrom_log(dev->log, "[Mode 1] Sub-header\n");
memcpy(b, dev->raw_buffer[dev->cur_buf] + 16, 8);
dev->cdrom_sector_size += 8;
b += 8;
}
}
if (cdrom_sector_flags & 0x10) {
/* User data */
cdrom_log(dev->log, "[Mode 1] User data\n");
if (mult > 1) {
memcpy(b, dev->raw_buffer[dev->cur_buf] + 16 +
(part * dev->sector_size), dev->sector_size);
dev->cdrom_sector_size += dev->sector_size;
b += dev->sector_size;
} else {
memcpy(b, dev->raw_buffer[dev->cur_buf] + 16, 2048);
dev->cdrom_sector_size += 2048;
b += 2048;
}
}
if (cdrom_sector_flags & 0x08) {
/* EDC/ECC */
cdrom_log(dev->log, "[Mode 1] EDC/ECC\n");
memcpy(b, dev->raw_buffer[dev->cur_buf] + 2064, (288 - ecc_diff));
dev->cdrom_sector_size += (288 - ecc_diff);
}
}
static void
process_mode2_non_xa(cdrom_t *dev, const int cdrom_sector_flags,
uint8_t *b)
{
dev->cdrom_sector_size = 0;
if (cdrom_sector_flags & 0x80) {
/* Sync */
cdrom_log(dev->log, "[Mode 2 Formless] Sync\n");
memcpy(b, dev->raw_buffer[dev->cur_buf], 12);
dev->cdrom_sector_size += 12;
b += 12;
}
if (cdrom_sector_flags & 0x20) {
/* Header */
cdrom_log(dev->log, "[Mode 2 Formless] Header\n");
memcpy(b, dev->raw_buffer[dev->cur_buf] + 12, 4);
dev->cdrom_sector_size += 4;
b += 4;
}
/* Mode 1 sector, expected type is 1 type. */
if (cdrom_sector_flags & 0x40) {
/* Sub-header */
cdrom_log(dev->log, "[Mode 2 Formless] Sub-header\n");
memcpy(b, dev->raw_buffer[dev->cur_buf] + 16, 8);
dev->cdrom_sector_size += 8;
b += 8;
}
if (cdrom_sector_flags & 0x10) {
/* User data */
cdrom_log(dev->log, "[Mode 2 Formless] User data\n");
memcpy(b, dev->raw_buffer[dev->cur_buf] + 24, (2336 - ecc_diff));
dev->cdrom_sector_size += (2336 - ecc_diff);
}
}
static void
process_mode2_xa_form1(cdrom_t *dev, const int cdrom_sector_flags,
uint8_t *b)
{
dev->cdrom_sector_size = 0;
if (cdrom_sector_flags & 0x80) {
/* Sync */
cdrom_log(dev->log, "[XA Mode 2 Form 1] Sync\n");
memcpy(b, dev->raw_buffer[dev->cur_buf], 12);
dev->cdrom_sector_size += 12;
b += 12;
}
if (cdrom_sector_flags & 0x20) {
/* Header */
cdrom_log(dev->log, "[XA Mode 2 Form 1] Header\n");
memcpy(b, dev->raw_buffer[dev->cur_buf] + 12, 4);
dev->cdrom_sector_size += 4;
b += 4;
}
if (cdrom_sector_flags & 0x40) {
/* Sub-header */
cdrom_log(dev->log, "[XA Mode 2 Form 1] Sub-header\n");
memcpy(b, dev->raw_buffer[dev->cur_buf] + 16, 8);
dev->cdrom_sector_size += 8;
b += 8;
}
if (cdrom_sector_flags & 0x10) {
/* User data */
cdrom_log(dev->log, "[XA Mode 2 Form 1] User data\n");
if (mult > 1) {
memcpy(b, dev->raw_buffer[dev->cur_buf] + 24 +
(part * dev->sector_size), dev->sector_size);
dev->cdrom_sector_size += dev->sector_size;
b += dev->sector_size;
} else {
memcpy(b, dev->raw_buffer[dev->cur_buf] + 24, 2048);
dev->cdrom_sector_size += 2048;
b += 2048;
}
}
if (cdrom_sector_flags & 0x08) {
/* EDC/ECC */
cdrom_log(dev->log, "[XA Mode 2 Form 1] EDC/ECC\n");
memcpy(b, dev->raw_buffer[dev->cur_buf] + 2072, (280 - ecc_diff));
dev->cdrom_sector_size += (280 - ecc_diff);
}
}
static void
process_mode2_xa_form2(cdrom_t *dev, const int cdrom_sector_flags,
uint8_t *b)
{
dev->cdrom_sector_size = 0;
if (cdrom_sector_flags & 0x80) {
/* Sync */
cdrom_log(dev->log, "[XA Mode 2 Form 2] Sync\n");
memcpy(b, dev->raw_buffer[dev->cur_buf], 12);
dev->cdrom_sector_size += 12;
b += 12;
}
if (cdrom_sector_flags & 0x20) {
/* Header */
cdrom_log(dev->log, "[XA Mode 2 Form 2] Header\n");
memcpy(b, dev->raw_buffer[dev->cur_buf] + 12, 4);
dev->cdrom_sector_size += 4;
b += 4;
}
if (cdrom_sector_flags & 0x40) {
/* Sub-header */
cdrom_log(dev->log, "[XA Mode 2 Form 2] Sub-header\n");
memcpy(b, dev->raw_buffer[dev->cur_buf] + 16, 8);
dev->cdrom_sector_size += 8;
b += 8;
}
if (cdrom_sector_flags & 0x10) {
/* User data */
cdrom_log(dev->log, "[XA Mode 2 Form 2] User data\n");
memcpy(b, dev->raw_buffer[dev->cur_buf] + 24,
(2328 - ecc_diff));
dev->cdrom_sector_size += (2328 - ecc_diff);
}
}
static void
process_c2(cdrom_t *dev, const int cdrom_sector_flags, uint8_t *b)
{
if ((cdrom_sector_flags & 0x06) == 0x02) {
/* Add error flags. */
cdrom_log(dev->log, "Error flags\n");
memcpy(b + dev->cdrom_sector_size, dev->extra_buffer, 294);
dev->cdrom_sector_size += 294;
} else if ((cdrom_sector_flags & 0x06) == 0x04) {
/* Add error flags. */
cdrom_log(dev->log, "Full error flags\n");
memcpy(b + dev->cdrom_sector_size, dev->extra_buffer, 296);
dev->cdrom_sector_size += 296;
}
}
static void
cdrom_deinterleave_subch(uint8_t *d, const uint8_t *s)
{
for (int i = 0; i < 8 * 12; i++) {
int dmask = 0x80;
int smask = 1 << (7 - (i / 12));
(*d) = 0;
for (int j = 0; j < 8; j++) {
(*d) |= (s[(i % 12) * 8 + j] & smask) ? dmask : 0;
dmask >>= 1;
}
d++;
}
}
static void
process_c2_and_subch(cdrom_t *dev, const int cdrom_sector_flags,
uint8_t *b)
{
if (dev->c2_first)
process_c2(dev, cdrom_sector_flags, b);
if ((cdrom_sector_flags & 0x700) == 0x100) {
cdrom_log(dev->log, "Raw subchannel data\n");
memcpy(b + dev->cdrom_sector_size, dev->raw_buffer[dev->cur_buf] +
2352, 96);
dev->cdrom_sector_size += 96;
} else if ((cdrom_sector_flags & 0x700) == 0x200) {
cdrom_log(dev->log, "Q subchannel data\n");
memcpy(b + dev->cdrom_sector_size, dev->raw_buffer[dev->cur_buf] +
2352, 16);
dev->cdrom_sector_size += 16;
} else if ((cdrom_sector_flags & 0x700) == 0x400) {
cdrom_log(dev->log, "R/W subchannel data\n");
cdrom_deinterleave_subch(b + dev->cdrom_sector_size,
dev->raw_buffer[dev->cur_buf] + 2352);
dev->cdrom_sector_size += 96;
}
if (!dev->c2_first)
process_c2(dev, cdrom_sector_flags, b);
}
static void
cdrom_drive_reset(cdrom_t *dev)
{
dev->priv = NULL;
dev->insert = NULL;
dev->close = NULL;
dev->get_volume = NULL;
dev->get_channel = NULL;
dev->cached_sector = -1;
if (cdrom_drive_types[dev->type].speed == -1)
dev->real_speed = dev->speed;
else
dev->real_speed = cdrom_drive_types[dev->type].speed;
}
static void
cdrom_unload(cdrom_t *dev)
{
if (dev->log != NULL) {
cdrom_log(dev->log, "CDROM: cdrom_unload(%s)\n", dev->image_path);
}
dev->cd_status = CD_STATUS_EMPTY;
dev->cached_sector = -1;
if (dev->local != NULL) {
dev->ops->close(dev->local);
dev->local = NULL;
}
dev->ops = NULL;
}
#ifdef ENABLE_CDROM_LOG
static void
cdrom_toc_dump(cdrom_t *dev)
{
uint8_t b[65536] = { 0 };
int len = cdrom_read_toc(dev, b, CD_TOC_RAW, 0, 0, 65536);
const char *fn2 = "d:\\86boxnew\\toc_cue.dmp";
FILE * fp = fopen(fn2, "wb");
fwrite(b, 1, len, fp);
fflush(fp);
fclose(fp);
cdrom_log(dev->log, "Written TOC of %i bytes to %s\n", len, fn2);
memset(b, 0x00, 65536);
len = cdrom_read_toc(dev, b, CD_TOC_NORMAL, 0, 0, 65536);
fn2 = "d:\\86boxnew\\toc_cue_cooked.dmp";
fp = fopen(fn2, "wb");
fwrite(b, 1, len, fp);
fflush(fp);
fclose(fp);
cdrom_log(dev->log, "Written cooked TOC of %i bytes to %s\n", len, fn2);
memset(b, 0x00, 65536);
len = cdrom_read_toc(dev, b, CD_TOC_SESSION, 0, 0, 65536);
fn2 = "d:\\86boxnew\\toc_cue_session.dmp";
fp = fopen(fn2, "wb");
fwrite(b, 1, len, fp);
fflush(fp);
fclose(fp);
cdrom_log(dev->log, "Written session TOC of %i bytes to %s\n", len, fn2);
}
#endif
/* Reset the CD-ROM Interface, whichever one that is. */
void
cdrom_interface_reset(void)
{
/* If we have a valid controller, add its device. */
if ((cdrom_interface_current > 0) &&
controllers[cdrom_interface_current].device)
device_add(controllers[cdrom_interface_current].device);
}
const char *
cdrom_interface_get_internal_name(const int cdinterface)
{
return device_get_internal_name(controllers[cdinterface].device);
}
int
cdrom_interface_get_from_internal_name(const char *s)
{
int c = 0;
while (controllers[c].device != NULL) {
if (!strcmp(controllers[c].device->internal_name, s))
return c;
c++;
}
return 0;
}
const device_t *
cdrom_interface_get_device(const int cdinterface)
{
return (controllers[cdinterface].device);
}
int
cdrom_interface_has_config(const int cdinterface)
{
const device_t *dev = cdrom_interface_get_device(cdinterface);
if (dev == NULL)
return 0;
if (!device_has_config(dev))
return 0;
return 1;
}
int
cdrom_interface_get_flags(const int cdinterface)
{
return (controllers[cdinterface].device->flags);
}
int
cdrom_interface_available(const int cdinterface)
{
return (device_available(controllers[cdinterface].device));
}
char *
cdrom_get_vendor(const int type)
{
return (char *) cdrom_drive_types[type].vendor;
}
void
cdrom_get_model(const int type, char *name, const int id)
{
if (!strcmp(cdrom_drive_types[type].vendor, EMU_NAME))
sprintf(name, "%s%02i", cdrom_drive_types[type].model, id);
else
sprintf(name, "%s", cdrom_drive_types[type].model);
}
char *
cdrom_get_revision(const int type)
{
return (char *) cdrom_drive_types[type].revision;
}
int
cdrom_get_scsi_std(const int type)
{
return cdrom_drive_types[type].scsi_std;
}
int
cdrom_is_early(const int type)
{
return (cdrom_drive_types[type].scsi_std == 1);
}
int
cdrom_is_dvd(const int type)
{
return (cdrom_drive_types[type].is_dvd == 1);
}
int
cdrom_is_generic(const int type)
{
return (cdrom_drive_types[type].speed == -1);
}
int
cdrom_is_caddy(const int type)
{
return cdrom_drive_types[type].caddy;
}
int
cdrom_get_speed(const int type)
{
return cdrom_drive_types[type].speed;
}
int
cdrom_get_inquiry_len(const int type)
{
return cdrom_drive_types[type].inquiry_len;
}
int
cdrom_get_transfer_max(const int type, const int mode)
{
return cdrom_drive_types[type].transfer_max[mode];
}
int
cdrom_has_dma(const int type)
{
return (cdrom_drive_types[type].transfer_max[2] != -1);
}
int
cdrom_get_type_count(void)
{
int count = 0;
while (1) {
if (strlen(cdrom_drive_types[count].vendor) == 0)
break;
else
count++;
}
return count;
}
void
cdrom_generate_name_mke(const int type, char *name)
{
char elements[2][512] = { 0 };
memcpy(elements[0], cdrom_drive_types[type].model,
strlen(cdrom_drive_types[type].model) + 1);
char *s = strstr(elements[0], " ");
if (s != NULL)
s[0] = 0x00;
memcpy(elements[1], cdrom_drive_types[type].revision,
strlen(cdrom_drive_types[type].revision) + 1);
s = strstr(elements[1], " ");
if (s != NULL)
s[0] = 0x00;
sprintf(name, "%s%s", elements[0], elements[1]);
}
void
cdrom_get_identify_model(const int type, char *name, const int id)
{
char elements[2][512] = { 0 };
memcpy(elements[0], cdrom_drive_types[type].vendor,
strlen(cdrom_drive_types[type].vendor) + 1);
memcpy(elements[1], cdrom_drive_types[type].model,
strlen(cdrom_drive_types[type].model) + 1);
char *s = strstr(elements[1], " ");
if (s != NULL)
s[0] = 0x00;
if (!strcmp(cdrom_drive_types[type].vendor, EMU_NAME))
sprintf(name, "%s%02i", elements[1], id);
else if (!strcmp(cdrom_drive_types[type].vendor, "ASUS"))
sprintf(name, "%s %s", elements[0], elements[1]);
else if (!strcmp(cdrom_drive_types[type].vendor, "NEC"))
sprintf(name, "%s %s", elements[0], elements[1]);
else if (!strcmp(cdrom_drive_types[type].vendor, "LITE-ON"))
sprintf(name, "%s", elements[1]);
else
sprintf(name, "%s %s", elements[0], elements[1]);
}
void
cdrom_get_name(const int type, char *name)
{
char n[2048] = { 0 };
cdrom_generate_name(type, n, 0);
if (cdrom_drive_types[type].bus_type == BUS_TYPE_SCSI)
sprintf(name, "[SCSI-%i] %s", cdrom_drive_types[type].scsi_std, n);
else
sprintf(name, "%s", n);
}
char *
cdrom_get_internal_name(const int type)
{
return (char *) cdrom_drive_types[type].internal_name;
}
int
cdrom_get_from_internal_name(const char *s)
{
int c = 0;
int found = 0;
while (strlen(cdrom_drive_types[c].internal_name) > 0) {
if (!strcmp((char *) cdrom_drive_types[c].internal_name, s)) {
found = 1;
break;
}
c++;
}
if (!found)
c = -1;
return c;
}
/* TODO: Configuration migration, remove when no longer needed. */
int
cdrom_get_from_name(const char *s)
{
int c = 0;
int found = 0;
char n[2048] = { 0 };
if (strcmp(s, "none")) {
while (strlen(cdrom_drive_types[c].internal_name) > 0) {
memset(n, 0x00, 2048);
cdrom_generate_name(c, n, 1);
/* Special case some names. */
if ((!strcmp(s, "86BOX_CD-ROM_1.00") && !strcmp(n, "86Box_86B_CD_3.50")) ||
(!strcmp(s, "TEAC_CD_532E_2.0A") && !strcmp(n, "TEAC_CD-532E_2.0A")) ||
!strcmp(n, s)) {
found = 1;
break;
}
c++;
}
}
if (!found) {
if (strcmp(s, "none")) {
wchar_t tempmsg[2048];
sprintf(n, "WARNING: CD-ROM \"%s\" not found - contact 86Box support\n", s);
swprintf(tempmsg, sizeof_w(tempmsg), L"%hs", n);
pclog("%s", n);
ui_msgbox_header(MBX_INFO,
plat_get_string(STRING_HW_NOT_AVAILABLE_TITLE),
tempmsg);
}
c = -1;
}
return c;
}
void
cdrom_set_type(const int model, const int type)
{
cdrom[model].type = type;
}
int
cdrom_get_type(const int model)
{
return cdrom[model].type;
}
int
cdrom_lba_to_msf_accurate(const int lba)
{
int pos = lba + 150;
const int f = pos % 75;
pos -= f;
pos /= 75;
const int s = pos % 60;
pos -= s;
pos /= 60;
const int m = pos;
return ((m << 16) | (s << 8) | f);
}
void
cdrom_interleave_subch(uint8_t *d, const uint8_t *s)
{
memset(d, 0x00, 96);
for (int i = 0; i < 8 * 12; i++) {
int smask = 0x80;
int dmask = 1 << (7 - (i / 12));
for (int j = 0; j < 8; j++) {
d[(i % 12) * 8 + j] |= ((*s) & smask) ? dmask : 0;
smask >>= 1;
}
s++;
}
}
double
cdrom_seek_time(const cdrom_t *dev)
{
uint32_t diff = dev->seek_diff;
const double sd = (double) (MAX_SEEK - MIN_SEEK);
if (diff < MIN_SEEK)
return 0.0;
if (diff > MAX_SEEK)
diff = MAX_SEEK;
diff -= MIN_SEEK;
return cdrom_get_short_seek(dev) +
((cdrom_get_long_seek(dev) * ((double) diff)) / sd);
}
void
cdrom_stop(cdrom_t *dev)
{
if (dev->cd_status > CD_STATUS_DVD)
dev->cd_status = CD_STATUS_STOPPED;
}
void
cdrom_seek(cdrom_t *dev, const uint32_t pos, const uint8_t vendor_type)
{
int m;
int s;
int f;
uint32_t real_pos = pos;
if (dev == NULL)
return;
cdrom_log(dev->log, "Seek to LBA %08X, vendor type = %02x.\n", pos, vendor_type);
switch (vendor_type) {
case 0x40:
m = bcd2bin((pos >> 24) & 0xff);
s = bcd2bin((pos >> 16) & 0xff);
f = bcd2bin((pos >> 8) & 0xff);
real_pos = MSFtoLBA(m, s, f) - 150;
break;
case 0x80:
real_pos = bcd2bin((pos >> 24) & 0xff);
break;
default:
break;
}
dev->seek_pos = real_pos;
cdrom_stop(dev);
}
int
cdrom_is_pre(const cdrom_t *dev, const uint32_t lba)
{
if (dev->ops && dev->ops->is_track_pre)
return dev->ops->is_track_pre(dev->local, lba);
return 0;
}
#include <86box/filters.h>
static void
cdrom_audio_deemphasize(int16_t *buffer)
{
for (int i = 0; i < 588; i++)
for (int j = 0; j < 2; j++)
buffer[(i * 2) + j] = deemph_iir(j, buffer[(i * 2) + j]);
}
int
cdrom_audio_callback(cdrom_t *dev, int16_t *output, const int len)
{
int ret = 1;
while (dev->cd_buflen < len) {
if (dev->seek_pos < dev->cd_end) {
ret = dev->ops->read_sector(dev->local,
dev->raw_buffer[dev->cur_buf ^ 1],
dev->seek_pos);
if (!dev->sound_on)
memset(dev->raw_buffer[dev->cur_buf ^ 1], 0x00, 2352);
dev->cur_buf ^= 1;
if (ret) {
cdrom_log(dev->log, "Read LBA %08X successful\n", dev->seek_pos);
dev->cached_sector = dev->seek_pos;
/* Q subchannel data in bit 6: 4-5-6-7-0-1-2-3. */
if ((dev->raw_buffer[dev->cur_buf][2353] >> 6) & 0x01)
/* Data sector, copy silence into buffer. */
memset((uint8_t *) &(dev->cd_buffer[dev->cd_buflen]),
0x00, RAW_SECTOR_SIZE);
else {
memcpy((uint8_t *) &(dev->cd_buffer[dev->cd_buflen]),
dev->raw_buffer[dev->cur_buf], RAW_SECTOR_SIZE);
if ((dev->raw_buffer[dev->cur_buf][2355] >> 6) & 0x01)
/* De-emphasize pre-emphasized audio. */
cdrom_audio_deemphasize(&(dev->cd_buffer[dev->cd_buflen]));
}
dev->seek_pos++;
dev->cd_buflen += (RAW_SECTOR_SIZE / 2);
ret = 1;
} else {
cdrom_log(dev->log, "Read LBA %08X failed\n", dev->seek_pos);
memset(&(dev->cd_buffer[dev->cd_buflen]), 0x00,
(CD_BUF_SIZE - dev->cd_buflen) * 2);
dev->cd_status = CD_STATUS_STOPPED;
dev->cd_buflen = len;
ret = 0;
}
} else {
cdrom_log(dev->log, "Playing completed\n");
memset(&dev->cd_buffer[dev->cd_buflen], 0x00, (CD_BUF_SIZE - dev->cd_buflen) * 2);
dev->cd_status = CD_STATUS_PLAYING_COMPLETED;
dev->cd_buflen = len;
ret = 0;
}
}
memcpy(output, dev->cd_buffer, len * 2);
memmove(dev->cd_buffer, &dev->cd_buffer[len], (CD_BUF_SIZE - len) * 2);
dev->cd_buflen -= len;
if (!dev->sound_on)
ret = 0;
cdrom_log(dev->log, "Audio callback returning %i\n", ret);
return ret;
}
uint8_t
cdrom_audio_play(cdrom_t *dev, const uint32_t pos, const uint32_t len, const int ismsf)
{
track_info_t ti;
uint32_t pos2 = pos;
uint32_t len2 = len;
int ret = 0;
if (dev->cd_status & CD_STATUS_HAS_AUDIO) {
cdrom_log(dev->log, "Play audio - %08X %08X %i\n", pos2, len, ismsf);
if (ismsf & 0x100) {
/* Track-relative audio play. */
pos2 = ismsf & 0xff;
if ((dev->is_bcd || dev->is_chinon) && (pos2 < 0xa0))
pos2 = bcd2bin(pos2);
ret = dev->ops->get_track_info(dev->local, ismsf & 0xff, 0, &ti);
if (ret)
pos2 += MSFtoLBA(ti.m, ti.s, ti.f) - 150;
else {
cdrom_log(dev->log, "Unable to get the starting position for "
"track %08X\n", ismsf & 0xff);
cdrom_stop(dev);
}
} else if ((ismsf == 2) || (ismsf == 3)) {
if ((dev->is_bcd || dev->is_chinon) && (pos2 < 0xa0))
pos2 = bcd2bin(pos2);
ret = dev->ops->get_track_info(dev->local, pos2, 0, &ti);
if (ret) {
pos2 = MSFtoLBA(ti.m, ti.s, ti.f) - 150;
if (ismsf == 2) {
/* We have to end at the *end* of the specified track,
not at the beginning. */
if ((dev->is_bcd || dev->is_chinon) && (len2 < 0xa0))
len2 = bcd2bin(len2);
ret = dev->ops->get_track_info(dev->local, len2, 1, &ti);
if (ret)
len2 = MSFtoLBA(ti.m, ti.s, ti.f) - 150;
else {
cdrom_log(dev->log, "Unable to get the ending position for "
"track %08X\n", pos2);
cdrom_stop(dev);
}
}
} else {
cdrom_log(dev->log, "Unable to get the starting position for "
"track %08X\n", pos2);
cdrom_stop(dev);
}
} else if (ismsf == 1) {
int m = (pos >> 16) & 0xff;
int s = (pos >> 8) & 0xff;
int f = pos & 0xff;
/* NEC CDR-260 speaks BCD. */
if (dev->is_bcd)
msf_from_bcd(&m, &s, &f);
if (pos == 0xffffff) {
cdrom_log(dev->log, "Playing from current position (MSF)\n");
pos2 = dev->seek_pos;
} else
pos2 = MSFtoLBA(m, s, f) - 150;
m = (len >> 16) & 0xff;
s = (len >> 8) & 0xff;
f = len & 0xff;
/* NEC CDR-260 speaks BCD. */
if (dev->is_bcd)
msf_from_bcd(&m, &s, &f);
len2 = MSFtoLBA(m, s, f) - 150;
ret = 1;
cdrom_log(dev->log, "MSF - pos = %08X len = %08X\n", pos2, len);
} else if (ismsf == 0) {
if (pos == 0xffffffff) {
cdrom_log(dev->log, "Playing from current position\n");
pos2 = dev->seek_pos;
}
len2 += pos2;
ret = 1;
}
}
if (ret) {
/*
Do this at this point, since it's at this point that we know the
actual LBA position to start playing from.
*/
ret = (dev->ops->get_track_type(dev->local, pos2) == CD_TRACK_AUDIO);
if (ret) {
dev->seek_diff = ABS(dev->seek_pos - pos2);
dev->seek_pos = pos2;
dev->cd_end = len2;
dev->cd_status = CD_STATUS_PLAYING;
dev->cd_buflen = 0;
} else {
cdrom_log(dev->log, "LBA %08X not on an audio track\n", pos);
cdrom_stop(dev);
}
}
return ret;
}
uint8_t
cdrom_audio_track_search(cdrom_t *dev, const uint32_t pos,
const int type, const uint8_t playbit)
{
uint32_t pos2 = pos;
uint8_t ret = 0;
if (dev->cd_status & CD_STATUS_HAS_AUDIO) {
cdrom_log(dev->log, "Audio Track Search: MSF = %06x, type = %02x, "
"playbit = %02x\n", pos, type, playbit);
ret = 1;
switch (type) {
case 0x00:
if (pos == 0xffffffff) {
cdrom_log(dev->log, "(Type 0) Search from current position\n");
pos2 = dev->seek_pos;
}
dev->seek_pos = pos2;
break;
case 0x40: {
const int m = bcd2bin((pos >> 24) & 0xff);
const int s = bcd2bin((pos >> 16) & 0xff);
const int f = bcd2bin((pos >> 8) & 0xff);
if (pos == 0xffffffff) {
cdrom_log(dev->log, "(Type 1) Search from current position\n");
pos2 = dev->seek_pos;
} else
pos2 = MSFtoLBA(m, s, f) - 150;
dev->seek_pos = pos2;
break;
} case 0x80: {
track_info_t ti;
pos2 = (pos2 >> 24) & 0xff;
if (pos2 < 0xa0)
pos2 = bcd2bin(pos2);
ret = dev->ops->get_track_info(dev->local, pos2, 1, &ti);
if (ret)
dev->seek_pos = MSFtoLBA(ti.m, ti.s, ti.f) - 150;
else {
cdrom_log(dev->log, "Unable to get the starting position for "
"track %08X\n", pos2 & 0xff);
cdrom_stop(dev);
}
break;
} default:
break;
}
if (ret) {
if (pos2 != 0x00000000)
pos2--;
cdrom_log(dev->log, "Track Search Toshiba: LBA=%08X.\n", pos);
dev->cd_end = dev->cdrom_capacity;
dev->cd_buflen = 0;
dev->cd_status = playbit ? CD_STATUS_PLAYING : CD_STATUS_HOLD;
ret = 1;
}
}
return ret;
}
uint8_t
cdrom_audio_track_search_pioneer(cdrom_t *dev, const uint32_t pos, const uint8_t playbit)
{
uint8_t ret = 0;
if (dev->cd_status &= CD_STATUS_HAS_AUDIO) {
const int f = bcd2bin((pos >> 24) & 0xff);
const int s = bcd2bin((pos >> 16) & 0xff);
const int m = bcd2bin((pos >> 8) & 0xff);
uint32_t pos2;
if (pos == 0xffffffff)
pos2 = dev->seek_pos;
else
pos2 = MSFtoLBA(m, s, f) - 150;
dev->seek_pos = pos2;
/*
Do this at this point, since it's at this point that we know the
actual LBA position to start playing from.
*/
if (dev->ops->get_track_type(dev->local, pos2) & CD_TRACK_AUDIO) {
dev->cd_end = dev->cdrom_capacity;
dev->cd_buflen = 0;
dev->cd_status = playbit ? CD_STATUS_PLAYING : CD_STATUS_HOLD;
ret = 1;
} else {
cdrom_log(dev->log, "LBA %08X not on an audio track\n", pos);
cdrom_stop(dev);
}
}
return ret;
}
uint8_t
cdrom_audio_play_pioneer(cdrom_t *dev, const uint32_t pos)
{
uint8_t ret = 0;
if (dev->cd_status & CD_STATUS_HAS_AUDIO) {
const int f = bcd2bin((pos >> 24) & 0xff);
const int s = bcd2bin((pos >> 16) & 0xff);
const int m = bcd2bin((pos >> 8) & 0xff);
uint32_t pos2 = MSFtoLBA(m, s, f) - 150;
dev->cd_end = pos2;
dev->cd_buflen = 0;
dev->cd_status = CD_STATUS_PLAYING;
ret = 1;
}
return ret;
}
uint8_t
cdrom_audio_play_toshiba(cdrom_t *dev, const uint32_t pos, const int type)
{
uint32_t pos2 = pos;
uint8_t ret = 0;
if (dev->cd_status & CD_STATUS_HAS_AUDIO) {
/* Preliminary support, revert if too incomplete. */
ret = 1;
switch (type) {
case 0x00:
dev->cd_end = pos2;
break;
case 0x40: {
const int m = bcd2bin((pos >> 24) & 0xff);
const int s = bcd2bin((pos >> 16) & 0xff);
const int f = bcd2bin((pos >> 8) & 0xff);
pos2 = MSFtoLBA(m, s, f) - 150;
dev->cd_end = pos2;
break;
} case 0x80: {
track_info_t ti;
pos2 = (pos2 >> 24) & 0xff;
if (pos2 < 0xa0)
pos2 = bcd2bin(pos2);
ret = dev->ops->get_track_info(dev->local, pos2, 1, &ti);
if (ret)
dev->cd_end = MSFtoLBA(ti.m, ti.s, ti.f) - 150;
else {
cdrom_log(dev->log, "Unable to get the starting position for "
"track %08X\n", pos2 & 0xff);
cdrom_stop(dev);
}
break;
} case 0xc0:
if (pos == 0xffffffff) {
cdrom_log(dev->log, "Playing from current position\n");
pos2 = dev->cd_end;
}
dev->cd_end = pos2;
break;
default:
break;
}
if (ret) {
cdrom_log(dev->log, "Toshiba Play Audio: LBA=%08X.\n", pos2);
dev->cd_status = CD_STATUS_PLAYING;
dev->cd_buflen = 0;
}
}
return ret;
}
uint8_t
cdrom_audio_scan(cdrom_t *dev, const uint32_t pos)
{
uint32_t pos2 = pos;
uint8_t ret = 0;
if (dev->cd_status & CD_STATUS_HAS_AUDIO) {
cdrom_log(dev->log, "Audio Scan: MSF = %06x\n", pos);
if (pos == 0xffffffff) {
cdrom_log(dev->log, "(Type 0) Search from current position\n");
pos2 = dev->seek_pos;
}
dev->seek_pos = pos2;
/* Do this at this point, since it's at this point that we know the
actual LBA position to start playing from. */
if (dev->ops->get_track_type(dev->local, pos) & CD_TRACK_AUDIO) {
dev->cd_buflen = 0;
ret = 1;
} else {
cdrom_log(dev->log, "LBA %08X not on an audio track\n", pos);
cdrom_stop(dev);
}
}
return ret;
}
void
cdrom_audio_pause_resume(cdrom_t *dev, const uint8_t resume)
{
if ((dev->cd_status == CD_STATUS_PLAYING) || (dev->cd_status == CD_STATUS_PAUSED))
dev->cd_status = (dev->cd_status & 0xfe) | (resume & 0x01);
}
uint8_t
cdrom_get_current_status(const cdrom_t *dev)
{
const uint8_t ret = status_codes[dev->is_chinon]
[dev->cd_status & CD_STATUS_MASK];
return ret;
}
void
cdrom_get_current_subchannel(cdrom_t *dev, uint8_t *b, const int msf)
{
subchannel_t subc;
int base = 0;
int diff = 4;
if (dev->cached_sector == -1)
cdrom_get_subchannel(dev, dev->seek_pos, &subc, 1);
else
cdrom_get_subchannel(dev, dev->cached_sector, &subc, 1);
cdrom_log(dev->log, "Returned subchannel absolute at %02i:%02i.%02i, "
"relative at %02i:%02i.%02i, seek pos = %08x, cd_end = %08x.\n",
subc.abs_m, subc.abs_s, subc.abs_f, subc.rel_m, subc.rel_s, subc.rel_f,
dev->seek_pos, dev->cd_end);
/* Format code. */
switch (b[0]) {
/*
Mode 0 = Q subchannel mode, first 16 bytes are indentical to mode 1 (current
position), the rest are stuff like ISRC etc., which can be all zeroes.
*/
case 0x00:
if (dev->bus_type == CDROM_BUS_ATAPI)
break;
diff = 0;
fallthrough;
case 0x01:
/* Current position. */
b[1] = subc.attr;
if ((dev->is_bcd || dev->is_chinon) &&
(subc.track >= 1) && (subc.track <= 99))
b[2] = bin2bcd(subc.track);
else
b[2] = subc.track;
b[3] = subc.index;
if (msf) {
b[4] = b[8] = 0x00;
/* NEC CDR-260 speaks BCD. */
if (dev->is_bcd) {
b[5] = bin2bcd(subc.abs_m);
b[6] = bin2bcd(subc.abs_s);
b[7] = bin2bcd(subc.abs_f);
b[9] = bin2bcd(subc.rel_m);
b[10] = bin2bcd(subc.rel_s);
b[11] = bin2bcd(subc.rel_f);
} else {
b[5] = subc.abs_m;
b[6] = subc.abs_s;
b[7] = subc.abs_f;
b[9] = subc.rel_m;
b[10] = subc.rel_s;
b[11] = subc.rel_f;
}
} else {
uint32_t dat = MSFtoLBA(subc.abs_m, subc.abs_s, subc.abs_f) - 150;
b[4] = (dat >> 24) & 0xff;
b[5] = (dat >> 16) & 0xff;
b[6] = (dat >> 8) & 0xff;
b[7] = dat & 0xff;
dat = MSFtoLBA(subc.rel_m, subc.rel_s, subc.rel_f);
b[8] = (dat >> 24) & 0xff;
b[9] = (dat >> 16) & 0xff;
b[10] = (dat >> 8) & 0xff;
b[11] = dat & 0xff;
}
if (b[0] != 0x00)
break;
base += 12;
fallthrough;
case 0x02:
/* UPC - TODO: Finding and reporting the actual UPC data. */
memset(&(b[base]), 0x00, 20 - diff);
base += diff;
memset(&(b[base + 1]), 0x30, 13);
/* NEC CDR-260 speaks BCD. */
if (dev->is_bcd)
b[base + 15] = bin2bcd(subc.abs_f);
else
b[base + 15] = subc.abs_f;
if (b[0] != 0x00)
break;
base += 16;
fallthrough;
case 0x03:
/* ISRC - TODO: Finding and reporting the actual ISRC data. */
memset(&(b[base]), 0x00, 20 - diff);
base += diff;
memset(&(b[base]), 0x30, 12);
/* NEC CDR-260 speaks BCD. */
if (dev->is_bcd)
b[base + 14] = bin2bcd(subc.abs_f);
else
b[base + 14] = subc.abs_f;
break;
default:
cdrom_log(dev->log, "b[0] = %02X\n", b[0]);
break;
}
}
void
cdrom_get_current_subchannel_sony(cdrom_t *dev, uint8_t *b, const int msf)
{
subchannel_t subc;
cdrom_get_subchannel(dev, dev->seek_pos, &subc, 1);
cdrom_log(dev->log, "Returned subchannel at %02i:%02i.%02i, seek pos = %08x, "
"cd_end = %08x, msf = %x.\n",
subc.abs_m, subc.abs_s, subc.abs_f, dev->seek_pos, dev->cd_end, msf);
b[0] = subc.attr;
b[1] = subc.track;
b[2] = subc.index;
if (msf) {
b[3] = subc.rel_m;
b[4] = subc.rel_s;
b[5] = subc.rel_f;
b[6] = subc.abs_m;
b[7] = subc.abs_s;
b[8] = subc.abs_f;
} else {
uint32_t dat = MSFtoLBA(subc.rel_m, subc.rel_s, subc.rel_f);
b[3] = (dat >> 16) & 0xff;
b[4] = (dat >> 8) & 0xff;
b[5] = dat & 0xff;
dat = MSFtoLBA(subc.abs_m, subc.abs_s, subc.abs_f) - 150;
b[6] = (dat >> 16) & 0xff;
b[7] = (dat >> 8) & 0xff;
b[8] = dat & 0xff;
}
}
uint8_t
cdrom_get_audio_status_pioneer(cdrom_t *dev, uint8_t *b)
{
uint8_t ret;
subchannel_t subc;
cdrom_get_subchannel(dev, dev->seek_pos, &subc, 0);
if (dev->cd_status & CD_STATUS_HAS_AUDIO) {
if (dev->cd_status == CD_STATUS_PLAYING)
ret = dev->sound_on ? 0x00 : 0x02;
else if (dev->cd_status == CD_STATUS_PAUSED)
ret = 0x01;
else
ret = 0x03;
} else
ret = 0x05;
b[0] = 0;
b[1] = subc.abs_m;
b[2] = subc.abs_s;
b[3] = subc.abs_f;
return ret;
}
uint8_t
cdrom_get_audio_status_sony(cdrom_t *dev, uint8_t *b, const int msf)
{
uint8_t ret;
subchannel_t subc;
cdrom_get_subchannel(dev, dev->seek_pos, &subc, 1);
if (dev->cd_status & CD_STATUS_HAS_AUDIO) {
if (dev->cd_status == CD_STATUS_PLAYING)
ret = dev->sound_on ? 0x00 : 0x02;
else if (dev->cd_status == CD_STATUS_PAUSED)
ret = 0x01;
else
ret = 0x03;
} else
ret = 0x05;
if (msf) {
b[0] = 0;
b[1] = subc.abs_m;
b[2] = subc.abs_s;
b[3] = subc.abs_f;
} else {
const uint32_t dat = MSFtoLBA(subc.abs_m, subc.abs_s, subc.abs_f) - 150;
b[0] = (dat >> 24) & 0xff;
b[1] = (dat >> 16) & 0xff;
b[2] = (dat >> 8) & 0xff;
b[3] = dat & 0xff;
}
return ret;
}
void
cdrom_get_current_subcodeq(cdrom_t *dev, uint8_t *b)
{
subchannel_t subc;
cdrom_get_subchannel(dev, dev->seek_pos, &subc, 0);
b[0] = (subc.attr >> 4) | ((subc.attr & 0xf) << 4);
b[1] = subc.track;
b[2] = subc.index;
b[3] = subc.rel_m;
b[4] = subc.rel_s;
b[5] = subc.rel_f;
b[6] = subc.abs_m;
b[7] = subc.abs_s;
b[8] = subc.abs_f;
cdrom_log(dev->log, "SubCodeQ: %02X %02X %02X %02X %02X %02X %02X %02X "
"%02X\n",
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8]);
}
uint8_t
cdrom_get_current_subcodeq_playstatus(cdrom_t *dev, uint8_t *b)
{
uint8_t ret;
cdrom_get_current_subcodeq(dev, b);
switch (dev->cd_status) {
default: case CD_STATUS_EMPTY:
case CD_STATUS_DATA_ONLY: case CD_STATUS_DVD:
case CD_STATUS_STOPPED: case CD_STATUS_PLAYING_COMPLETED:
case CD_STATUS_DVD_REJECTED:
ret = 0x03;
break;
case CD_STATUS_HOLD:
ret = 0x02;
break;
case CD_STATUS_PAUSED:
ret = 0x01;
break;
case CD_STATUS_PLAYING:
ret = 0x00;
break;
}
cdrom_log(dev->log, "SubCodeQ: Play Status: Seek LBA=%08x, CDEND=%08x.\n",
dev->seek_pos, dev->cd_end);
return ret;
}
int
cdrom_read_pma(const cdrom_t *dev, uint8_t *b, const int max_len)
{
if (max_len < 4)
return -1;
// This is exactly what a blank CD-R returns, no PMA, seems only CD-RWs have PMA?
b[0] = 0x00; /* PMA Data Length (MSB) */
b[1] = 0x02; /* PMA Data Length (LSB) */
b[2] = 0x00; /* Reserved */
b[3] = 0x00; /* Reserved */
return 4;
}
int
cdrom_read_atip(const cdrom_t *dev, uint8_t *b, const int max_len)
{
if (max_len < 28)
return -1;
b[ 0] = 0x00; /* ATIP Data Length (MSB) */
b[ 1] = 0x1a; /* ATIP Data Length (LSB) */
b[ 2] = 0x00; /* Reserved */
b[ 3] = 0x00; /* Reserved */
// Special Information 1
b[ 4] = 0xd0; /* Indicative Target Writing Power, Reference Speed */
b[ 5] = 0x00; /* URU */
b[ 6] = 0x98; /* 1, Disc Type, Disc Sub-Type, A1 Valid, A2 Valid, A3 Valid*/
b[ 7] = 0x00; /* Reserved*/
// Special Information 2
b[ 8] = 0x61; /* ATIP Start Time of Lead-in (Min) */
b[ 9] = 0x1a; /* ATIP Start Time of Lead-in (Sec) */
b[10] = 0x42; /* ATIP Start Time of Lead-in (Frame) */
b[11] = 0x00; /* Reserved */
// Special Information 3
b[12] = 0x4f; /* ATIP Last Possible Start of Lead-out (Min) */
b[13] = 0x3b; /* ATIP Last Possible Start of Lead-out (Sec) */
b[14] = 0x47; /* ATIP Last Possible Start of Lead-out (Frame) */
b[15] = 0x00; /* Reserved */
// Additional Information 1
b[16] = 0x00; /* A1 Values */
b[17] = 0x00; /* A1 Values */
b[18] = 0x80; /* A1 Values */
b[19] = 0x00; /* Reserved */
// Additional Information 2
b[20] = 0x00; /* A2 Values */
b[21] = 0x80; /* A2 Values */
b[22] = 0x00; /* A2 Values */
b[23] = 0x00; /* Reserved */
// Additional Information 3
b[24] = 0x00; /* A3 Values */
b[25] = 0x80; /* A3 Values */
b[26] = 0x80; /* A3 Values */
b[27] = 0x00; /* Reserved */
return 28;
}
int
cdrom_read_toc(const cdrom_t *dev, uint8_t *b, const int type,
const uint8_t start_track, const int msf, const int max_len)
{
int len;
switch (type) {
case CD_TOC_NORMAL:
len = read_toc_normal(dev, b, start_track, msf, 0);
break;
case CD_TOC_SESSION:
len = read_toc_session(dev, b, msf);
break;
case CD_TOC_RAW:
len = read_toc_raw(dev, b, start_track);
break;
default:
cdrom_log(dev->log, "Unknown TOC read type: %i\n", type);
return 0;
}
len = MIN(len, max_len);
b[0] = (uint8_t) (((len - 2) >> 8) & 0xff);
b[1] = (uint8_t) ((len - 2) & 0xff);
return len;
}
int
cdrom_read_toc_sony(const cdrom_t *dev, uint8_t *b, const uint8_t start_track,
const int msf, const int max_len)
{
int len = read_toc_normal(dev, b, start_track, msf, 1);
len = MIN(len, max_len);
b[0] = (uint8_t) (((len - 2) >> 8) & 0xff);
b[1] = (uint8_t) ((len - 2) & 0xff);
return len;
}
#ifdef USE_CDROM_MITSUMI
/* New API calls for Mitsumi CD-ROM. */
void
cdrom_get_track_buffer(cdrom_t *dev, uint8_t *buf)
{
uint8_t rti[65536] = { 0 };
raw_track_info_t *trti = (raw_track_info_t *) rti;
int num = 0;
int first = -1;
int last = -1;
if (dev != NULL)
dev->ops->get_raw_track_info(dev->local, &num, rti);
if (num > 0) {
first = find_track(trti, num, 1);
last = find_track(trti, num, 0);
}
if (first != -1) {
buf[0] = trti[first].point;
buf[2] = trti[first].pm;
buf[3] = trti[first].ps;
buf[4] = trti[first].pf;
} else {
buf[0] = 0x01;
buf[2] = 0x00;
buf[3] = 0x02;
buf[4] = 0x00;
}
if (last != -1) {
buf[1] = trti[last].point;
buf[5] = trti[first].pm;
buf[6] = trti[first].ps;
buf[7] = trti[first].pf;
} else {
buf[1] = 0x01;
buf[5] = 0x00;
buf[6] = 0x02;
buf[7] = 0x00;
}
buf[8] = 0x00;
}
/* TODO: Actually implement this properly. */
void
cdrom_get_q(UNUSED(cdrom_t *dev), uint8_t *buf, UNUSED(int *curtoctrk), UNUSED(uint8_t mode))
{
memset(buf, 0x00, 10);
}
uint8_t
cdrom_mitsumi_audio_play(cdrom_t *dev, uint32_t pos, uint32_t len)
{
track_info_t ti;
int ret = 0;
if (dev->cd_status & CD_STATUS_HAS_AUDIO) {
cdrom_log(dev->log, "Play Mitsumi audio - %08X %08X\n", pos, len);
ret = dev->ops->get_track_info(dev->local, pos, 0, &ti);
if (ret) {
pos = MSFtoLBA(ti.m, ti.s, ti.f) - 150;
ret = dev->ops->get_track_info(dev->local, len, 1, &ti);
if (ret) {
len = MSFtoLBA(ti.m, ti.s, ti.f) - 150;
/*
Do this at this point, since it's at this point that we know the
actual LBA position to start playing from.
*/
ret = (dev->ops->get_track_type(dev->local, pos) == CD_TRACK_AUDIO);
if (ret) {
dev->seek_pos = pos;
dev->cd_end = len;
dev->cd_status = CD_STATUS_PLAYING;
dev->cd_buflen = 0;
} else {
cdrom_log(dev->log, "LBA %08X not on an audio track\n", pos);
cdrom_stop(dev);
}
} else {
cdrom_log(dev->log, "Unable to get the ending position for track %08X\n",
len);
cdrom_stop(dev);
}
} else {
cdrom_log(dev->log, "Unable to get the starting position for track %08X\n", pos);
cdrom_stop(dev);
}
}
return ret;
}
#endif
uint8_t
cdrom_read_disc_info_toc(cdrom_t *dev, uint8_t *b,
const uint8_t track, const int type)
{
uint8_t rti[65536] = { 0 };
const raw_track_info_t *trti = (raw_track_info_t *) rti;
int num = 0;
int first = -1;
int t = -1;
uint8_t ret = 1;
uint32_t temp;
cdrom_log(dev->log, "Read DISC Info TOC Type = %d, track = %d\n", type, track);
dev->inv_field = track;
dev->ops->get_raw_track_info(dev->local, &num, rti);
switch (type) {
case 0:
if (num > 0) {
if (num < 4)
ret = 0;
else {
b[0] = bin2bcd(trti[0].pm);
b[1] = bin2bcd(trti[1].pm);
b[2] = 0x00;
b[3] = 0x00;
cdrom_log(dev->log, "Returned Toshiba/NEC disc information (type 0) "
"at %02i:%02i\n", b[0], b[1]);
}
} else
ret = 0;
break;
case 1:
if (num > 0)
t = find_last_lead_out(trti, num);
if (t == -1)
ret = 0;
else {
b[0] = bin2bcd(trti[t].pm);
b[1] = bin2bcd(trti[t].ps);
b[2] = bin2bcd(trti[t].pf);
b[3] = 0x00;
cdrom_log(dev->log, "Returned Toshiba/NEC disc information (type 1) at "
"%02i:%02i.%02i\n", b[0], b[1], b[2]);
}
break;
case 2:
if (num > 0)
t = find_specific_track(trti, num, bcd2bin(track));
if (t == -1)
ret = 0;
else {
b[0] = bin2bcd(trti[t].pm);
b[1] = bin2bcd(trti[t].ps);
b[2] = bin2bcd(trti[t].pf);
b[3] = trti[t].adr_ctl;
cdrom_log(dev->log, "Returned Toshiba/NEC disc information (type 2) at "
"%02i:%02i.%02i, track=%d, attr=%02x.\n", b[0], b[1],
b[2], bcd2bin(track), b[3]);
}
break;
case 3: /* Undocumented on NEC CD-ROM's, from information based on sr_vendor.c from the Linux kernel */
if (dev->is_nec) {
b[0x0e] = 0x00;
if (num > 0)
first = find_track(trti, num, 1);
if (first == -1)
ret = 0;
else {
temp = MSFtoLBA(trti[first].pm, trti[first].ps, trti[first].pf) - 150;
b[0x0f] = temp >> 24;
b[0x10] = temp >> 16;
b[0x11] = temp >> 8;
b[0x12] = temp;
}
} else {
b[0] = trti[0].ps; /* Disc type. */
if (num > 0)
first = find_track(trti, num, 1);
if (first == -1)
ret = 0;
else {
temp = MSFtoLBA(trti[first].pm, trti[first].ps, trti[first].pf) - 150;
b[0x1] = temp >> 24;
b[0x2] = temp >> 16;
b[0x3] = temp >> 8;
}
}
break;
default:
break;
}
return ret;
}
static uint32_t
cdrom_msf_to_lba(const int sector, const int ismsf,
int cdrom_sector_type, const uint8_t vendor_type)
{
int pos = sector;
uint32_t lba;
if ((cdrom_sector_type & 0x0f) >= 0x08) {
mult = cdrom_sector_type >> 4;
pos /= mult;
}
if (ismsf) {
const int m = (pos >> 16) & 0xff;
const int s = (pos >> 8) & 0xff;
const int f = pos & 0xff;
lba = MSFtoLBA(m, s, f) - 150;
} else {
switch (vendor_type) {
case 0x00:
lba = pos;
break;
case 0x40: {
const int m = bcd2bin((pos >> 24) & 0xff);
const int s = bcd2bin((pos >> 16) & 0xff);
const int f = bcd2bin((pos >> 8) & 0xff);
lba = MSFtoLBA(m, s, f) - 150;
break;
} case 0x80:
lba = bcd2bin((pos >> 24) & 0xff);
break;
/* Never used values but the compiler complains. */
default:
lba = 0;
}
}
return lba;
}
int
cdrom_is_track_audio(cdrom_t *dev, const int sector,
const int ismsf, int cdrom_sector_type,
const uint8_t vendor_type)
{
int audio = 0;
uint32_t lba = cdrom_msf_to_lba(sector, ismsf,
cdrom_sector_type, vendor_type);
if (dev->ops->get_track_type)
audio = dev->ops->get_track_type(dev->local, lba);
audio &= CD_TRACK_AUDIO;
return audio;
}
int
cdrom_readsector_raw(cdrom_t *dev, uint8_t *buffer, const int sector, const int ismsf,
int cdrom_sector_type, const int cdrom_sector_flags,
int *len, const uint8_t vendor_type)
{
int pos = sector;
int ret = 0;
const int old_type = cdrom_sector_type;
if ((cdrom_sector_type & 0x0f) >= 0x08) {
mult = cdrom_sector_type >> 4;
cdrom_sector_type &= 0x0f;
part = pos % mult;
pos /= mult;
ecc_diff = (cdrom_sector_type & 0x01) ? 4 : 0;
} else {
mult = 1;
part = 0;
ecc_diff = 0;
}
if ((dev->cd_status != CD_STATUS_EMPTY) && (dev->cd_status != CD_STATUS_DVD_REJECTED)) {
uint8_t *temp_b;
uint8_t *b = temp_b = buffer;
int audio = 0;
uint32_t lba = cdrom_msf_to_lba(sector, ismsf,
old_type, vendor_type);
int mode2 = 0;
*len = 0;
if (dev->ops->get_track_type)
audio = dev->ops->get_track_type(dev->local, lba);
const int dm = audio & CD_TRACK_MODE_MASK;
audio &= CD_TRACK_AUDIO;
if (dm != CD_TRACK_NORMAL)
mode2 = 1;
dev->mode2 = mode2;
memset(dev->extra_buffer, 0, 296);
if ((cdrom_sector_flags & 0xf8) == 0x08) {
/* 0x08 is an illegal mode */
cdrom_log(dev->log, "[Mode 1] 0x08 is an illegal mode\n");
} else if ((cdrom_sector_type > 5) && (cdrom_sector_type < 8)) {
cdrom_log(dev->log, "Attempting to read an unrecognized sector "
"type from an image\n");
return 0;
} else {
if ((cdrom_sector_type > 1) && audio &&
(dev->cd_status & CD_STATUS_HAS_AUDIO)) {
cdrom_log(dev->log, "[%s] Attempting to read a data sector "
"from an audio track\n",
cdrom_req_modes[cdrom_sector_type]);
} else if ((cdrom_sector_type == 1) &&
(!audio || !(dev->cd_status & CD_STATUS_HAS_AUDIO))) {
cdrom_log(dev->log, "[Audio] Attempting to read an audio "
"sector from a data track\n");
} else if (audio) {
if (!track_type_is_valid(dev, cdrom_sector_type,
cdrom_sector_flags, 1, 0x00))
ret = 0;
else
ret = read_audio(dev, lba, temp_b);
} else {
ret = read_data(dev, lba, 1);
/* Return with error if we had one. */
if (ret > 0) {
int form = 0;
if ((dev->raw_buffer[dev->cur_buf][0x000f] == 0x00) ||
(dev->raw_buffer[dev->cur_buf][0x000f] > 0x02)) {
cdrom_log(dev->log, "[%s] Unknown mode: %02X\n",
cdrom_req_modes[cdrom_sector_type],
dev->raw_buffer[dev->cur_buf][0x000f]);
ret = 0;
} else if (mode2) {
if (dev->raw_buffer[dev->cur_buf][0x000f] == 0x01)
/*
Use Mode 1, since evidently specification-violating
discs exist.
*/
mode2 = 0;
else if (dev->raw_buffer[dev->cur_buf][0x0012] !=
dev->raw_buffer[dev->cur_buf][0x0016]) {
cdrom_log(dev->log, "[%s] XA Mode 2 sector with "
"malformed sub-header\n",
cdrom_req_modes[cdrom_sector_type]);
ret = 0;
} else
form = ((dev->raw_buffer[dev->cur_buf][0x0012] &
0x20) >> 5) + 1;
} else if (dev->raw_buffer[dev->cur_buf][0x000f] == 0x02)
mode2 = 1;
if (ret > 0) {
const int mode_id = mode2 + form;
cdrom_log(dev->log, "[%s] %s detected\n",
cdrom_req_modes[cdrom_sector_type],
cdrom_modes[mode_id]);
if (!track_type_is_valid(dev, cdrom_sector_type,
cdrom_sector_flags, 0,
(mode2 << 2) + form)) {
cdrom_log(dev->log, "[%s] Invalid track type\n",
cdrom_req_modes[cdrom_sector_type]);
ret = 0;
} else if (cdrom_mode_masks[cdrom_sector_type] &
(1 << mode_id))
cdrom_process_data[mode_id](dev, cdrom_sector_flags,
temp_b);
else {
cdrom_log(dev->log, "[%s] Attempting to read a "
"%s sector\n",
cdrom_req_modes[cdrom_sector_type],
cdrom_modes[mode_id]);
ret = 0;
}
}
}
}
if (ret > 0) {
process_c2_and_subch(dev, cdrom_sector_flags, b);
*len = dev->cdrom_sector_size;
}
}
}
return ret;
}
/*
Read DVD Structure
Yes, +2 instead of +4 is correct, I have verified this via Windows IOCTL, and it also matches
the MMC specification.
*/
int
cdrom_read_dvd_structure(const cdrom_t *dev, const uint8_t layer, const uint8_t format,
uint8_t *buffer, uint32_t *info)
{
int max_layer = 0;
int ret = 0;
uint64_t total_sectors;
if (format < 0xc0) {
if (dev->cd_status != CD_STATUS_DVD) {
*info = format;
ret = -(SENSE_ILLEGAL_REQUEST << 16) | (ASC_INCOMPATIBLE_FORMAT << 8);
} else if ((dev->ops != NULL) && (dev->ops->read_dvd_structure != NULL))
ret = dev->ops->read_dvd_structure(dev->local, layer, format, buffer, info);
}
if (ret == 0) switch (format) {
case 0x00: /* Physical format information */
total_sectors = (uint64_t) dev->cdrom_capacity;
if (total_sectors > DVD_LAYER_0_SECTORS)
max_layer++;
if (layer > max_layer) {
*info = layer;
ret = -(SENSE_ILLEGAL_REQUEST << 16) | (ASC_INV_FIELD_IN_CMD_PACKET << 8);
} else {
if (total_sectors == 0) {
*info = 0x00000000;
ret = -(SENSE_NOT_READY << 16) | (ASC_MEDIUM_NOT_PRESENT << 8);
} else {
buffer[4] = 0x01; /* DVD-ROM, part version 1. */
buffer[5] = 0x0f; /* 120mm disc, minimum rate unspecified .*/
if (max_layer == 1)
/* Two layers, OTP track path, read-only (per MMC-2 spec). */
buffer[6] = 0x31;
else
/* One layer, read-only (per MMC-2 spec). */
buffer[6] = 0x01;
buffer[7] = 0x10; /* Default densities. */
/* Start sector. */
buffer[8] = 0x00;
buffer[9] = (0x030000 >> 16) & 0xff;
buffer[10] = (0x030000 >> 8) & 0xff;
buffer[11] = 0x030000 & 0xff;
/* End sector. */
buffer[12] = 0x00;
if (layer == 1) {
buffer[13] = ((total_sectors - DVD_LAYER_0_SECTORS) >> 16) & 0xff;
buffer[14] = ((total_sectors - DVD_LAYER_0_SECTORS) >> 8) & 0xff;
buffer[15] = (total_sectors - DVD_LAYER_0_SECTORS) & 0xff;
} else if (max_layer == 1) {
buffer[13] = (DVD_LAYER_0_SECTORS >> 16) & 0xff;
buffer[14] = (DVD_LAYER_0_SECTORS >> 8) & 0xff;
buffer[15] = DVD_LAYER_0_SECTORS & 0xff;
} else {
buffer[13] = (total_sectors >> 16) & 0xff;
buffer[14] = (total_sectors >> 8) & 0xff;
buffer[15] = total_sectors & 0xff;
}
/* Layer 0 end sector. */
buffer[16] = 0x00;
buffer[17] = (total_sectors >> 16) & 0xff;
buffer[18] = (total_sectors >> 8) & 0xff;
buffer[19] = total_sectors & 0xff;
buffer[20] = 0x00; /* No BCA */
/* 2048 bytes of data + 2 byte header */
ret = (2048 + 2);
}
}
break;
case 0x01: /* DVD copyright information */
buffer[4] = 0; /* No copyright data. */
buffer[5] = 0; /* No region restrictions. */
/* 4 bytes of data + 2 byte header. */
ret = (4 + 2);
break;
case 0x04: /* DVD disc manufacturing information. */
/* 2048 bytes of data + 2 byte header */
ret = (2048 + 2);
break;
case 0xff:
/*
* This lists all the command capabilities above. Add new ones
* in order and update the length and buffer return values.
*/
buffer[4] = 0x00; /* Physical format */
buffer[5] = 0x40; /* Not writable, is readable */
buffer[6] = ((2048 + 4) >> 8) & 0xff;
buffer[7] = (2048 + 4) & 0xff;
buffer[8] = 0x01; /* Copyright info */
buffer[9] = 0x40; /* Not writable, is readable */
buffer[10] = ((4 + 2) >> 8) & 0xff;
buffer[11] = (4 + 2) & 0xff;
buffer[12] = 0x03; /* BCA info */
buffer[13] = 0x40; /* Not writable, is readable */
buffer[14] = ((188 + 2) >> 8) & 0xff;
buffer[15] = (188 + 2) & 0xff;
buffer[16] = 0x04; /* Manufacturing info */
buffer[17] = 0x40; /* Not writable, is readable */
buffer[18] = ((2048 + 2) >> 8) & 0xff;
buffer[19] = (2048 + 2) & 0xff;
/* data written + 4 byte header */
ret = (16 + 2);
break;
default:
*info = format;
ret = -(SENSE_ILLEGAL_REQUEST << 16) | (ASC_INV_FIELD_IN_CMD_PACKET << 8);
break;
}
return ret;
}
void
cdrom_read_disc_information(const cdrom_t *dev, uint8_t *buffer)
{
uint8_t rti[65536] = { 0 };
raw_track_info_t *t = (raw_track_info_t *) rti;
int num = 0;
int first = 0;
int sessions = 0;
int ls_first = 0;
int ls_last = 0;
int t_b0 = -1;
if (dev->blank_media == CD_BLANK_CDR) {
memset(buffer, 0x00, 34);
buffer[ 0] = 0x00; /* Disc Information Length (MSB) */
buffer[ 1] = 0x20; /* Disc Information Lenght (LSB) */
buffer[ 2] = 0x00; /* Reserved, Erasable, State of Last Session, Disc Status */
buffer[ 3] = 0x01; /* Number of First Track on Disc */
buffer[ 4] = 0x01; /* Number of Sessions (LSB) */
buffer[ 5] = 0x01; /* First track number in last session (LSB) */
buffer[ 6] = 0x01; /* Last track number in last session (LSB) */
buffer[ 7] = 0x00; /* DID_V, DBC_V, URU, DAC_V, Reserved, Dbit, BG format Status */
buffer[ 8] = 0xff; /* Disc type */
buffer[ 9] = 0x00; /* Number Of Sessions (MSB) */
buffer[10] = 0x00; /* First Track Number in Last Session (MSB) */
buffer[11] = 0x00; /* Last Track Number in Last Session (MSB) */
buffer[12] = 0x00; /* Disc identification (MSB...) */
buffer[13] = 0x00; /* ... */
buffer[14] = 0x00; /* ... */
buffer[15] = 0x00; /* Disc identification (...LSB) */
buffer[16] = 0x00; /* Last session Lead-In Start Address (MSB...) */
buffer[17] = 0x61; /* ... */
buffer[18] = 0x1a; /* ... */
buffer[19] = 0x42; /* Last session Lead-In Start Address (...LSB) */
buffer[20] = 0x00; /* Last Possible Start Time for Start of Lead-out (MSB) */
buffer[21] = 0x4f; /* ... */
buffer[22] = 0x3b; /* ... */
buffer[23] = 0x47; /* Last Possible Start Time for Start of Lead-out (...LSB) */
buffer[24] = 0x00; /* Disc barcode (MSB...) */
buffer[25] = 0x00; /* ... */
buffer[26] = 0x00; /* ... */
buffer[27] = 0x00; /* ... */
buffer[28] = 0x00; /* ... */
buffer[29] = 0x00; /* ... */
buffer[30] = 0x00; /* ... */
buffer[31] = 0x00; /* Disc barcode (...LSB) */
return;
}
dev->ops->get_raw_track_info(dev->local, &num, rti);
for (int i = 0; i < num; i++)
if (t[i].session > sessions)
sessions = t[i].session;
else if ((first == 0) && (t[i].point >= 1) && (t[i].point <= 99))
first = t[i].point;
for (int i = 0; i < num; i++)
if ((t[i].session == sessions) && (t[i].point >= 1) && (t[i].point <= 99)) {
ls_first = t[i].point;
break;
}
for (int i = (num - 1); i >= 0; i--)
if ((t[i].session == sessions) && (t[i].point >= 1) && (t[i].point <= 99)) {
ls_last = t[i].point;
break;
}
for (int i = (num - 1); i >= 0; i--)
if (t[i].point == 0xb0) {
t_b0 = i;
break;
}
memset(buffer, 0x00, 34);
buffer[ 0] = 0x00; /* Disc Information Length (MSB) */
buffer[ 1] = 0x20; /* Disc Information Lenght (LSB) */
buffer[ 2] = 0x0e; /* Last session complete, disc finalized */
buffer[ 3] = first; /* Number of First Track on Disc */
buffer[ 4] = sessions; /* Number of Sessions (LSB) */
buffer[ 5] = ls_first; /* First Track Number in Last Session (LSB) */
buffer[ 6] = ls_last; /* Last Track Number in Last Session (LSB) */
buffer[ 7] = 0x20; /* Unrestricted use */
buffer[ 8] = t[0].ps; /* Disc Type */
buffer[ 9] = 0x00; /* Number Of Sessions (MSB) */
buffer[10] = 0x00; /* First Track Number in Last Session (MSB) */
buffer[11] = 0x00; /* Last Track Number in Last Session (MSB) */
if (t_b0 == -1) {
/* Single-session disc. */
/* Last Session Lead-in Start Time MSF is 00:00:00 */
/* Last Possible Start Time for Start of Lead-out */
buffer[20] = t[2].pm;
buffer[21] = t[2].ps;
buffer[22] = t[2].pf;
} else {
/* Multi-session disc. */
/* Last Session Lead-in Start Time MSF */
buffer[17] = t[t_b0].m;
buffer[18] = t[t_b0].s;
buffer[19] = t[t_b0].f;
/* Last Possible Start Time for Start of Lead-out */
buffer[20] = t[t_b0].pm;
buffer[21] = t[t_b0].ps;
buffer[22] = t[t_b0].pf;
}
}
int
cdrom_read_track_information(cdrom_t *dev, const uint8_t *cdb, uint8_t *buffer)
{
uint8_t rti[65536] = { 0 };
const raw_track_info_t *t = (raw_track_info_t *) rti;
const raw_track_info_t *track = NULL;
const raw_track_info_t lead_in = { 0 };
const uint32_t pos = (cdb[2] << 24) | (cdb[3] << 16) |
(cdb[4] << 8) | cdb[5];
uint32_t real_pos = pos;
int num = 0;
int ret;
dev->ops->get_raw_track_info(dev->local, &num, rti);
switch (cdb[1] & 0x03) {
default:
ret = -cdb[1];
break;
case 0x00:
if (num < 4)
ret = -pos;
else {
for (int i = 0; i < num; i++) {
const raw_track_info_t *ct = &(t[i]);
const uint32_t start = ((ct->pm * 60 * 75) + (ct->ps * 75) +
ct->pf) - 150;
if (pos > start) {
track = ct;
break;
}
}
if (track == NULL)
ret = -cdb[1];
else
ret = 36;
}
break;
case 0x01:
if (dev->blank_media) {
if (pos != 0xff) {
ret = -cdb[1];
break;
}
// Otherwise it is asking the invisible/incomplete track information
memset(buffer, 0, 36);
buffer[ 0] = 0x00; /* Data Length (MSB) */
buffer[ 1] = 0x22; /* Data Length (LSB) */
buffer[ 2] = 0x01; /* Logical Track Number (LSB) */
buffer[ 3] = 0x01; /* Session Number (LSB) */
buffer[ 4] = 0x00; /* Reserved */
buffer[ 5] = 0x04; /* LJRS, Damage, Copy, Track Mode */
buffer[ 6] = 0x4f; /* RT, Blank, Packet, FP, Data Mode */
buffer[ 7] = 0x01; /* LRA_V, NWA_V */
buffer[ 8] = 0x00; /* Logical Track Start Addres (MSB...) */
buffer[ 9] = 0x00; /* ... */
buffer[10] = 0x00; /* ... */
buffer[11] = 0x00; /* Logical Track Start Address (...LSB) */
buffer[12] = 0x00; /* Next Writable Address (MSB...) */
buffer[13] = 0x00; /* ... */
buffer[14] = 0x00; /* ... */
buffer[15] = 0x00; /* Next Writable Address (...LSB) */
buffer[16] = 0x00; /* Free Blocks (MSB...) */
buffer[17] = 0x05; /* ... */
buffer[18] = 0x7d; /* ... */
buffer[19] = 0xa4; /* Free Blocks (...LSB) */
buffer[20] = 0x00; /* Fixed Packet Size (MSB...) */
buffer[21] = 0x00; /* ... */
buffer[22] = 0x00; /* ... */
buffer[23] = 0x00; /* Fixed Packet Size (...LSB) */
buffer[24] = 0x00; /* Logical Track Size (MSB...) */
buffer[25] = 0x05; /* ... */
buffer[26] = 0x7d; /* ... */
buffer[27] = 0xa4; /* Logical Track Size (...LSB) */
ret = 28;
break;
}
switch (pos) {
default:
/*
TODO: Does READ TRACK INFORMATION use track AAh
or the raw A0h, A1h, and A2h?
*/
if (pos == 0xaa)
real_pos = 0xa2;
for (int i = 0; i < num; i++) {
const raw_track_info_t *ct = &(t[i]);
if (ct->point == real_pos) {
track = ct;
break;
}
}
if (track == NULL)
ret = -pos;
else
ret = 36;
break;
case 0x00:
track = &lead_in;
ret = 36;
break;
case 0xff:
ret = -pos;
break;
}
break;
case 0x02:
for (int i = 0; i < num; i++) {
const raw_track_info_t *ct = &(t[i]);
if ((ct->session == pos) && (ct->point >= 1) && (ct->point <= 99)) {
track = ct;
break;
}
}
if (track == NULL)
ret = -pos;
else
ret = 36;
break;
}
if (ret == 36) {
uint32_t start = ((track->pm * 60 * 75) + (track->ps * 75) +
track->pf) - 150;
uint32_t len = 0x00000000;
uint8_t mode = 0xf;
memset(buffer, 0, 36);
buffer[0] = 0x00;
buffer[1] = 0x22;
buffer[2] = track->point; /* Track number (LSB). */
buffer[3] = track->session; /* Session number (LSB). */
/* Not damaged, primary copy. */
buffer[5] = track->adr_ctl & 0x04;
if ((track->point >= 1) && (track->point >= 99)) {
for (int i = 0; i < num; i++) {
const raw_track_info_t *ct = &(t[i]);
const uint32_t ts = ((ct->pm * 60 * 75) + (ct->ps * 75) +
ct->pf) - 150;
if ((ts > start) && ((ct->point == 0xa2) || ((ct->point >= 1) &&
(ct->point <= 99)))) {
len = ts - start;
break;
}
}
if (track->adr_ctl & 0x04) {
ret = read_data(dev, start, 0);
mode = dev->raw_buffer[dev->cur_buf][3];
}
} else if (track->point != 0xa2)
start = 0x00000000;
/* Not reserved track, not blank, not packet writing, not fixed packet. */
buffer[ 6] = mode << 0;
/* Last recorded address not valid, next recordable address not valid. */
buffer[ 7] = 0x00;
buffer[ 8] = (start >> 24) & 0xff;
buffer[ 9] = (start >> 16) & 0xff;
buffer[10] = (start >> 8) & 0xff;
buffer[11] = start & 0xff;
buffer[24] = (len >> 24) & 0xff;
buffer[25] = (len >> 16) & 0xff;
buffer[26] = (len >> 8) & 0xff;
buffer[27] = len & 0xff;
}
return ret;
}
uint8_t
cdrom_get_current_mode(cdrom_t *dev)
{
if (dev->cached_sector == -1)
(void) read_data(dev, dev->seek_pos, 0);
else
(void) read_data(dev, dev->cached_sector, 0);
return dev->raw_buffer[dev->cur_buf][3];
}
void
cdrom_set_empty(cdrom_t *dev)
{
dev->cd_status = CD_STATUS_EMPTY;
dev->cached_sector = -1;
}
void
cdrom_update_status(cdrom_t *dev)
{
const int was_empty = (dev->cd_status == CD_STATUS_EMPTY);
if (dev->ops->load != NULL)
dev->ops->load(dev->local);
/* All good, reset state. */
dev->seek_pos = 0;
dev->cd_buflen = 0;
if (dev->ops->is_dvd(dev->local)) {
if (cdrom_is_dvd(dev->type))
dev->cd_status = CD_STATUS_DVD;
else
dev->cd_status = CD_STATUS_DVD_REJECTED;
} else
dev->cd_status = dev->ops->has_audio(dev->local) ? CD_STATUS_STOPPED :
CD_STATUS_DATA_ONLY;
dev->cached_sector = -1;
dev->cdrom_capacity = dev->ops->get_last_block(dev->local);
if ((dev->cd_status != CD_STATUS_EMPTY) && (dev->cd_status != CD_STATUS_DVD_REJECTED)) {
/* Signal media change to the emulated machine. */
cdrom_insert(dev->id);
/* The drive was previously empty, transition directly to UNIT ATTENTION. */
if (was_empty)
cdrom_insert(dev->id);
}
}
int
cdrom_insert_blank(cdrom_t *dev, const char *fn)
{
int ret = 0;
// Create new image
dev->local = wimage_open(dev, dev->image_path);
// Clear cached sectors
dev->cached_sector = -1;
// If new image could not be created
if (dev->local == NULL) {
dev->ops = NULL;
dev->image_path[0] = 0;
ret = 1;
} else {
// All looking good, reset state
dev->seek_pos = 0;
dev->cd_buflen = 0;
dev->cd_status = CD_STATUS_DATA_ONLY;
dev->blank_media = CD_BLANK_CDR;
dev->cdrom_capacity = 0;
cdrom_log(dev->log, "Blank CD-R created\n");
cdrom_insert(dev->id);
}
return ret;
}
int
cdrom_load(cdrom_t *dev, const char *fn, const int skip_insert)
{
const int was_empty = cdrom_is_empty(dev->id);
int ret = 0;
/* Make sure to not STRCPY if the two are pointing
at the same place. */
if (fn != dev->image_path)
strcpy(dev->image_path, fn);
/* Open the target. */
if ((strlen(dev->image_path) != 0) &&
(strstr(dev->image_path, "ioctl://") == dev->image_path))
dev->local = ioctl_open(dev, dev->image_path);
else
dev->local = image_open(dev, dev->image_path);
dev->cached_sector = -1;
if (dev->local == NULL) {
dev->ops = NULL;
dev->image_path[0] = 0;
ret = 1;
} else {
/* All good, reset state. */
dev->seek_pos = 0;
dev->cd_buflen = 0;
if ((dev->ops->is_empty != NULL) && dev->ops->is_empty(dev->local))
dev->cd_status = CD_STATUS_EMPTY;
else if (dev->ops->is_dvd(dev->local)) {
if (cdrom_is_dvd(dev->type))
dev->cd_status = CD_STATUS_DVD;
else {
warning("DVD image \"%s\" in a CD-only drive, reporting as empty\n", fn);
dev->cd_status = CD_STATUS_DVD_REJECTED;
}
} else
dev->cd_status = dev->ops->has_audio(dev->local) ? CD_STATUS_STOPPED :
CD_STATUS_DATA_ONLY;
dev->cdrom_capacity = dev->ops->get_last_block(dev->local);
cdrom_log(dev->log, "CD-ROM capacity: %i sectors (%" PRIi64 " bytes)\n",
dev->cdrom_capacity, ((uint64_t) dev->cdrom_capacity) << 11ULL);
}
#ifdef ENABLE_CDROM_LOG
cdrom_toc_dump(dev);
#endif
if (!skip_insert && (dev->cd_status != CD_STATUS_EMPTY) && (dev->cd_status != CD_STATUS_DVD_REJECTED)) {
/* Signal media change to the emulated machine. */
cdrom_insert(dev->id);
/* The drive was previously empty, transition directly to UNIT ATTENTION. */
if (was_empty)
cdrom_insert(dev->id);
}
return ret;
}
/* Peform a master init on the entire module. */
void
cdrom_global_init(void)
{
/* Clear the global data. */
memset(cdrom, 0x00, sizeof(cdrom));
for (uint8_t i = 0; i < CDROM_NUM; i++)
cdrom[i].cached_sector = -1;
}
void
cdrom_hard_reset(void)
{
cdrom_assigned_letters = 0;
for (uint8_t i = 0; i < CDROM_NUM; i++) {
cdrom_t *dev = &cdrom[i];
if (dev->bus_type) {
dev->id = i;
const char *vendor = cdrom_drive_types[dev->type].vendor;
dev->is_early = cdrom_is_early(dev->type);
dev->is_bcd = !strcmp(vendor, "NEC");
dev->is_nec = (dev->bus_type == CDROM_BUS_SCSI) &&
!strcmp(vendor, "NEC");
dev->is_chinon = !strcmp(vendor, "CHINON");
dev->is_pioneer = !strcmp(vendor, "PIONEER");
dev->is_plextor = !strcmp(vendor, "PLEXTOR");
dev->is_yamaha = !strcmp(vendor, "YAMAHA");
dev->is_sony = (dev->bus_type == CDROM_BUS_SCSI) &&
(!strcmp(vendor, "DEC") ||
!strcmp(vendor, "ShinaKen") ||
!strcmp(vendor, "SONY") ||
!strcmp(vendor, "TEXEL"));
dev->is_toshiba = !strcmp(vendor, "TOSHIBA");
dev->c2_first = !strcmp(vendor, "NEC") ||
!strcmp(vendor, "PLEXTOR");
cdrom_drive_reset(dev);
char n[1024] = { 0 };
sprintf(n, "CD-ROM %i ", i + 1);
dev->log = log_open(n);
cdrom_log(dev->log, "Hard reset\n");
switch (dev->bus_type) {
case CDROM_BUS_ATAPI:
case CDROM_BUS_SCSI:
scsi_cdrom_drive_reset(i);
break;
default:
break;
}
dev->cd_status = CD_STATUS_EMPTY;
dev->host_letter = 0xff;
dev->cached_sector = -1;
if (strlen(dev->image_path) > 0) {
#ifdef _WIN32
if ((strlen(dev->image_path) >= 1) && (dev->image_path[strlen(dev->image_path) - 1] == '/'))
dev->image_path[strlen(dev->image_path) - 1] = '\\';
#else
if ((strlen(dev->image_path) >= 1) &&
(dev->image_path[strlen(dev->image_path) - 1] == '\\'))
dev->image_path[strlen(dev->image_path) - 1] = '/';
#endif
cdrom_load(dev, dev->image_path, 0);
}
for (uint32_t j = 0; j < _LUT_SIZE; ++j) {
dev->_F_LUT[j] = (j << 1) ^ (j & 0x80 ? 0x11d : 0);
dev->_B_LUT[j ^ dev->_F_LUT[j]] = j;
}
}
}
sound_cd_thread_reset();
}
void
cdrom_close(void)
{
for (uint8_t i = 0; i < CDROM_NUM; i++) {
cdrom_t *dev = &cdrom[i];
if (dev->bus_type == CDROM_BUS_SCSI)
memset(&scsi_devices[dev->scsi_device_id], 0x00, sizeof(scsi_device_t));
if (dev->close)
dev->close(dev->priv);
cdrom_unload(dev);
dev->ops = NULL;
dev->priv = NULL;
cdrom_drive_reset(dev);
if (dev->log != NULL) {
cdrom_log(dev->log, "Log closed\n");
log_close(dev->log);
dev->log = NULL;
}
}
}
/* Signal disc change to the emulated machine. */
void
cdrom_insert(const uint8_t id)
{
const cdrom_t *dev = &cdrom[id];
if (dev->bus_type && dev->insert)
dev->insert(dev->priv);
}
void
cdrom_exit(const uint8_t id)
{
cdrom_t *dev = &cdrom[id];
strcpy(dev->prev_image_path, dev->image_path);
dev->cached_sector = -1;
if (dev->ops) {
cdrom_unload(dev);
dev->ops = NULL;
}
memset(dev->image_path, 0, sizeof(dev->image_path));
cdrom_log(dev->log, "cdrom_exit(): cdrom_insert()\n");
cdrom_insert(id);
}
int
cdrom_is_empty(const uint8_t id)
{
const cdrom_t *dev = &cdrom[id];
int ret = 0;
/* This entire block should be in cdrom.c/cdrom_eject(dev*) ... */
if ((strlen(dev->image_path) == 0) || (dev->cd_status == CD_STATUS_EMPTY))
/* Switch from empty to empty. Do nothing. */
ret = 1;
return ret;
}
/* The mechanics of ejecting a CD-ROM from a drive. */
void
cdrom_eject(const uint8_t id)
{
const cdrom_t *dev = &cdrom[id];
if (strlen(dev->image_path) != 0) {
cdrom_exit(id);
plat_cdrom_ui_update(id, 0);
config_save();
}
}
/* The mechanics of re-loading a CD-ROM drive. */
void
cdrom_reload(const uint8_t id)
{
cdrom_t *dev = &cdrom[id];
if ((strcmp(dev->image_path, dev->prev_image_path) == 0) ||
(strlen(dev->prev_image_path) == 0) ||
(strlen(dev->image_path) > 0)) {
/* Switch from empty to empty. Do nothing. */
return;
}
cdrom_unload(dev);
dev->ops = NULL;
memset(dev->image_path, 0, sizeof(dev->image_path));
if (strlen(dev->image_path) > 0) {
/* Reload a previous image. */
if (strlen(dev->prev_image_path) > 0)
strcpy(dev->image_path, dev->prev_image_path);
#ifdef _WIN32
if ((strlen(dev->prev_image_path) > 0) && (strlen(dev->image_path) >= 1) &&
(dev->image_path[strlen(dev->image_path) - 1] == '/'))
dev->image_path[strlen(dev->image_path) - 1] = '\\';
#else
if ((strlen(dev->prev_image_path) > 0) && (strlen(dev->image_path) >= 1) &&
(dev->image_path[strlen(dev->image_path) - 1] == '\\'))
dev->image_path[strlen(dev->image_path) - 1] = '/';
#endif
cdrom_load(dev, dev->image_path, 0);
}
plat_cdrom_ui_update(id, 1);
config_save();
}
int
cdrom_send_cuesheet(cdrom_t *dev, const uint8_t *buffer, int len)
{
return dev->ops->send_cuesheet(dev->local, buffer, len);
}
int
cdrom_read_buffer_capacity(cdrom_t *dev, uint8_t *buffer, int block)
{
/* Buffer capacity hardcoded to 1MiB */
memset(buffer, 0, 12);
buffer[0] = 0x00; /* Data Length (MSB) */
buffer[1] = 0x0a; /* Data Length (LSB) */
buffer[2] = 0x00; /* Reserved */
buffer[3] = 0x00; /* Reserved */
if (block) { /* 512 blocks */
buffer[4] = 0x00; /* Length of the Buffer (MSB...) */
buffer[5] = 0x00; /* ... */
buffer[6] = 0x02; /* ... */
buffer[7] = 0x00; /* Length of the Buffer (...LSB) */
buffer[8] = 0x00; /* Blank Length of the Buffer (MSB...) */
buffer[9] = 0x00; /* ... */
buffer[10] = 0x00; /* ... */
buffer[11] = 0x00; /* Blank Length of the Buffer (...LSB) */
}
else { /* 1048576 bytes */
buffer[4] = 0x00; /* Length of the Buffer (MSB...) */
buffer[5] = 0x10; /* ... */
buffer[6] = 0x00; /* ... */
buffer[7] = 0x00; /* Length of the Buffer (...LSB) */
buffer[8] = 0x00; /* Blank Length of the Buffer (MSB...) */
buffer[9] = 0x10; /* ... */
buffer[10] = 0x00; /* ... */
buffer[11] = 0x00; /* Blank Length of the Buffer (...LSB) */
}
return 12;
}