3493 lines
104 KiB
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;
|
|
} |