2402 lines
58 KiB
C++
2402 lines
58 KiB
C++
/*
|
|
* UAE - The Un*x Amiga Emulator
|
|
*
|
|
* lowlevel cd device glue, scsi emulator
|
|
*
|
|
* Copyright 2009-2013 Toni Wilen
|
|
*
|
|
*/
|
|
|
|
#include "sysconfig.h"
|
|
#include "sysdeps.h"
|
|
#include "options.h"
|
|
#include "memory.h"
|
|
|
|
#include "traps.h"
|
|
#include "blkdev.h"
|
|
#include "scsidev.h"
|
|
#include <amiberry_filesys.hpp>
|
|
|
|
#include "savestate.h"
|
|
#include "crc32.h"
|
|
#include "threaddep/thread.h"
|
|
#include "execio.h"
|
|
#include "zfile.h"
|
|
#include "scsi.h"
|
|
#include "statusline.h"
|
|
|
|
#define PRE_INSERT_DELAY (3 * (currprefs.ntscmode ? 60 : 50))
|
|
|
|
struct blkdevstate
|
|
{
|
|
bool scsiemu;
|
|
int type;
|
|
struct device_functions *device_func;
|
|
int isopen;
|
|
int waspaused;
|
|
int delayed;
|
|
uae_sem_t sema;
|
|
int sema_cnt;
|
|
int current_pos;
|
|
int play_end_pos;
|
|
uae_u8 play_qcode[SUBQ_SIZE];
|
|
TCHAR newimagefile[256];
|
|
int imagechangetime;
|
|
bool cdimagefileinuse;
|
|
int wasopen;
|
|
bool mediawaschanged;
|
|
struct scsi_data_tape *tape;
|
|
bool showstatusline;
|
|
};
|
|
|
|
struct blkdevstate state[MAX_TOTAL_SCSI_DEVICES];
|
|
|
|
static bool dev_init;
|
|
|
|
/* convert minutes, seconds and frames -> logical sector number */
|
|
int msf2lsn (int msf)
|
|
{
|
|
int sector = (((msf >> 16) & 0xff) * 60 * 75 + ((msf >> 8) & 0xff) * 75 + ((msf >> 0) & 0xff));
|
|
sector -= 150;
|
|
return sector;
|
|
}
|
|
|
|
/* convert logical sector number -> minutes, seconds and frames */
|
|
int lsn2msf (int sectors)
|
|
{
|
|
int msf;
|
|
sectors += 150;
|
|
msf = (sectors / (75 * 60)) << 16;
|
|
msf |= ((sectors / 75) % 60) << 8;
|
|
msf |= (sectors % 75) << 0;
|
|
return msf;
|
|
}
|
|
|
|
uae_u8 frombcd (uae_u8 v)
|
|
{
|
|
return (v >> 4) * 10 + (v & 15);
|
|
}
|
|
uae_u8 tobcd (uae_u8 v)
|
|
{
|
|
return ((v / 10) << 4) | (v % 10);
|
|
}
|
|
int fromlongbcd (uae_u8 *p)
|
|
{
|
|
return (frombcd (p[0]) << 16) | (frombcd (p[1]) << 8) | (frombcd (p[2]) << 0);
|
|
}
|
|
void tolongbcd (uae_u8 *p, int v)
|
|
{
|
|
p[0] = tobcd ((v >> 16) & 0xff);
|
|
p[1] = tobcd ((v >> 8) & 0xff);
|
|
p[2] = tobcd ((v >> 0) & 0xff);
|
|
}
|
|
|
|
static struct cd_toc *gettoc (int unitnum, struct cd_toc_head *th, int block)
|
|
{
|
|
if (th->lastaddress == 0) {
|
|
if (unitnum < 0)
|
|
return NULL;
|
|
if (!sys_command_cd_toc(unitnum, th))
|
|
return NULL;
|
|
}
|
|
for (int i = th->first_track_offset + 1; i <= th->last_track_offset; i++) {
|
|
struct cd_toc *t = &th->toc[i];
|
|
if (block < t->paddress)
|
|
return t - 1;
|
|
}
|
|
return &th->toc[th->last_track_offset];
|
|
}
|
|
|
|
int cdtracknumber(struct cd_toc_head *th, int block)
|
|
{
|
|
struct cd_toc *t = gettoc(-1, th, block);
|
|
if (!t)
|
|
return -1;
|
|
return t->track;
|
|
}
|
|
int isaudiotrack (struct cd_toc_head *th, int block)
|
|
{
|
|
struct cd_toc *t = gettoc (-1, th, block);
|
|
if (!t)
|
|
return 0;
|
|
return (t->control & 0x0c) != 4;
|
|
}
|
|
int isdatatrack (struct cd_toc_head *th, int block)
|
|
{
|
|
return !isaudiotrack (th, block);
|
|
}
|
|
|
|
static int cdscsidevicetype[MAX_TOTAL_SCSI_DEVICES];
|
|
|
|
static struct device_functions *devicetable[] = {
|
|
NULL,
|
|
&devicefunc_cdimage,
|
|
#ifdef WITH_SCSI_IOCTL
|
|
&devicefunc_scsi_ioctl,
|
|
#else
|
|
NULL,
|
|
#endif
|
|
#ifdef WITH_SCSI_SPTI
|
|
&devicefunc_scsi_spti,
|
|
#else
|
|
NULL,
|
|
#endif
|
|
};
|
|
#define NUM_DEVICE_TABLE_ENTRIES 4
|
|
static int driver_installed[NUM_DEVICE_TABLE_ENTRIES];
|
|
|
|
static void install_driver (int flags)
|
|
{
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
|
|
struct blkdevstate *st = &state[i];
|
|
st->scsiemu = false;
|
|
st->type = -1;
|
|
st->device_func = NULL;
|
|
}
|
|
if (flags > 0) {
|
|
state[0].device_func = devicetable[flags];
|
|
state[0].scsiemu = true;
|
|
} else {
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
|
|
struct blkdevstate *st = &state[i];
|
|
st->scsiemu = false;
|
|
st->device_func = NULL;
|
|
switch (cdscsidevicetype[i])
|
|
{
|
|
case SCSI_UNIT_IMAGE:
|
|
st->device_func = devicetable[SCSI_UNIT_IMAGE];
|
|
st->scsiemu = true;
|
|
break;
|
|
case SCSI_UNIT_IOCTL:
|
|
st->device_func = devicetable[SCSI_UNIT_IOCTL];
|
|
st->scsiemu = true;
|
|
break;
|
|
case SCSI_UNIT_SPTI:
|
|
//if (currprefs.win32_uaescsimode == UAESCSI_CDEMU) {
|
|
// st->device_func = devicetable[SCSI_UNIT_IOCTL];
|
|
// st->scsiemu = true;
|
|
//} else {
|
|
// st->device_func = devicetable[SCSI_UNIT_SPTI];
|
|
//}
|
|
break;
|
|
}
|
|
// do not default to image mode if unit 1+ and automount
|
|
if (i == 0) {
|
|
// use image mode if driver disabled
|
|
for (int j = 1; j < NUM_DEVICE_TABLE_ENTRIES; j++) {
|
|
if (devicetable[j] == st->device_func && driver_installed[j] < 0) {
|
|
st->device_func = devicetable[SCSI_UNIT_IMAGE];
|
|
st->scsiemu = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int j = 1; j < NUM_DEVICE_TABLE_ENTRIES; j++) {
|
|
if (devicetable[j] == NULL) {
|
|
continue;
|
|
}
|
|
if (!driver_installed[j]) {
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
|
|
struct blkdevstate *st = &state[i];
|
|
if (st->device_func == devicetable[j]) {
|
|
int ok = st->device_func->openbus (0);
|
|
if (!ok && st->device_func != devicetable[SCSI_UNIT_IMAGE]) {
|
|
st->device_func = devicetable[SCSI_UNIT_IMAGE];
|
|
st->scsiemu = true;
|
|
write_log (_T("Fallback to image mode, unit %d.\n"), i);
|
|
driver_installed[j] = -1;
|
|
} else {
|
|
driver_installed[j] = 1;
|
|
}
|
|
write_log (_T("%s driver installed, ok=%d\n"), st->device_func->name, ok);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void blkdev_default_prefs (struct uae_prefs *p)
|
|
{
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
|
|
p->cdslots[i].name[0] = 0;
|
|
p->cdslots[i].inuse = false;
|
|
p->cdslots[i].type = SCSI_UNIT_DEFAULT;
|
|
p->cdslots[i].temporary = false;
|
|
cdscsidevicetype[i] = SCSI_UNIT_DEFAULT;
|
|
}
|
|
}
|
|
|
|
void blkdev_fix_prefs (struct uae_prefs *p)
|
|
{
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
|
|
cdscsidevicetype[i] = p->cdslots[i].type;
|
|
if (p->cdslots[i].inuse == false && p->cdslots[i].name[0] && p->cdslots[i].type != SCSI_UNIT_DISABLED)
|
|
p->cdslots[i].inuse = true;
|
|
}
|
|
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
|
|
if (cdscsidevicetype[i] != SCSI_UNIT_DEFAULT && (currprefs.scsi == 0))
|
|
continue;
|
|
if (p->cdslots[i].inuse || p->cdslots[i].name[0]) {
|
|
TCHAR *name = p->cdslots[i].name;
|
|
if (_tcslen (name) == 3 && name[1] == ':' && name[2] == '\\') {
|
|
//if (currprefs.scsi && (currprefs.win32_uaescsimode == UAESCSI_SPTI || currprefs.win32_uaescsimode == UAESCSI_SPTISCAN))
|
|
// cdscsidevicetype[i] = SCSI_UNIT_SPTI;
|
|
//else
|
|
cdscsidevicetype[i] = SCSI_UNIT_IOCTL;
|
|
} else {
|
|
cdscsidevicetype[i] = SCSI_UNIT_IMAGE;
|
|
}
|
|
} else if (currprefs.scsi) {
|
|
//if (currprefs.win32_uaescsimode == UAESCSI_CDEMU)
|
|
// cdscsidevicetype[i] = SCSI_UNIT_IOCTL;
|
|
//else
|
|
cdscsidevicetype[i] = SCSI_UNIT_SPTI;
|
|
} else {
|
|
cdscsidevicetype[i] = SCSI_UNIT_IOCTL;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static bool getsem (int unitnum, bool dowait)
|
|
{
|
|
struct blkdevstate *st = &state[unitnum];
|
|
if (st->sema == NULL)
|
|
uae_sem_init (&st->sema, 0, 1);
|
|
bool gotit = false;
|
|
if (dowait) {
|
|
uae_sem_wait (&st->sema);
|
|
gotit = true;
|
|
} else {
|
|
gotit = uae_sem_trywait (&st->sema) == 0;
|
|
}
|
|
if (gotit)
|
|
st->sema_cnt++;
|
|
if (st->sema_cnt > 1)
|
|
write_log (_T("CD: unitsem%d acquire mismatch! cnt=%d\n"), unitnum, st->sema_cnt);
|
|
return gotit;
|
|
}
|
|
static bool getsem (int unitnum)
|
|
{
|
|
return getsem (unitnum, false);
|
|
}
|
|
static void freesem (int unitnum)
|
|
{
|
|
struct blkdevstate *st = &state[unitnum];
|
|
st->sema_cnt--;
|
|
if (st->sema_cnt < 0)
|
|
write_log (_T("CD: unitsem%d release mismatch! cnt=%d\n"), unitnum, st->sema_cnt);
|
|
uae_sem_post (&st->sema);
|
|
}
|
|
static void sys_command_close_internal (int unitnum)
|
|
{
|
|
struct blkdevstate *st = &state[unitnum];
|
|
getsem (unitnum, true);
|
|
st->waspaused = 0;
|
|
if (st->isopen <= 0)
|
|
write_log (_T("BUG unit %d close: opencnt=%d!\n"), unitnum, st->isopen);
|
|
if (st->device_func) {
|
|
state[unitnum].device_func->closedev (unitnum);
|
|
if (st->isopen > 0)
|
|
st->isopen--;
|
|
}
|
|
freesem (unitnum);
|
|
if (st->isopen == 0) {
|
|
uae_sem_destroy (&st->sema);
|
|
st->sema = NULL;
|
|
}
|
|
}
|
|
|
|
static int sys_command_open_internal (int unitnum, const TCHAR *ident, cd_standard_unit csu)
|
|
{
|
|
struct blkdevstate *st = &state[unitnum];
|
|
int ret = 0;
|
|
if (st->sema == NULL)
|
|
uae_sem_init (&st->sema, 0, 1);
|
|
getsem (unitnum, true);
|
|
if (st->isopen)
|
|
write_log (_T("BUG unit %d open: opencnt=%d!\n"), unitnum, st->isopen);
|
|
if (st->device_func) {
|
|
ret = state[unitnum].device_func->opendev (unitnum, ident, csu != CD_STANDARD_UNIT_DEFAULT);
|
|
if (ret)
|
|
st->isopen++;
|
|
}
|
|
freesem (unitnum);
|
|
return ret;
|
|
}
|
|
|
|
static int getunitinfo (int unitnum, int drive, cd_standard_unit csu, int *isaudio)
|
|
{
|
|
struct device_info di;
|
|
if (sys_command_info (unitnum, &di, 0)) {
|
|
write_log (_T("Scanning drive %s: "), di.label);
|
|
if (di.media_inserted) {
|
|
if (isaudiotrack (&di.toc, 0)) {
|
|
if (*isaudio == 0)
|
|
*isaudio = drive;
|
|
write_log (_T("CDA"));
|
|
}
|
|
uae_u8 buffer[2048];
|
|
if (sys_command_cd_read (unitnum, buffer, 16, 1)) {
|
|
if (!memcmp (buffer + 8, "CDTV", 4) || !memcmp (buffer + 8, "CD32", 4) || !memcmp (buffer + 8, "COMM", 4)) {
|
|
uae_u32 crc;
|
|
write_log (_T("CD32 or CDTV"));
|
|
if (sys_command_cd_read (unitnum, buffer, 21, 1)) {
|
|
crc = get_crc32 (buffer, sizeof buffer);
|
|
if (crc == 0xe56c340f) {
|
|
write_log (_T(" [CD32.TM]"));
|
|
if (csu == CD_STANDARD_UNIT_CD32) {
|
|
write_log (_T("\n"));
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
if (csu == CD_STANDARD_UNIT_CDTV || csu == CD_STANDARD_UNIT_CD32) {
|
|
write_log (_T("\n"));
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
write_log (_T("no media"));
|
|
}
|
|
}
|
|
write_log (_T("\n"));
|
|
return 0;
|
|
}
|
|
|
|
static int get_standard_cd_unit2 (struct uae_prefs *p, cd_standard_unit csu)
|
|
{
|
|
int unitnum = 0;
|
|
int isaudio = 0;
|
|
if (p->cdslots[unitnum].name[0] || p->cdslots[unitnum].inuse) {
|
|
if (p->cdslots[unitnum].name[0]) {
|
|
device_func_init (SCSI_UNIT_IOCTL);
|
|
if (!sys_command_open_internal (unitnum, p->cdslots[unitnum].name, csu)) {
|
|
device_func_init (SCSI_UNIT_IMAGE);
|
|
if (!sys_command_open_internal (unitnum, p->cdslots[unitnum].name, csu))
|
|
goto fallback;
|
|
}
|
|
} else {
|
|
goto fallback;
|
|
}
|
|
return unitnum;
|
|
}
|
|
if (isaudio) {
|
|
TCHAR vol[100];
|
|
_stprintf (vol, _T("%c:\\"), isaudio);
|
|
if (sys_command_open_internal (unitnum, vol, csu))
|
|
return unitnum;
|
|
}
|
|
fallback:
|
|
device_func_init (SCSI_UNIT_IMAGE);
|
|
if (!sys_command_open_internal (unitnum, _T(""), csu)) {
|
|
write_log (_T("image mounter failed to open as empty!?\n"));
|
|
return -1;
|
|
}
|
|
return unitnum;
|
|
}
|
|
|
|
static void cd_statusline_label(int unitnum)
|
|
{
|
|
TCHAR *p = currprefs.cdslots[unitnum].name;
|
|
if (p[0]) {
|
|
TCHAR name[MAX_DPATH];
|
|
struct device_info di;
|
|
cfgfile_resolve_path_out_load(p, name, sizeof(name) / sizeof(TCHAR), PATH_CD);
|
|
const TCHAR *fname = my_getfilepart(name);
|
|
if (sys_command_info(unitnum, &di, 0) && di.volume_id[0])
|
|
statusline_add_message(STATUSTYPE_CD, _T("CD%d: [%s] %s"), unitnum, di.volume_id, fname);
|
|
else
|
|
statusline_add_message(STATUSTYPE_CD, _T("CD%d: %s"), unitnum, fname);
|
|
}
|
|
}
|
|
|
|
int get_standard_cd_unit (cd_standard_unit csu)
|
|
{
|
|
int unitnum = get_standard_cd_unit2 (&currprefs, csu);
|
|
if (unitnum < 0)
|
|
return -1;
|
|
struct blkdevstate *st = &state[unitnum];
|
|
#ifdef RETROPLATFORM
|
|
rp_cd_device_enable (unitnum, true);
|
|
#endif
|
|
st->delayed = 0;
|
|
if (currprefs.cdslots[unitnum].delayed) {
|
|
st->delayed = PRE_INSERT_DELAY;
|
|
}
|
|
st->showstatusline = true;
|
|
return unitnum;
|
|
}
|
|
|
|
void close_standard_cd_unit (int unitnum)
|
|
{
|
|
sys_command_close (unitnum);
|
|
}
|
|
|
|
int sys_command_isopen (int unitnum)
|
|
{
|
|
struct blkdevstate *st = &state[unitnum];
|
|
return st->isopen;
|
|
}
|
|
|
|
int sys_command_open (int unitnum)
|
|
{
|
|
struct blkdevstate *st = &state[unitnum];
|
|
blkdev_fix_prefs (&currprefs);
|
|
if (!dev_init) {
|
|
device_func_init (0);
|
|
}
|
|
|
|
if (st->isopen) {
|
|
st->isopen++;
|
|
return -1;
|
|
}
|
|
st->waspaused = 0;
|
|
int v = sys_command_open_internal (unitnum, currprefs.cdslots[unitnum].name[0] ? currprefs.cdslots[unitnum].name : NULL, CD_STANDARD_UNIT_DEFAULT);
|
|
if (!v)
|
|
return 0;
|
|
#ifdef RETROPLATFORM
|
|
rp_cd_device_enable (unitnum, true);
|
|
#endif
|
|
return v;
|
|
}
|
|
|
|
void sys_command_close (int unitnum)
|
|
{
|
|
struct blkdevstate *st = &state[unitnum];
|
|
if (st->isopen > 1) {
|
|
st->isopen--;
|
|
return;
|
|
}
|
|
//if (st->tape) {
|
|
// tape_free (st->tape);
|
|
// st->tape = NULL;
|
|
//}
|
|
sys_command_close_internal (unitnum);
|
|
}
|
|
|
|
void blkdev_cd_change (int unitnum, const TCHAR *name)
|
|
{
|
|
struct device_info di;
|
|
sys_command_info (unitnum, &di, 1);
|
|
#ifdef RETROPLATFORM
|
|
rp_cd_image_change (unitnum, name);
|
|
#endif
|
|
}
|
|
|
|
void device_func_reset(void)
|
|
{
|
|
// if reset during delayed CD change, re-insert the CD immediately
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
|
|
struct blkdevstate *st = &state[i];
|
|
if (st->imagechangetime > 0 && st->newimagefile[0] && !currprefs.cdslots[i].name[0]) {
|
|
_tcscpy(changed_prefs.cdslots[i].name, st->newimagefile);
|
|
_tcscpy(currprefs.cdslots[i].name, st->newimagefile);
|
|
cd_statusline_label(i);
|
|
}
|
|
st->imagechangetime = 0;
|
|
st->newimagefile[0] = 0;
|
|
st->mediawaschanged = false;
|
|
st->waspaused = false;
|
|
}
|
|
}
|
|
|
|
void device_func_free(void)
|
|
{
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
|
|
struct blkdevstate *st = &state[i];
|
|
st->wasopen = 0;
|
|
st->waspaused = false;
|
|
st->mediawaschanged = false;
|
|
st->imagechangetime = 0;
|
|
st->cdimagefileinuse = false;
|
|
st->newimagefile[0] = 0;
|
|
}
|
|
dev_init = false;
|
|
}
|
|
|
|
int device_func_init (int flags)
|
|
{
|
|
blkdev_fix_prefs (&currprefs);
|
|
install_driver (flags);
|
|
dev_init = true;
|
|
return 1;
|
|
}
|
|
|
|
bool blkdev_get_info (struct uae_prefs *p, int unitnum, struct device_info *di)
|
|
{
|
|
bool open = true, opened = false, ok = false;
|
|
struct blkdevstate *st = &state[unitnum];
|
|
if (!st->isopen) {
|
|
blkdev_fix_prefs (p);
|
|
install_driver (0);
|
|
opened = true;
|
|
open = sys_command_open_internal (unitnum, p->cdslots[unitnum].name[0] ? p->cdslots[unitnum].name : NULL, CD_STANDARD_UNIT_DEFAULT) != 0;
|
|
}
|
|
if (open) {
|
|
ok = sys_command_info (unitnum, di, true) != 0;
|
|
}
|
|
if (open && opened)
|
|
sys_command_close_internal (unitnum);
|
|
return ok;
|
|
}
|
|
|
|
void blkdev_entergui (void)
|
|
{
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
|
|
struct blkdevstate *st = &state[i];
|
|
st->waspaused = 0;
|
|
struct device_info di;
|
|
if (sys_command_info (i, &di, 1)) {
|
|
if (sys_command_cd_pause (i, 1) == 0)
|
|
st->waspaused = 1;
|
|
}
|
|
}
|
|
}
|
|
void blkdev_exitgui (void)
|
|
{
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
|
|
struct blkdevstate *st = &state[i];
|
|
if (st->waspaused) {
|
|
struct device_info di;
|
|
if (sys_command_info (i, &di, 1)) {
|
|
sys_command_cd_pause (i, 0);
|
|
}
|
|
}
|
|
st->waspaused = 0;
|
|
}
|
|
}
|
|
|
|
void check_prefs_changed_cd (void)
|
|
{
|
|
if (!config_changed)
|
|
return;
|
|
}
|
|
|
|
static void check_changes (int unitnum)
|
|
{
|
|
struct blkdevstate *st = &state[unitnum];
|
|
bool changed = false;
|
|
bool gotsem = false;
|
|
|
|
if (st->device_func == NULL)
|
|
return;
|
|
|
|
if (st->showstatusline) {
|
|
cd_statusline_label(unitnum);
|
|
st->showstatusline = 0;
|
|
}
|
|
|
|
if (st->delayed) {
|
|
st->delayed--;
|
|
if (st->delayed == 0)
|
|
write_log (_T("CD: startup delayed insert '%s'\n"), currprefs.cdslots[unitnum].name[0] ? currprefs.cdslots[unitnum].name : _T("<EMPTY>"));
|
|
return;
|
|
}
|
|
|
|
if (_tcscmp (changed_prefs.cdslots[unitnum].name, currprefs.cdslots[unitnum].name) != 0)
|
|
changed = true;
|
|
if (!changed && changed_prefs.cdslots[unitnum].name[0] == 0 && changed_prefs.cdslots[unitnum].inuse != currprefs.cdslots[unitnum].inuse)
|
|
changed = true;
|
|
|
|
if (changed) {
|
|
bool wasimage = currprefs.cdslots[unitnum].name[0] != 0;
|
|
if (st->sema)
|
|
gotsem = getsem (unitnum, true);
|
|
st->cdimagefileinuse = changed_prefs.cdslots[unitnum].inuse;
|
|
_tcscpy (st->newimagefile, changed_prefs.cdslots[unitnum].name);
|
|
changed_prefs.cdslots[unitnum].name[0] = currprefs.cdslots[unitnum].name[0] = 0;
|
|
currprefs.cdslots[unitnum].inuse = changed_prefs.cdslots[unitnum].inuse;
|
|
int pollmode = 0;
|
|
st->imagechangetime = 3 * 50;
|
|
struct device_info di;
|
|
st->device_func->info (unitnum, &di, 0, -1);
|
|
if (st->wasopen >= 0)
|
|
st->wasopen = di.open ? 1 : 0;
|
|
if (st->wasopen) {
|
|
st->device_func->closedev (unitnum);
|
|
st->wasopen = -1;
|
|
#ifdef SCSIEMU
|
|
if (currprefs.scsi) {
|
|
scsi_do_disk_change (unitnum, 0, &pollmode);
|
|
if (pollmode)
|
|
st->imagechangetime = 8 * 50;
|
|
if (filesys_do_disk_change (unitnum, 0)) {
|
|
st->imagechangetime = st->newimagefile[0] ? 3 * 50 : 0;
|
|
pollmode = 0;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
write_log (_T("CD: eject (%s) open=%d\n"), pollmode ? _T("slow") : _T("fast"), st->wasopen ? 1 : 0);
|
|
if (wasimage)
|
|
statusline_add_message(STATUSTYPE_CD, _T("CD%d: -"), unitnum);
|
|
|
|
#ifdef RETROPLATFORM
|
|
rp_cd_image_change (unitnum, NULL);
|
|
#endif
|
|
if (gotsem) {
|
|
freesem (unitnum);
|
|
gotsem = false;
|
|
}
|
|
}
|
|
if (st->imagechangetime == 0)
|
|
return;
|
|
st->imagechangetime--;
|
|
if (st->imagechangetime > 0)
|
|
return;
|
|
if (st->sema)
|
|
gotsem = getsem (unitnum, true);
|
|
_tcscpy (currprefs.cdslots[unitnum].name, st->newimagefile);
|
|
_tcscpy (changed_prefs.cdslots[unitnum].name, st->newimagefile);
|
|
currprefs.cdslots[unitnum].inuse = changed_prefs.cdslots[unitnum].inuse = st->cdimagefileinuse;
|
|
st->newimagefile[0] = 0;
|
|
write_log (_T("CD: delayed insert '%s' (open=%d,unit=%d)\n"), currprefs.cdslots[unitnum].name[0] ? currprefs.cdslots[unitnum].name : _T("<EMPTY>"), st->wasopen ? 1 : 0, unitnum);
|
|
device_func_init (0);
|
|
if (st->wasopen) {
|
|
if (!st->device_func->opendev (unitnum, currprefs.cdslots[unitnum].name, 0)) {
|
|
write_log (_T("-> device open failed\n"));
|
|
st->wasopen = 0;
|
|
} else {
|
|
st->wasopen = 1;
|
|
write_log (_T("-> device reopened\n"));
|
|
}
|
|
}
|
|
#ifdef SCSIEMU
|
|
if (currprefs.scsi && st->wasopen) {
|
|
struct device_info di;
|
|
st->device_func->info (unitnum, &di, 0, -1);
|
|
int pollmode;
|
|
if (gotsem) {
|
|
freesem (unitnum);
|
|
gotsem = false;
|
|
}
|
|
scsi_do_disk_change (unitnum, 1, &pollmode);
|
|
filesys_do_disk_change (unitnum, 1);
|
|
}
|
|
#endif
|
|
st->mediawaschanged = true;
|
|
st->showstatusline = true;
|
|
if (gotsem) {
|
|
freesem (unitnum);
|
|
gotsem = false;
|
|
}
|
|
#ifdef RETROPLATFORM
|
|
rp_cd_image_change (unitnum, currprefs.cdslots[unitnum].name);
|
|
#endif
|
|
|
|
set_config_changed ();
|
|
|
|
}
|
|
|
|
void blkdev_vsync (void)
|
|
{
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++)
|
|
check_changes (i);
|
|
}
|
|
|
|
static int do_scsi (int unitnum, uae_u8 *cmd, int cmdlen)
|
|
{
|
|
uae_u8 *p = state[unitnum].device_func->exec_out (unitnum, cmd, cmdlen);
|
|
return p != NULL;
|
|
}
|
|
static int do_scsi (int unitnum, uae_u8 *cmd, int cmdlen, uae_u8 *out, int outsize)
|
|
{
|
|
uae_u8 *p = state[unitnum].device_func->exec_in (unitnum, cmd, cmdlen, &outsize);
|
|
if (p)
|
|
memcpy (out, p, outsize);
|
|
return p != NULL;
|
|
}
|
|
|
|
static int failunit (int unitnum)
|
|
{
|
|
if (unitnum < 0 || unitnum >= MAX_TOTAL_SCSI_DEVICES)
|
|
return 1;
|
|
if (state[unitnum].device_func == NULL)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static int audiostatus (int unitnum)
|
|
{
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
uae_u8 cmd[10] = {0x42,2,0x40,1,0,0,0,(uae_u8)(DEVICE_SCSI_BUFSIZE>>8),(uae_u8)(DEVICE_SCSI_BUFSIZE&0xff),0};
|
|
uae_u8 *p = state[unitnum].device_func->exec_in (unitnum, cmd, sizeof (cmd), 0);
|
|
freesem (unitnum);
|
|
if (!p)
|
|
return 0;
|
|
return p[1];
|
|
}
|
|
|
|
/* pause/unpause CD audio */
|
|
int sys_command_cd_pause (int unitnum, int paused)
|
|
{
|
|
if (failunit (unitnum))
|
|
return -1;
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
int v;
|
|
if (state[unitnum].device_func->pause == NULL) {
|
|
int as = audiostatus (unitnum);
|
|
uae_u8 cmd[10] = {0x4b,0,0,0,0,0,0,0,(uae_u8)(paused?0:1),0};
|
|
do_scsi (unitnum, cmd, sizeof cmd);
|
|
v = as == AUDIO_STATUS_PAUSED;
|
|
} else {
|
|
v = state[unitnum].device_func->pause (unitnum, paused);
|
|
}
|
|
freesem (unitnum);
|
|
return v;
|
|
}
|
|
|
|
/* stop CD audio */
|
|
void sys_command_cd_stop (int unitnum)
|
|
{
|
|
if (failunit (unitnum))
|
|
return;
|
|
if (!getsem (unitnum))
|
|
return;
|
|
if (state[unitnum].device_func->stop == NULL) {
|
|
int as = audiostatus (unitnum);
|
|
uae_u8 cmd[6] = {0x4e,0,0,0,0,0};
|
|
do_scsi (unitnum, cmd, sizeof cmd);
|
|
} else {
|
|
state[unitnum].device_func->stop (unitnum);
|
|
}
|
|
freesem (unitnum);
|
|
}
|
|
|
|
/* play CD audio */
|
|
int sys_command_cd_play (int unitnum, int startlsn, int endlsn, int scan)
|
|
{
|
|
int v;
|
|
if (failunit (unitnum))
|
|
return 0;
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
state[unitnum].play_end_pos = endlsn;
|
|
if (state[unitnum].device_func->play == NULL) {
|
|
uae_u8 cmd[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
|
|
int startmsf = lsn2msf (startlsn);
|
|
int endmsf = lsn2msf (endlsn);
|
|
cmd[0] = 0x47;
|
|
cmd[3] = (uae_u8)(startmsf >> 16);
|
|
cmd[4] = (uae_u8)(startmsf >> 8);
|
|
cmd[5] = (uae_u8)(startmsf >> 0);
|
|
cmd[6] = (uae_u8)(endmsf >> 16);
|
|
cmd[7] = (uae_u8)(endmsf >> 8);
|
|
cmd[8] = (uae_u8)(endmsf >> 0);
|
|
v = do_scsi (unitnum, cmd, sizeof cmd) ? 0 : 1;
|
|
} else {
|
|
v = state[unitnum].device_func->play (unitnum, startlsn, endlsn, scan, NULL, NULL);
|
|
}
|
|
freesem (unitnum);
|
|
return v;
|
|
}
|
|
|
|
/* play CD audio with subchannels */
|
|
int sys_command_cd_play (int unitnum, int startlsn, int endlsn, int scan, play_status_callback statusfunc, play_subchannel_callback subfunc)
|
|
{
|
|
int v;
|
|
if (failunit (unitnum))
|
|
return 0;
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
state[unitnum].play_end_pos = endlsn;
|
|
if (state[unitnum].device_func->play == NULL)
|
|
v = sys_command_cd_play (unitnum, startlsn, endlsn, scan);
|
|
else
|
|
v = state[unitnum].device_func->play (unitnum, startlsn, endlsn, scan, statusfunc, subfunc);
|
|
freesem (unitnum);
|
|
return v;
|
|
}
|
|
|
|
/* set CD audio volume */
|
|
uae_u32 sys_command_cd_volume (int unitnum, uae_u16 volume_left, uae_u16 volume_right)
|
|
{
|
|
int v;
|
|
if (failunit (unitnum))
|
|
return 0;
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
if (state[unitnum].device_func->volume == NULL)
|
|
v = -1;
|
|
else
|
|
v = state[unitnum].device_func->volume (unitnum, volume_left, volume_right);
|
|
freesem (unitnum);
|
|
return v;
|
|
}
|
|
|
|
/* read qcode */
|
|
int sys_command_cd_qcode (int unitnum, uae_u8 *buf, int sector, bool all)
|
|
{
|
|
int v;
|
|
if (failunit (unitnum))
|
|
return 0;
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
if (state[unitnum].device_func->qcode == NULL) {
|
|
if (all) {
|
|
v = 0;
|
|
} else {
|
|
uae_u8 cmd[10] = {0x42,2,0x40,1,0,0,0,(uae_u8)(SUBQ_SIZE>>8),(uae_u8)(SUBQ_SIZE&0xff),0};
|
|
v = do_scsi (unitnum, cmd, sizeof cmd, buf, SUBQ_SIZE);
|
|
}
|
|
} else {
|
|
v = state[unitnum].device_func->qcode (unitnum, buf, sector, all);
|
|
}
|
|
freesem (unitnum);
|
|
return v;
|
|
};
|
|
|
|
/* read table of contents */
|
|
int sys_command_cd_toc (int unitnum, struct cd_toc_head *th)
|
|
{
|
|
int v;
|
|
if (failunit (unitnum))
|
|
return 0;
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
if (state[unitnum].device_func->toc == NULL) {
|
|
uae_u8 buf[4 + 8 * 103];
|
|
int size = sizeof buf;
|
|
uae_u8 cmd [10] = { 0x43,0,0,0,0,0,0,(uae_u8)(size>>8),(uae_u8)(size&0xff),0};
|
|
v = do_scsi(unitnum, cmd, sizeof cmd, buf, size);
|
|
if (v > 0) {
|
|
th->first_track = buf[2];
|
|
th->last_track = buf[3];
|
|
th->tracks = th->last_track - th->first_track + 1;
|
|
th->firstaddress = 0;
|
|
th->points = th->tracks + 1;
|
|
int len = (buf[0] << 8) | buf[1];
|
|
uae_u8 *p = buf + 4;
|
|
if ((th->tracks + 1) * 8 > len) {
|
|
freesem(unitnum);
|
|
return 0;
|
|
}
|
|
struct cd_toc *t = &th->toc[th->first_track];
|
|
for (int i = th->first_track; i <= th->last_track; i++) {
|
|
t->adr = p[1] >> 4;
|
|
t->control = p[1] & 15;
|
|
t->point = t->track = p[2];
|
|
t->paddress = (p[5] << 16) | (p[6] << 8) | (p[7] << 0);
|
|
p += 8;
|
|
t++;
|
|
}
|
|
th->lastaddress = (p[5] << 16) | (p[6] << 8) | (p[7] << 0);
|
|
t->track = t->point = 0xaa;
|
|
th->first_track_offset = 0;
|
|
th->last_track_offset = th->last_track;
|
|
v = 1;
|
|
}
|
|
} else {
|
|
v = state[unitnum].device_func->toc (unitnum, th);
|
|
}
|
|
freesem (unitnum);
|
|
return v;
|
|
}
|
|
|
|
/* read one cd sector */
|
|
int sys_command_cd_read (int unitnum, uae_u8 *data, int block, int size)
|
|
{
|
|
int v;
|
|
if (failunit (unitnum))
|
|
return 0;
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
if (state[unitnum].device_func->read == NULL) {
|
|
uae_u8 cmd1[12] = { 0x28, 0, (uae_u8)(block >> 24), (uae_u8)(block >> 16), (uae_u8)(block >> 8), (uae_u8)(block >> 0), 0, (uae_u8)(size >> 8), (uae_u8)(size >> 0), 0, 0, 0 };
|
|
v = do_scsi (unitnum, cmd1, sizeof cmd1, data, size * 2048);
|
|
#if 0
|
|
if (!v) {
|
|
uae_u8 cmd2[12] = { 0xbe, 0, block >> 24, block >> 16, block >> 8, block >> 0, size >> 16, size >> 8, size >> 0, 0x10, 0, 0 };
|
|
v = do_scsi (unitnum, cmd2, sizeof cmd2, data, size * 2048);
|
|
}
|
|
#endif
|
|
} else {
|
|
v = state[unitnum].device_func->read (unitnum, data, block, size);
|
|
}
|
|
freesem (unitnum);
|
|
return v;
|
|
}
|
|
int sys_command_cd_rawread (int unitnum, uae_u8 *data, int block, int size, int sectorsize)
|
|
{
|
|
int v;
|
|
if (failunit (unitnum))
|
|
return -1;
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
if (state[unitnum].device_func->rawread == NULL) {
|
|
uae_u8 cmd[12] = { 0xbe, 0, (uae_u8)(block >> 24), (uae_u8)(block >> 16), (uae_u8)(block >> 8), (uae_u8)(block >> 0), (uae_u8)(size >> 16), (uae_u8)(size >> 8), (uae_u8)(size >> 0), 0x10, 0, 0 };
|
|
v = do_scsi (unitnum, cmd, sizeof cmd, data, size * sectorsize);
|
|
} else {
|
|
v = state[unitnum].device_func->rawread (unitnum, data, block, size, sectorsize, 0xffffffff);
|
|
}
|
|
freesem (unitnum);
|
|
return v;
|
|
}
|
|
int sys_command_cd_rawread (int unitnum, uae_u8 *data, int block, int size, int sectorsize, uae_u8 sectortype, uae_u8 scsicmd9, uae_u8 subs)
|
|
{
|
|
int v;
|
|
if (failunit (unitnum))
|
|
return -1;
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
if (state[unitnum].device_func->rawread == NULL) {
|
|
uae_u8 cmd[12] = { 0xbe, 0, (uae_u8)(block >> 24), (uae_u8)(block >> 16), (uae_u8)(block >> 8), (uae_u8)(block >> 0), (uae_u8)(size >> 16), (uae_u8)(size >> 8), (uae_u8)(size >> 0), 0x10, 0, 0 };
|
|
v = do_scsi (unitnum, cmd, sizeof cmd, data, size * sectorsize);
|
|
} else {
|
|
v = state[unitnum].device_func->rawread (unitnum, data, block, size, sectorsize, (sectortype << 16) | (scsicmd9 << 8) | subs);
|
|
}
|
|
freesem (unitnum);
|
|
return v;
|
|
}
|
|
|
|
/* read block */
|
|
int sys_command_read (int unitnum, uae_u8 *data, int block, int size)
|
|
{
|
|
int v;
|
|
if (failunit (unitnum))
|
|
return 0;
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
if (state[unitnum].device_func->read == NULL) {
|
|
uae_u8 cmd[12] = { 0xa8, 0, 0, 0, 0, 0, (uae_u8)(size >> 24), (uae_u8)(size >> 16), (uae_u8)(size >> 8), (uae_u8)(size >> 0), 0, 0 };
|
|
cmd[2] = (uae_u8)(block >> 24);
|
|
cmd[3] = (uae_u8)(block >> 16);
|
|
cmd[4] = (uae_u8)(block >> 8);
|
|
cmd[5] = (uae_u8)(block >> 0);
|
|
v = do_scsi (unitnum, cmd, sizeof cmd, data, size * 2048);
|
|
} else {
|
|
v = state[unitnum].device_func->read (unitnum, data, block, size);
|
|
}
|
|
freesem (unitnum);
|
|
return v;
|
|
}
|
|
|
|
/* write block */
|
|
int sys_command_write (int unitnum, uae_u8 *data, int offset, int size)
|
|
{
|
|
int v;
|
|
if (failunit (unitnum))
|
|
return 0;
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
if (state[unitnum].device_func->write == NULL) {
|
|
v = 0;
|
|
} else {
|
|
v = state[unitnum].device_func->write (unitnum, data, offset, size);
|
|
}
|
|
freesem (unitnum);
|
|
return v;
|
|
}
|
|
|
|
int sys_command_ismedia (int unitnum, int quick)
|
|
{
|
|
int v;
|
|
struct blkdevstate *st = &state[unitnum];
|
|
if (failunit (unitnum))
|
|
return -1;
|
|
if (st->delayed)
|
|
return 0;
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
if (state[unitnum].device_func->ismedia == NULL) {
|
|
uae_u8 cmd[6] = { 0, 0, 0, 0, 0, 0 };
|
|
v = do_scsi (unitnum, cmd, sizeof cmd);
|
|
} else {
|
|
v = state[unitnum].device_func->ismedia (unitnum, quick);
|
|
}
|
|
freesem (unitnum);
|
|
return v;
|
|
}
|
|
|
|
struct device_info *sys_command_info_session (int unitnum, struct device_info *di, int quick, int session)
|
|
{
|
|
struct blkdevstate *st = &state[unitnum];
|
|
if (failunit (unitnum))
|
|
return NULL;
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
if (st->device_func->info == NULL)
|
|
return 0;
|
|
struct device_info *di2 = st->device_func->info (unitnum, di, quick, -1);
|
|
if (di2)
|
|
st->type = di2->type;
|
|
if (di2 && st->delayed)
|
|
di2->media_inserted = 0;
|
|
freesem (unitnum);
|
|
return di2;
|
|
}
|
|
struct device_info *sys_command_info (int unitnum, struct device_info *di, int quick)
|
|
{
|
|
struct device_info *dix;
|
|
|
|
dix = sys_command_info_session (unitnum, di, quick, -1);
|
|
if (dix && dix->media_inserted && !quick && !dix->audio_playing) {
|
|
TCHAR *name = NULL;
|
|
uae_u8 buf[2048];
|
|
if (sys_command_cd_read(unitnum, buf, 16, 1)) {
|
|
if ((buf[0] == 1 || buf[0] == 2) && !memcmp(buf + 1, "CD001", 5)) {
|
|
TCHAR *p;
|
|
au_copy(dix->volume_id, 32, (uae_char*)buf + 40);
|
|
au_copy(dix->system_id, 32, (uae_char*)buf + 8);
|
|
p = dix->volume_id + _tcslen(dix->volume_id) - 1;
|
|
while (p > dix->volume_id && *p == ' ')
|
|
*p-- = 0;
|
|
p = dix->system_id + _tcslen(dix->system_id) - 1;
|
|
while (p > dix->system_id && *p == ' ')
|
|
*p-- = 0;
|
|
}
|
|
}
|
|
}
|
|
return dix;
|
|
}
|
|
|
|
#define MODE_SELECT_6 0x15
|
|
#define MODE_SENSE_6 0x1a
|
|
#define MODE_SELECT_10 0x55
|
|
#define MODE_SENSE_10 0x5a
|
|
|
|
void scsi_atapi_fixup_pre (uae_u8 *scsi_cmd, int *len, uae_u8 **datap, int *datalenp, int *parm)
|
|
{
|
|
uae_u8 cmd, *p, *data = *datap;
|
|
int l, datalen = *datalenp;
|
|
|
|
*parm = 0;
|
|
cmd = scsi_cmd[0];
|
|
if (cmd != MODE_SELECT_6 && cmd != MODE_SENSE_6)
|
|
return;
|
|
l = scsi_cmd[4];
|
|
if (l > 4)
|
|
l += 4;
|
|
scsi_cmd[7] = l >> 8;
|
|
scsi_cmd[8] = l;
|
|
if (cmd == MODE_SELECT_6) {
|
|
scsi_cmd[0] = MODE_SELECT_10;
|
|
scsi_cmd[9] = scsi_cmd[5];
|
|
scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = scsi_cmd[6] = 0;
|
|
*len = 10;
|
|
p = xmalloc (uae_u8, 8 + datalen + 4);
|
|
if (datalen > 4)
|
|
memcpy (p + 8, data + 4, datalen - 4);
|
|
p[0] = 0;
|
|
p[1] = data[0];
|
|
p[2] = data[1];
|
|
p[3] = data[2];
|
|
p[4] = p[5] = p[6] = 0;
|
|
p[7] = data[3];
|
|
if (l > 8)
|
|
datalen += 4;
|
|
*parm = MODE_SELECT_10;
|
|
*datap = p;
|
|
} else {
|
|
scsi_cmd[0] = MODE_SENSE_10;
|
|
scsi_cmd[9] = scsi_cmd[5];
|
|
scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = scsi_cmd[6] = 0;
|
|
if (l > 8)
|
|
datalen += 4;
|
|
*datap = xmalloc (uae_u8, datalen);
|
|
*len = 10;
|
|
*parm = MODE_SENSE_10;
|
|
}
|
|
*datalenp = datalen;
|
|
}
|
|
|
|
void scsi_atapi_fixup_post (uae_u8 *scsi_cmd, int len, uae_u8 *olddata, uae_u8 *data, int *datalenp, int parm)
|
|
{
|
|
int datalen = *datalenp;
|
|
if (!data || !datalen)
|
|
return;
|
|
if (parm == MODE_SENSE_10) {
|
|
olddata[0] = data[1];
|
|
olddata[1] = data[2];
|
|
olddata[2] = data[3];
|
|
olddata[3] = data[7];
|
|
datalen -= 4;
|
|
if (datalen > 4)
|
|
memcpy (olddata + 4, data + 8, datalen - 4);
|
|
*datalenp = datalen;
|
|
}
|
|
}
|
|
|
|
static void scsi_atapi_fixup_inquiry (struct amigascsi *as)
|
|
{
|
|
uae_u8 *scsi_data = as->data;
|
|
uae_u32 scsi_len = as->len;
|
|
uae_u8 *scsi_cmd = as->cmd;
|
|
uae_u8 cmd;
|
|
|
|
cmd = scsi_cmd[0];
|
|
/* CDROM INQUIRY: most Amiga programs expect ANSI version == 2
|
|
* (ATAPI normally responds with zero)
|
|
*/
|
|
if (cmd == 0x12 && scsi_len > 2 && scsi_data) {
|
|
uae_u8 per = scsi_data[0];
|
|
uae_u8 b = scsi_data[2];
|
|
/* CDROM and ANSI version == 0 ? */
|
|
if ((per & 31) == 5 && (b & 7) == 0) {
|
|
b |= 2;
|
|
scsi_data[2] = b;
|
|
}
|
|
}
|
|
}
|
|
|
|
void scsi_log_before (uae_u8 *cdb, int cdblen, uae_u8 *data, int datalen)
|
|
{
|
|
int i;
|
|
for (i = 0; i < cdblen; i++) {
|
|
write_log (_T("%s%02X"), i > 0 ? _T(".") : _T(""), cdb[i]);
|
|
}
|
|
write_log (_T("\n"));
|
|
if (data) {
|
|
write_log (_T("DATAOUT: %d\n"), datalen);
|
|
for (i = 0; i < datalen && i < 100; i++)
|
|
write_log (_T("%s%02X"), i > 0 ? _T(".") : _T(""), data[i]);
|
|
if (datalen > 0)
|
|
write_log (_T("\n"));
|
|
}
|
|
}
|
|
|
|
void scsi_log_after (uae_u8 *data, int datalen, uae_u8 *sense, int senselen)
|
|
{
|
|
int i;
|
|
write_log (_T("DATAIN: %d\n"), datalen);
|
|
for (i = 0; i < datalen && i < 100 && data; i++)
|
|
write_log (_T("%s%02X"), i > 0 ? _T(".") : _T(""), data[i]);
|
|
if (data && datalen > 0)
|
|
write_log (_T("\n"));
|
|
if (senselen > 0) {
|
|
write_log (_T("SENSE: %d,"), senselen);
|
|
for (i = 0; i < senselen && i < 32; i++) {
|
|
write_log (_T("%s%02X"), i > 0 ? _T(".") : _T(""), sense[i]);
|
|
}
|
|
write_log (_T("\n"));
|
|
}
|
|
}
|
|
|
|
static bool nodisk (struct device_info *di)
|
|
{
|
|
return di->media_inserted == 0;
|
|
}
|
|
static int cmd_readx (int unitnum, uae_u8 *dataptr, int offset, int len)
|
|
{
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
int v = state[unitnum].device_func->read (unitnum, dataptr, offset, len);
|
|
freesem (unitnum);
|
|
if (v >= 0)
|
|
return len;
|
|
return v;
|
|
}
|
|
|
|
static void wl (uae_u8 *p, int v)
|
|
{
|
|
p[0] = v >> 24;
|
|
p[1] = v >> 16;
|
|
p[2] = v >> 8;
|
|
p[3] = v;
|
|
}
|
|
static void ww (uae_u8 *p, int v)
|
|
{
|
|
p[0] = v >> 8;
|
|
p[1] = v;
|
|
}
|
|
static int rl (uae_u8 *p)
|
|
{
|
|
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]);
|
|
}
|
|
static int rw (uae_u8 *p)
|
|
{
|
|
return (p[0] << 8) | (p[1]);
|
|
}
|
|
|
|
static void stopplay (int unitnum)
|
|
{
|
|
sys_command_cd_stop (unitnum);
|
|
}
|
|
|
|
static int addtocentry (uae_u8 **dstp, int *len, int point, int newpoint, int msf, uae_u8 *head, struct cd_toc_head *th)
|
|
{
|
|
uae_u8 *dst = *dstp;
|
|
|
|
for (int i = 0; i < th->points; i++) {
|
|
struct cd_toc *t = &th->toc[i];
|
|
if (t->point == point) {
|
|
if (*len < 8)
|
|
return 0;
|
|
int addr = t->paddress;
|
|
if (msf)
|
|
addr = lsn2msf (addr);
|
|
dst[0] = 0;
|
|
dst[1] = (t->adr << 4) | t->control;
|
|
dst[2] = newpoint >= 0 ? newpoint : point;
|
|
dst[3] = 0;
|
|
dst[4] = addr >> 24;
|
|
dst[5] = addr >> 16;
|
|
dst[6] = addr >> 8;
|
|
dst[7] = addr >> 0;
|
|
|
|
if (point >= 1 && point <= 99) {
|
|
if (head[2] == 0)
|
|
head[2] = point;
|
|
head[3] = point;
|
|
}
|
|
|
|
*len -= 8;
|
|
*dstp = dst + 8;
|
|
return 1;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int scsiemudrv (int unitnum, uae_u8 *cmd)
|
|
{
|
|
if (failunit (unitnum))
|
|
return -1;
|
|
if (!getsem (unitnum))
|
|
return 0;
|
|
int v = 0;
|
|
if (state[unitnum].device_func->scsiemu)
|
|
v = state[unitnum].device_func->scsiemu (unitnum, cmd);
|
|
freesem (unitnum);
|
|
return v;
|
|
}
|
|
|
|
static int scsi_read_cd_da(int unitnum, uae_u8 *cmd, uae_u8 *data, struct device_info *di)
|
|
{
|
|
struct blkdevstate *st = &state[unitnum];
|
|
int msf = cmd[0] == 0xd9;
|
|
int start = msf ? msf2lsn(rl(cmd + 2) & 0x00ffffff) : rl(cmd + 2);
|
|
int len = rl(cmd + 6) & 0x00ffffff;
|
|
int sectorsize;
|
|
uae_u8 subcode = cmd[10];
|
|
switch (subcode)
|
|
{
|
|
case 0:
|
|
sectorsize = 2352;
|
|
break;
|
|
case 1:
|
|
sectorsize = 2368;
|
|
break;
|
|
case 2:
|
|
sectorsize = 2448;
|
|
break;
|
|
case 3:
|
|
sectorsize = 96;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
if (msf) {
|
|
int end = msf2lsn(len);
|
|
len = end - start;
|
|
if (len < 0)
|
|
return -1;
|
|
}
|
|
if (len == 0)
|
|
return 0;
|
|
int v = sys_command_cd_rawread(unitnum, data, start, len, sectorsize);
|
|
if (v > 0)
|
|
st->current_pos = start + len;
|
|
return v;
|
|
}
|
|
|
|
static int scsi_read_cd(int unitnum, uae_u8 *cmd, uae_u8 *data, struct device_info *di)
|
|
{
|
|
struct blkdevstate *st = &state[unitnum];
|
|
int msf = cmd[0] == 0xb9;
|
|
int start = msf ? msf2lsn (rl (cmd + 2) & 0x00ffffff) : rl (cmd + 2);
|
|
int len = rl (cmd + 5) & 0x00ffffff;
|
|
if (msf) {
|
|
int end = msf2lsn (len);
|
|
len = end - start;
|
|
if (len < 0)
|
|
return -1;
|
|
}
|
|
int subs = cmd[10] & 7;
|
|
if (len == 0)
|
|
return 0;
|
|
int v = sys_command_cd_rawread (unitnum, data, start, len, 0, (cmd[1] >> 2) & 7, cmd[9], subs);
|
|
if (v > 0)
|
|
st->current_pos = start + len;
|
|
return v;
|
|
}
|
|
|
|
static int scsi_read_cd_data (int unitnum, uae_u8 *scsi_data, uae_u32 offset, uae_u32 len, struct device_info *di, int *scsi_len, struct cd_toc *t)
|
|
{
|
|
struct blkdevstate *st = &state[unitnum];
|
|
int end = t[1].paddress;
|
|
|
|
if (len == 0) {
|
|
if (offset >= end)
|
|
return -1;
|
|
*scsi_len = 0;
|
|
return 0;
|
|
} else {
|
|
if (offset >= end)
|
|
return -1;
|
|
int v = cmd_readx (unitnum, scsi_data, offset, len) * di->bytespersector;
|
|
if (v > 0) {
|
|
st->current_pos = offset + len;
|
|
*scsi_len = v;
|
|
return 0;
|
|
}
|
|
return -2;
|
|
}
|
|
}
|
|
|
|
int scsi_cd_emulate (int unitnum, uae_u8 *cmdbuf, int scsi_cmd_len,
|
|
uae_u8 *scsi_data, int *data_len, uae_u8 *r, int *reply_len, uae_u8 *s, int *sense_len, bool atapi)
|
|
{
|
|
struct blkdevstate *st = &state[unitnum];
|
|
uae_u32 len, offset;
|
|
int lr = 0, ls = 0;
|
|
int scsi_len = -1;
|
|
int v;
|
|
int status = 0;
|
|
struct device_info di;
|
|
uae_u8 cmd;
|
|
int dlen, lun;
|
|
|
|
if (cmdbuf == NULL) {
|
|
if (st->mediawaschanged) {
|
|
st->mediawaschanged = false;
|
|
return (0x28 << 8) | (0x00);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
cmd = cmdbuf[0];
|
|
|
|
if (cmd == 0x03) {
|
|
return 0;
|
|
}
|
|
|
|
dlen = *data_len;
|
|
*reply_len = *sense_len = 0;
|
|
|
|
lun = cmdbuf[1] >> 5;
|
|
if (cmdbuf[0] != 0x03 && cmdbuf[0] != 0x12 && lun) {
|
|
status = 2; /* CHECK CONDITION */
|
|
s[0] = 0x70;
|
|
s[2] = 5; /* ILLEGAL REQUEST */
|
|
s[12] = 0x25; /* INVALID LUN */
|
|
ls = 0x12;
|
|
write_log (_T("CD SCSIEMU %d: CMD=%02X LUN=%d ignored\n"), unitnum, cmdbuf[0], lun);
|
|
goto end;
|
|
}
|
|
|
|
sys_command_info (unitnum, &di, 1);
|
|
|
|
switch (cmdbuf[0])
|
|
{
|
|
case 0x00: /* TEST UNIT READY */
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
scsi_len = 0;
|
|
break;
|
|
case 0x1e: /* PREVENT/ALLOW MEDIUM REMOVAL */
|
|
scsi_len = 0;
|
|
break;
|
|
case 0xbd: /* MECHANISM STATUS */
|
|
len = (cmdbuf[8] << 8) | cmdbuf[9];
|
|
if (len > 8)
|
|
len = 8;
|
|
scsi_len = len;
|
|
r[2] = st->current_pos >> 16;
|
|
r[3] = st->current_pos >> 8;
|
|
r[4] = st->current_pos >> 0;
|
|
break;
|
|
case 0x12: /* INQUIRY */
|
|
{
|
|
if ((cmdbuf[1] & 1) || cmdbuf[2] != 0)
|
|
goto err;
|
|
len = cmdbuf[4];
|
|
if (cmdbuf[1] >> 5) {
|
|
r[0] = 0x7f;
|
|
} else {
|
|
r[0] = 5; // CDROM
|
|
}
|
|
r[1] |= 0x80; // removable
|
|
r[2] = 2; /* supports SCSI-2 */
|
|
r[3] = 2; /* response data format */
|
|
if (atapi)
|
|
r[3] |= 3 << 5; // atapi transport version
|
|
r[4] = 32; /* additional length */
|
|
r[7] = 0;
|
|
scsi_len = lr = len < 36 ? len : 36;
|
|
r[2] = 2;
|
|
r[3] = 2;
|
|
char *s = ua (di.vendorid);
|
|
memcpy (r + 8, s, strlen (s));
|
|
xfree (s);
|
|
s = ua (di.productid);
|
|
memcpy (r + 16, s, strlen (s));
|
|
xfree (s);
|
|
s = ua (di.revision);
|
|
memcpy (r + 32, s, strlen (s));
|
|
xfree (s);
|
|
for (int i = 8; i < 36; i++) {
|
|
if (r[i] == 0)
|
|
r[i] = 32;
|
|
}
|
|
}
|
|
break;
|
|
case 0xd8: // READ CD-DA
|
|
case 0xd9: // READ CD-DA MSF
|
|
if (nodisk(&di))
|
|
goto nodisk;
|
|
scsi_len = scsi_read_cd_da(unitnum, cmdbuf, scsi_data, &di);
|
|
if (scsi_len == -2)
|
|
goto wrongtracktype;
|
|
if (scsi_len == -1)
|
|
goto errreq;
|
|
break;
|
|
case 0xbe: // READ CD
|
|
case 0xb9: // READ CD MSF
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
scsi_len = scsi_read_cd(unitnum, cmdbuf, scsi_data, &di);
|
|
if (scsi_len == -2)
|
|
goto wrongtracktype;
|
|
if (scsi_len == -1)
|
|
goto errreq;
|
|
break;
|
|
case 0x55: // MODE SELECT(10)
|
|
case 0x15: // MODE SELECT(6)
|
|
{
|
|
uae_u8 *p;
|
|
bool mode10 = cmdbuf[0] == 0x55;
|
|
p = scsi_data + 4;
|
|
if (mode10)
|
|
p += 4;
|
|
int pcode = p[0] & 0x3f;
|
|
if (pcode == 14) { // CD audio control
|
|
uae_u16 vol_left = (p[9] << 7) | (p[9] >> 1);
|
|
uae_u16 vol_right = (p[11] << 7) | (p[11] >> 1);
|
|
sys_command_cd_volume (unitnum, vol_left, vol_right);
|
|
scsi_len = 0;
|
|
} else {
|
|
goto errreq;
|
|
}
|
|
}
|
|
break;
|
|
case 0x5a: // MODE SENSE(10)
|
|
case 0x1a: /* MODE SENSE(6) */
|
|
{
|
|
uae_u8 *p;
|
|
int maxlen;
|
|
bool pcodeloop = false;
|
|
bool sense10 = cmdbuf[0] == 0x5a;
|
|
int psize, totalsize, bdsize;
|
|
int pc = cmdbuf[2] >> 6;
|
|
int pcode = cmdbuf[2] & 0x3f;
|
|
int dbd = cmdbuf[1] & 8;
|
|
|
|
if (atapi) {
|
|
if (!sense10)
|
|
goto err;
|
|
dbd = 1;
|
|
}
|
|
p = r;
|
|
if (sense10) {
|
|
totalsize = 8 - 2;
|
|
maxlen = (cmdbuf[7] << 8) | cmdbuf[8];
|
|
p[2] = 0;
|
|
p[3] = 0;
|
|
p[4] = 0;
|
|
p[5] = 0;
|
|
p[6] = 0;
|
|
p[7] = 0;
|
|
p += 8;
|
|
} else {
|
|
totalsize = 4 - 1;
|
|
maxlen = cmdbuf[4];
|
|
p[1] = 0;
|
|
p[2] = 0;
|
|
p[3] = 0;
|
|
p += 4;
|
|
}
|
|
bdsize = 0;
|
|
if (!dbd) {
|
|
wl(p + 0, 0);
|
|
wl(p + 4, di.bytespersector);
|
|
bdsize = 8;
|
|
p += bdsize;
|
|
}
|
|
if (pcode == 0x3f) {
|
|
pcode = 1; // page = 0 must be last
|
|
pcodeloop = true;
|
|
}
|
|
for (;;) {
|
|
psize = 0;
|
|
if (pcode == 0) {
|
|
p[0] = 0;
|
|
p[1] = 0;
|
|
p[2] = 0x20;
|
|
p[3] = 0;
|
|
psize = 4;
|
|
} else if (pcode == 14) { // CD audio control
|
|
uae_u32 vol = sys_command_cd_volume (unitnum, 0xffff, 0xffff);
|
|
p[0] = 0x0e;
|
|
p[1] = 0x0e;
|
|
p[2] = 4|1;
|
|
p[3] = 4;
|
|
p[6] = 0;
|
|
p[7] = 75;
|
|
p[8] = 1;
|
|
p[9] = pc == 0 ? (vol >> 7) & 0xff : 0xff;
|
|
p[10] = 2;
|
|
p[11] = pc == 0 ? (vol >> (16 + 7)) & 0xff : 0xff;
|
|
psize = p[1] + 2;
|
|
} else if (pcode == 0x2a) { // cd/dvd capabilities
|
|
p[0] = 0x2a;
|
|
p[1] = 0x18;
|
|
p[2] = 1; // | 0x10 | 0x20; // read: CD-R/DVD-ROM/DVD-R
|
|
p[3] = 0; // write: nothing
|
|
p[4] = 0x40 | 0x20 | 0x10 | 0x01;
|
|
p[5] = 0x08 | 0x04 | 0x02 | 0x01;
|
|
p[6] = (1 << 5) | 0x10; // type = tray, eject supported
|
|
p[7] = 3; // separate channel mute and volume
|
|
p[8] = 2; p[9] = 0;
|
|
p[10] = 0xff; p[11] = 0xff; // number of volume levels
|
|
p[12] = 4; p[13] = 0; // "1M buffer"
|
|
p[14] = 2; p[15] = 0;
|
|
p[16] = 0;
|
|
p[17] = 0;
|
|
p[18] = p[19] = 0;
|
|
p[20] = p[21] = 0;
|
|
p[22] = p[23] = 0;
|
|
psize = p[1] + 2;
|
|
} else {
|
|
if (!pcodeloop)
|
|
goto err;
|
|
}
|
|
totalsize += psize;
|
|
p += psize;
|
|
if (!pcodeloop)
|
|
break;
|
|
if (pcode == 0)
|
|
break;
|
|
pcode++;
|
|
if (pcode == 0x3f)
|
|
pcode = 0;
|
|
}
|
|
if (sense10) {
|
|
totalsize += bdsize;
|
|
r[6] = bdsize >> 8;
|
|
r[7] = bdsize & 0xff;
|
|
r[0] = totalsize >> 8;
|
|
r[1] = totalsize & 0xff;
|
|
} else {
|
|
totalsize += bdsize;
|
|
r[3] = bdsize & 0xff;
|
|
r[0] = totalsize & 0xff;
|
|
}
|
|
scsi_len = totalsize + 1;
|
|
if (scsi_len > maxlen)
|
|
scsi_len = maxlen;
|
|
lr = scsi_len;
|
|
}
|
|
break;
|
|
case 0x01: /* REZERO UNIT */
|
|
scsi_len = 0;
|
|
break;
|
|
case 0x1d: /* SEND DIAGNOSTICS */
|
|
scsi_len = 0;
|
|
break;
|
|
case 0x25: /* READ CAPACITY */
|
|
{
|
|
int pmi = cmdbuf[8] & 1;
|
|
uae_u32 lba = (cmdbuf[2] << 24) | (cmdbuf[3] << 16) | (cmdbuf[4] << 8) | cmdbuf[5];
|
|
int cyl, cylsec, head, tracksec;
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
uae_u32 blocks = di.sectorspertrack * di.cylinders * di.trackspercylinder - 1;
|
|
cyl = di.cylinders;
|
|
head = 1;
|
|
cylsec = tracksec = di.trackspercylinder;
|
|
if (pmi == 0 && lba != 0)
|
|
goto errreq;
|
|
if (pmi) {
|
|
lba += tracksec * head;
|
|
lba /= tracksec * head;
|
|
lba *= tracksec * head;
|
|
if (lba > blocks)
|
|
lba = blocks;
|
|
blocks = lba;
|
|
}
|
|
wl (r, blocks);
|
|
wl (r + 4, di.bytespersector);
|
|
scsi_len = lr = 8;
|
|
}
|
|
break;
|
|
case 0x0b: /* SEEK (6) */
|
|
{
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
stopplay (unitnum);
|
|
offset = ((cmdbuf[1] & 31) << 16) | (cmdbuf[2] << 8) | cmdbuf[3];
|
|
struct cd_toc *t = gettoc (unitnum, &di.toc, offset);
|
|
if (!t)
|
|
goto readerr;
|
|
v = scsi_read_cd_data (unitnum, scsi_data, offset, 0, &di, &scsi_len, t);
|
|
if (v == -1)
|
|
goto outofbounds;
|
|
}
|
|
break;
|
|
case 0x08: /* READ (6) */
|
|
{
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
stopplay (unitnum);
|
|
offset = ((cmdbuf[1] & 31) << 16) | (cmdbuf[2] << 8) | cmdbuf[3];
|
|
struct cd_toc *t = gettoc (unitnum, &di.toc, offset);
|
|
if (!t)
|
|
goto readerr;
|
|
if ((t->control & 0x0c) == 0x04) {
|
|
len = cmdbuf[4];
|
|
if (!len)
|
|
len = 256;
|
|
v = scsi_read_cd_data (unitnum, scsi_data, offset, len, &di, &scsi_len, t);
|
|
if (v == -1)
|
|
goto outofbounds;
|
|
if (v == -2)
|
|
goto readerr;
|
|
} else {
|
|
goto wrongtracktype;
|
|
}
|
|
}
|
|
break;
|
|
case 0x0a: /* WRITE (6) */
|
|
goto readprot;
|
|
case 0x2b: /* SEEK (10) */
|
|
{
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
stopplay (unitnum);
|
|
offset = rl (cmdbuf + 2);
|
|
struct cd_toc *t = gettoc (unitnum, &di.toc, offset);
|
|
if (!t)
|
|
goto readerr;
|
|
v = scsi_read_cd_data (unitnum, scsi_data, offset, 0, &di, &scsi_len, t);
|
|
if (v == -1)
|
|
goto outofbounds;
|
|
}
|
|
break;
|
|
case 0x28: /* READ (10) */
|
|
{
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
stopplay (unitnum);
|
|
offset = rl (cmdbuf + 2);
|
|
struct cd_toc *t = gettoc (unitnum, &di.toc, offset);
|
|
if (!t)
|
|
goto readerr;
|
|
if ((t->control & 0x0c) == 0x04) {
|
|
len = rl (cmdbuf + 7 - 2) & 0xffff;
|
|
v = scsi_read_cd_data (unitnum, scsi_data, offset, len, &di, &scsi_len, t);
|
|
if (v == -1)
|
|
goto outofbounds;
|
|
if (v == -2)
|
|
goto readerr;
|
|
} else {
|
|
goto wrongtracktype;
|
|
}
|
|
}
|
|
break;
|
|
case 0x2a: /* WRITE (10) */
|
|
goto readprot;
|
|
case 0xa8: /* READ (12) */
|
|
{
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
stopplay (unitnum);
|
|
offset = rl (cmdbuf + 2);
|
|
struct cd_toc *t = gettoc (unitnum, &di.toc, offset);
|
|
if (!t)
|
|
goto readerr;
|
|
if ((t->control & 0x0c) == 0x04) {
|
|
len = rl (cmdbuf + 6);
|
|
v = scsi_read_cd_data (unitnum, scsi_data, offset, len, &di, &scsi_len, t);
|
|
if (v == -1)
|
|
goto outofbounds;
|
|
if (v == -2)
|
|
goto readerr;
|
|
} else {
|
|
goto wrongtracktype;
|
|
}
|
|
}
|
|
break;
|
|
case 0xaa: /* WRITE (12) */
|
|
goto readprot;
|
|
case 0x51: /* READ DISC INFORMATION */
|
|
{
|
|
struct cd_toc_head ttoc;
|
|
int maxlen = (cmdbuf[7] << 8) | cmdbuf[8];
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
if (!sys_command_cd_toc (unitnum, &ttoc))
|
|
goto readerr;
|
|
struct cd_toc_head *toc = &ttoc;
|
|
uae_u8 *p = scsi_data;
|
|
p[0] = 0;
|
|
p[1] = 34 - 2;
|
|
p[2] = 2 | (3 << 2); // complete cd rom, last session is complete
|
|
p[3] = toc->first_track;
|
|
p[4] = 1;
|
|
p[5] = toc->first_track;
|
|
p[6] = toc->last_track;
|
|
wl (p + 16, lsn2msf (toc->lastaddress));
|
|
wl (p + 20, 0x00ffffff);
|
|
scsi_len = p[1] + 2;
|
|
if (scsi_len > maxlen)
|
|
scsi_len = maxlen;
|
|
}
|
|
break;
|
|
case 0x52: /* READ TRACK INFORMATION */
|
|
{
|
|
struct cd_toc_head ttoc;
|
|
int maxlen = (cmdbuf[7] << 8) | cmdbuf[8];
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
if (!sys_command_cd_toc (unitnum, &ttoc))
|
|
goto readerr;
|
|
struct cd_toc_head *toc = &ttoc;
|
|
uae_u8 *p = scsi_data;
|
|
int lsn;
|
|
if (cmdbuf[1] & 1) {
|
|
int track = cmdbuf[5];
|
|
lsn = toc->toc[track].address;
|
|
} else {
|
|
lsn = rl (p + 2);
|
|
}
|
|
struct cd_toc *t = gettoc (unitnum, toc, lsn);
|
|
if (!t)
|
|
goto readerr;
|
|
p[0] = 0;
|
|
p[1] = 28 - 2;
|
|
p[2] = t->track;
|
|
p[3] = 1;
|
|
p[5] = t->control;
|
|
p[6] = 0; // data mode, fixme
|
|
wl (p + 8, t->address);
|
|
wl (p + 24, t[1].address - t->address);
|
|
scsi_len = p[1] + 2;
|
|
if (scsi_len > maxlen)
|
|
scsi_len = maxlen;
|
|
}
|
|
break;
|
|
case 0x43: // READ TOC
|
|
{
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
uae_u8 *p = scsi_data;
|
|
int strack = cmdbuf[6];
|
|
int msf = cmdbuf[1] & 2;
|
|
int format = cmdbuf[2] & 7;
|
|
if (format >= 3)
|
|
goto errreq;
|
|
int maxlen = (cmdbuf[7] << 8) | cmdbuf[8];
|
|
int maxlen2 = maxlen;
|
|
struct cd_toc_head ttoc;
|
|
if (!sys_command_cd_toc (unitnum, &ttoc))
|
|
goto readerr;
|
|
struct cd_toc_head *toc = &ttoc;
|
|
if (format == 1) {
|
|
p[0] = 0;
|
|
p[1] = 2 + 8;
|
|
p[2] = 1;
|
|
p[3] = 1;
|
|
p[4] = 0;
|
|
p[5] = (toc->toc[0].adr << 4) | toc->toc[0].control;
|
|
p[6] = toc->first_track;
|
|
p[7] = 0;
|
|
if (msf)
|
|
wl (p + 8, lsn2msf (toc->toc[0].address));
|
|
else
|
|
wl (p + 8 , toc->toc[0].address);
|
|
scsi_len = 12;
|
|
} else if (format == 2 || format == 0) {
|
|
if (format == 2 && !msf)
|
|
goto errreq;
|
|
if (strack == 0)
|
|
strack = toc->first_track;
|
|
if (format == 0 && strack >= 100 && strack != 0xaa)
|
|
goto errreq;
|
|
uae_u8 *p2 = p + 4;
|
|
p[2] = 0;
|
|
p[3] = 0;
|
|
maxlen -= 4;
|
|
if (format == 2) {
|
|
if (!addtocentry (&p2, &maxlen, 0xa0, -1, msf, p, toc))
|
|
break;
|
|
if (!addtocentry (&p2, &maxlen, 0xa1, -1, msf, p, toc))
|
|
break;
|
|
if (!addtocentry (&p2, &maxlen, 0xa2, -1, msf, p, toc))
|
|
break;
|
|
}
|
|
while (strack < 100) {
|
|
if (!addtocentry (&p2, &maxlen, strack, -1, msf, p, toc))
|
|
break;
|
|
strack++;
|
|
}
|
|
addtocentry (&p2, &maxlen, 0xa2, 0xaa, msf, p, toc);
|
|
int tlen = p2 - (p + 2);
|
|
p[0] = tlen >> 8;
|
|
p[1] = tlen >> 0;
|
|
scsi_len = tlen + 2;
|
|
}
|
|
if (scsi_len > maxlen2)
|
|
scsi_len = maxlen2;
|
|
}
|
|
break;
|
|
case 0x42: // READ SUB-CHANNEL
|
|
{
|
|
int msf = cmdbuf[1] & 2;
|
|
int subq = cmdbuf[2] & 0x40;
|
|
int format = cmdbuf[3];
|
|
int track = cmdbuf[6];
|
|
int maxlen = rw(cmdbuf + 7);
|
|
uae_u8 buf[SUBQ_SIZE] = { 0 };
|
|
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
sys_command_cd_qcode (unitnum, buf, -1, false);
|
|
scsi_len = 4;
|
|
scsi_data[0] = 0;
|
|
scsi_data[1] = buf[1];
|
|
if (subq && format == 1) {
|
|
scsi_data[2] = 0;
|
|
scsi_data[3] = 12;
|
|
scsi_len += 12;
|
|
scsi_data[4] = 1;
|
|
scsi_data[5] = (buf[4 + 0] << 4) | (buf[4 + 0] >> 4);
|
|
scsi_data[6] = frombcd (buf[4 + 1]); // track
|
|
scsi_data[7] = frombcd (buf[4 + 2]); // index
|
|
int reladdr = fromlongbcd (&buf[4 + 3]);
|
|
int absaddr = fromlongbcd (&buf[4 + 7]);
|
|
if (!msf) {
|
|
reladdr = msf2lsn (reladdr);
|
|
absaddr = msf2lsn (absaddr);
|
|
}
|
|
wl (scsi_data + 8, absaddr);
|
|
wl (scsi_data + 12, reladdr);
|
|
} else {
|
|
scsi_data[2] = 0;
|
|
scsi_data[3] = 0;
|
|
}
|
|
if (scsi_len > maxlen)
|
|
scsi_len = maxlen;
|
|
}
|
|
break;
|
|
case 0x1b: // START/STOP
|
|
sys_command_cd_stop (unitnum);
|
|
scsiemudrv (unitnum, cmdbuf);
|
|
scsi_len = 0;
|
|
break;
|
|
case 0x4e: // STOP PLAY/SCAN
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
sys_command_cd_stop (unitnum);
|
|
scsi_len = 0;
|
|
break;
|
|
case 0xba: // SCAN
|
|
{
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
struct cd_toc_head ttoc;
|
|
if (!sys_command_cd_toc (unitnum, &ttoc))
|
|
goto readerr;
|
|
struct cd_toc_head *toc = &ttoc;
|
|
int scan = (cmdbuf[1] & 0x10) ? -1 : 1;
|
|
int start = rl (cmdbuf + 1) & 0x00ffffff;
|
|
int end = scan > 0 ? toc->lastaddress : toc->toc[toc->first_track_offset].paddress;
|
|
int type = cmdbuf[9] >> 6;
|
|
if (type == 1)
|
|
start = lsn2msf (start);
|
|
if (type == 3)
|
|
goto errreq;
|
|
if (type == 2) {
|
|
if (toc->first_track_offset + start >= toc->last_track_offset)
|
|
goto errreq;
|
|
start = toc->toc[toc->first_track_offset + start].paddress;
|
|
}
|
|
sys_command_cd_pause (unitnum, 0);
|
|
sys_command_cd_play (unitnum, start, end, scan);
|
|
scsi_len = 0;
|
|
}
|
|
break;
|
|
case 0x48: // PLAY AUDIO TRACK/INDEX
|
|
{
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
int strack = cmdbuf[4];
|
|
int etrack = cmdbuf[7];
|
|
struct cd_toc_head ttoc;
|
|
if (!sys_command_cd_toc (unitnum, &ttoc))
|
|
goto readerr;
|
|
struct cd_toc_head *toc = &ttoc;
|
|
if (strack < toc->first_track || strack > toc->last_track ||
|
|
etrack < toc->first_track || etrack > toc->last_track ||
|
|
strack > etrack)
|
|
goto errreq;
|
|
int start = toc->toc[toc->first_track_offset + strack - 1].paddress;
|
|
int end = etrack == toc->last_track ? toc->lastaddress : toc->toc[toc->first_track_offset + etrack - 1 + 1].paddress;
|
|
sys_command_cd_pause (unitnum, 0);
|
|
if (!sys_command_cd_play (unitnum, start, end, 0))
|
|
goto wrongtracktype;
|
|
scsi_len = 0;
|
|
}
|
|
break;
|
|
case 0x49: // PLAY AUDIO TRACK RELATIVE (10)
|
|
case 0xa9: // PLAY AUDIO TRACK RELATIVE (12)
|
|
{
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
int len = cmd == 0xa9 ? rl (cmdbuf + 6) : rw (cmdbuf + 7);
|
|
int track = cmd == 0xa9 ? cmdbuf[10] : cmdbuf[6];
|
|
if (track < di.toc.first_track || track > di.toc.last_track)
|
|
goto errreq;
|
|
int start = di.toc.toc[di.toc.first_track_offset + track - 1].paddress;
|
|
int rel = rl (cmdbuf + 2);
|
|
start += rel;
|
|
int end = start + len;
|
|
if (end > di.toc.lastaddress)
|
|
end = di.toc.lastaddress;
|
|
if (len > 0) {
|
|
sys_command_cd_pause (unitnum, 0);
|
|
if (!sys_command_cd_play (unitnum, start, start + len, 0))
|
|
goto wrongtracktype;
|
|
}
|
|
scsi_len = 0;
|
|
}
|
|
break;
|
|
case 0x47: // PLAY AUDIO MSF
|
|
{
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
int start = rl (cmdbuf + 2) & 0x00ffffff;
|
|
if (start == 0x00ffffff) {
|
|
uae_u8 buf[SUBQ_SIZE] = { 0 };
|
|
sys_command_cd_qcode (unitnum, buf, -1, false);
|
|
start = fromlongbcd (buf + 4 + 7);
|
|
}
|
|
int end = msf2lsn (rl (cmdbuf + 5) & 0x00ffffff);
|
|
if (end > di.toc.lastaddress)
|
|
end = di.toc.lastaddress;
|
|
start = msf2lsn (start);
|
|
if (start > end)
|
|
goto errreq;
|
|
if (start < end)
|
|
sys_command_cd_pause (unitnum, 0);
|
|
if (!sys_command_cd_play (unitnum, start, end, 0))
|
|
goto wrongtracktype;
|
|
scsi_len = 0;
|
|
}
|
|
break;
|
|
case 0x45: // PLAY AUDIO (10)
|
|
case 0xa5: // PLAY AUDIO (12)
|
|
{
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
int start = rl (cmdbuf + 2);
|
|
int len;
|
|
if (cmd == 0xa5)
|
|
len = rl (cmdbuf + 6);
|
|
else
|
|
len = rw (cmdbuf + 7);
|
|
if (len > 0) {
|
|
if (start == -1) {
|
|
uae_u8 buf[SUBQ_SIZE] = { 0 };
|
|
sys_command_cd_qcode (unitnum, buf, -1, false);
|
|
start = msf2lsn (fromlongbcd (buf + 4 + 7));
|
|
}
|
|
int end = start + len;
|
|
if (end > di.toc.lastaddress)
|
|
end = di.toc.lastaddress;
|
|
sys_command_cd_pause (unitnum, 0);
|
|
if (!sys_command_cd_play (unitnum, start, end, 0))
|
|
goto wrongtracktype;
|
|
}
|
|
scsi_len = 0;
|
|
}
|
|
break;
|
|
case 0xbc: // PLAY CD
|
|
{
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
int start = -1;
|
|
int end = -1;
|
|
if (cmdbuf[1] & 2) {
|
|
start = msf2lsn (rl (cmdbuf + 2) & 0x00ffffff);
|
|
end = msf2lsn (rl (cmdbuf + 5) & 0x00ffffff);
|
|
} else {
|
|
start = rl (cmdbuf + 2);
|
|
end = start + rl (cmdbuf + 6);
|
|
}
|
|
if (end > di.toc.lastaddress)
|
|
end = di.toc.lastaddress;
|
|
if (start > end)
|
|
goto errreq;
|
|
if (start < end) {
|
|
sys_command_cd_pause (unitnum, 0);
|
|
if (!sys_command_cd_play (unitnum, start, end, 0))
|
|
goto wrongtracktype;
|
|
}
|
|
}
|
|
break;
|
|
case 0x4b: // PAUSE/RESUME
|
|
{
|
|
if (nodisk (&di))
|
|
goto nodisk;
|
|
uae_u8 buf[SUBQ_SIZE] = { 0 };
|
|
int resume = cmdbuf[8] & 1;
|
|
sys_command_cd_qcode (unitnum, buf, -1, false);
|
|
if (buf[1] != AUDIO_STATUS_IN_PROGRESS && buf[1] != AUDIO_STATUS_PAUSED)
|
|
goto errreq;
|
|
sys_command_cd_pause (unitnum, resume ? 0 : 1);
|
|
scsi_len = 0;
|
|
}
|
|
break;
|
|
case 0x35: /* SYNCRONIZE CACHE (10) */
|
|
scsi_len = 0;
|
|
break;
|
|
|
|
default:
|
|
err:
|
|
write_log (_T("CD SCSIEMU: unsupported scsi command 0x%02X\n"), cmdbuf[0]);
|
|
readprot:
|
|
status = 2; /* CHECK CONDITION */
|
|
s[0] = 0x70;
|
|
s[2] = 5;
|
|
s[12] = 0x20; /* INVALID COMMAND */
|
|
ls = 0x12;
|
|
break;
|
|
nodisk:
|
|
status = 2; /* CHECK CONDITION */
|
|
s[0] = 0x70;
|
|
s[2] = 2; /* NOT READY */
|
|
s[12] = 0x3A; /* MEDIUM NOT PRESENT */
|
|
ls = 0x12;
|
|
break;
|
|
readerr:
|
|
status = 2; /* CHECK CONDITION */
|
|
s[0] = 0x70;
|
|
s[2] = 2; /* NOT READY */
|
|
s[12] = 0x11; /* UNRECOVERED READ ERROR */
|
|
ls = 0x12;
|
|
break;
|
|
wrongtracktype:
|
|
status = 2;
|
|
s[0] = 0x70;
|
|
s[2] = 5;
|
|
s[12] = 0x64; /* ILLEGAL MODE FOR THIS TRACK */
|
|
ls = 0x12;
|
|
break;
|
|
outofbounds:
|
|
status = 2; /* CHECK CONDITION */
|
|
s[0] = 0x70;
|
|
s[2] = 5; /* ILLEGAL REQUEST */
|
|
s[12] = 0x21; /* LOGICAL BLOCK OUT OF RANGE */
|
|
ls = 0x12;
|
|
break;
|
|
errreq:
|
|
lr = -1;
|
|
status = 2; /* CHECK CONDITION */
|
|
s[0] = 0x70;
|
|
s[2] = 5; /* ILLEGAL REQUEST */
|
|
s[12] = 0x24; /* ILLEGAL FIELD IN CDB */
|
|
ls = 0x12;
|
|
break;
|
|
}
|
|
end:
|
|
*data_len = scsi_len;
|
|
*reply_len = lr;
|
|
*sense_len = ls;
|
|
|
|
if (ls) {
|
|
//s[0] |= 0x80;
|
|
if (ls > 7)
|
|
s[7] = ls - 8; // additional sense length
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static int emulate_cd_audio = 1;
|
|
|
|
int blkdev_is_audio_command(uae_u8 cmd)
|
|
{
|
|
if (!emulate_cd_audio)
|
|
return false;
|
|
// audio cd command?
|
|
switch (cmd)
|
|
{
|
|
case 0x42:
|
|
case 0x45:
|
|
case 0x47:
|
|
case 0x48:
|
|
case 0x49:
|
|
case 0x4b:
|
|
case 0x4e:
|
|
case 0xa5:
|
|
case 0xa9:
|
|
case 0xba:
|
|
case 0xbc:
|
|
case 0xcd:
|
|
return 1;
|
|
}
|
|
|
|
// commands that won't stop cd audio
|
|
switch (cmd)
|
|
{
|
|
case 0x00:
|
|
case 0x03:
|
|
case 0x12:
|
|
case 0x15:
|
|
case 0x1a:
|
|
case 0x1e:
|
|
case 0x25:
|
|
case 0x35:
|
|
case 0x55:
|
|
case 0x5a:
|
|
return 0;
|
|
}
|
|
|
|
// all other commands stop cd audio
|
|
return -1;
|
|
}
|
|
|
|
int blkdev_execute_audio_command(int unitnum, uae_u8 *cdb, int cdblen, uae_u8 *inbuf, int inlen, uae_u8 *sense, int *senselen)
|
|
{
|
|
int len = inlen;
|
|
uae_u8 reply[256];
|
|
int replylen = sizeof(reply);
|
|
int status = scsi_cd_emulate(unitnum, cdb, cdblen, inbuf, &len, reply, &replylen, sense, senselen, true);
|
|
if (status)
|
|
return -1;
|
|
return len;
|
|
}
|
|
|
|
static int execscsicmd_direct (int unitnum, int type, struct amigascsi *as)
|
|
{
|
|
int io_error = 0;
|
|
uae_u8 *scsi_datap, *scsi_datap_org;
|
|
uae_u32 scsi_cmd_len_orig = as->cmd_len;
|
|
uae_u8 cmd[16] = { 0 };
|
|
uae_u8 replydata[256] = { 0 };
|
|
int datalen = as->len;
|
|
int senselen = as->sense_len;
|
|
int replylen = 0;
|
|
|
|
memcpy (cmd, as->cmd, as->cmd_len);
|
|
scsi_datap = scsi_datap_org = as->len ? as->data : 0;
|
|
if (as->sense_len > 32)
|
|
as->sense_len = 32;
|
|
|
|
/* never report media change state if uaescsi.device */
|
|
state[unitnum].mediawaschanged = false;
|
|
|
|
switch (type)
|
|
{
|
|
case INQ_ROMD:
|
|
as->status = scsi_cd_emulate (unitnum, cmd, as->cmd_len, scsi_datap, &datalen, replydata, &replylen, as->sensedata, &senselen, false);
|
|
break;
|
|
case INQ_SEQD:
|
|
//as->status = scsi_tape_emulate (state[unitnum].tape, cmd, as->cmd_len, scsi_datap, &datalen, replydata, &replylen, as->sensedata, &senselen);
|
|
break;
|
|
default:
|
|
as->status = 2;
|
|
break;
|
|
}
|
|
|
|
as->cmdactual = as->status != 0 ? 0 : as->cmd_len; /* fake scsi_CmdActual */
|
|
if (as->status) {
|
|
io_error = IOERR_BadStatus;
|
|
as->sactual = senselen;
|
|
as->actual = 0; /* scsi_Actual */
|
|
} else {
|
|
int i;
|
|
if (replylen > 0) {
|
|
for (int i = 0; i < replylen; i++) {
|
|
scsi_datap[i] = replydata[i];
|
|
}
|
|
datalen = replylen;
|
|
}
|
|
for (i = 0; i < as->sense_len; i++)
|
|
as->sensedata[i] = 0;
|
|
if (datalen < 0) {
|
|
io_error = IOERR_NotSpecified;
|
|
as->actual = 0; /* scsi_Actual */
|
|
} else {
|
|
as->len = datalen;
|
|
io_error = 0;
|
|
as->actual = as->len; /* scsi_Actual */
|
|
}
|
|
}
|
|
|
|
return io_error;
|
|
}
|
|
|
|
int sys_command_scsi_direct_native(int unitnum, int type, struct amigascsi *as)
|
|
{
|
|
struct blkdevstate *st = &state[unitnum];
|
|
if (st->scsiemu || (type >= 0 && st->type != type)) {
|
|
return execscsicmd_direct (unitnum, type, as);
|
|
} else {
|
|
if (!st->device_func->exec_direct)
|
|
return -1;
|
|
}
|
|
int ret = st->device_func->exec_direct (unitnum, as);
|
|
if (!ret && st->device_func->isatapi(unitnum))
|
|
scsi_atapi_fixup_inquiry (as);
|
|
return ret;
|
|
}
|
|
|
|
int sys_command_scsi_direct(TrapContext *ctx, int unitnum, int type, uaecptr acmd)
|
|
{
|
|
int ret;
|
|
struct amigascsi as = { 0 };
|
|
uaecptr ap;
|
|
addrbank *bank;
|
|
uae_u8 scsicmd[30];
|
|
|
|
trap_get_bytes(ctx, scsicmd, acmd, sizeof scsicmd);
|
|
|
|
as.cmd_len = get_word_host(scsicmd + 16);
|
|
if (as.cmd_len > sizeof as.cmd)
|
|
return IOERR_BADLENGTH;
|
|
as.flags = get_byte_host(scsicmd + 20);
|
|
ap = get_long_host(scsicmd + 0);
|
|
as.len = get_long_host(scsicmd + 4);
|
|
|
|
if (trap_is_indirect()) {
|
|
if (!(as.flags & 1)) { // write?
|
|
as.data = xmalloc(uae_u8, as.len);
|
|
trap_get_bytes(ctx, as.data, ap, as.len);
|
|
} else {
|
|
as.data = xcalloc(uae_u8, as.len);
|
|
}
|
|
} else {
|
|
bank = &get_mem_bank (ap);
|
|
if (!bank || !bank->check(ap, as.len))
|
|
return IOERR_BADADDRESS;
|
|
as.data = bank->xlateaddr (ap);
|
|
}
|
|
|
|
ap = get_long_host(scsicmd + 12);
|
|
trap_get_bytes(ctx, as.cmd, ap, as.cmd_len);
|
|
as.sense_len = get_word_host(scsicmd + 26);
|
|
|
|
|
|
ret = sys_command_scsi_direct_native (unitnum, type, &as);
|
|
|
|
put_long_host(scsicmd + 8, as.actual);
|
|
put_word_host(scsicmd + 18, as.cmdactual);
|
|
put_byte_host(scsicmd + 21, as.status);
|
|
put_word_host(scsicmd + 28, as.sactual);
|
|
|
|
if (as.flags & (2 | 4)) { // autosense
|
|
ap = get_long_host(scsicmd + 22);
|
|
if (as.sactual)
|
|
trap_put_bytes(ctx, as.sensedata, ap, as.sactual > as.sense_len ? as.sense_len : as.sactual);
|
|
if (as.sense_len > as.sactual)
|
|
trap_set_bytes(ctx, ap + as.sactual, 0, as.sense_len - as.sactual);
|
|
}
|
|
|
|
trap_put_bytes(ctx, scsicmd, acmd, sizeof scsicmd);
|
|
|
|
if (trap_is_indirect()) {
|
|
if (as.flags & 1) { // read?
|
|
int len = as.actual > as.len ? as.len : as.actual;
|
|
trap_put_bytes(ctx, as.data, get_long_host(scsicmd + 0), len);
|
|
}
|
|
xfree(as.data);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef SAVESTATE
|
|
|
|
void restore_blkdev_start(void)
|
|
{
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
|
|
struct cdslot *cd = &currprefs.cdslots[i];
|
|
if (cd->temporary) {
|
|
memset(cd, 0, sizeof(struct cdslot));
|
|
memset(&changed_prefs.cdslots[i], 0, sizeof(struct cdslot));
|
|
}
|
|
}
|
|
}
|
|
|
|
uae_u8 *save_cd (int num, int *len)
|
|
{
|
|
struct blkdevstate *st = &state[num];
|
|
uae_u8 *dstbak, *dst;
|
|
|
|
memset(st->play_qcode, 0, SUBQ_SIZE);
|
|
if (!currprefs.cdslots[num].inuse || num >= MAX_TOTAL_SCSI_DEVICES)
|
|
return NULL;
|
|
if (!currprefs.cs_cd32cd && !currprefs.cs_cdtvcd && !currprefs.scsi)
|
|
return NULL;
|
|
dstbak = dst = xmalloc (uae_u8, 4 + MAX_DPATH + 4 + 4 + 4 + 2 * MAX_DPATH);
|
|
save_u32 (4 | 8 | 16);
|
|
save_path (currprefs.cdslots[num].name, SAVESTATE_PATH_CD);
|
|
save_u32 (currprefs.cdslots[num].type);
|
|
save_u32 (0);
|
|
save_u32 (0);
|
|
sys_command_cd_qcode (num, st->play_qcode, -1, false);
|
|
for (int i = 0; i < SUBQ_SIZE; i++)
|
|
save_u8 (st->play_qcode[i]);
|
|
save_u32 (st->play_end_pos);
|
|
save_path_full(currprefs.cdslots[num].name, SAVESTATE_PATH_CD);
|
|
*len = dst - dstbak;
|
|
return dstbak;
|
|
}
|
|
|
|
uae_u8 *restore_cd (int num, uae_u8 *src)
|
|
{
|
|
struct blkdevstate *st = &state[num];
|
|
uae_u32 flags;
|
|
TCHAR *s;
|
|
|
|
if (num >= MAX_TOTAL_SCSI_DEVICES)
|
|
return NULL;
|
|
flags = restore_u32 ();
|
|
s = restore_path (SAVESTATE_PATH_CD);
|
|
int type = restore_u32 ();
|
|
restore_u32 ();
|
|
if (flags & 8) {
|
|
restore_u32 ();
|
|
for (int i = 0; i < SUBQ_SIZE; i++)
|
|
st->play_qcode[i] = restore_u8 ();
|
|
st->play_end_pos = restore_u32 ();
|
|
}
|
|
if (flags & 16) {
|
|
xfree(s);
|
|
s = restore_path_full();
|
|
}
|
|
if (flags & 4) {
|
|
if (currprefs.cdslots[num].name[0] == 0 || zfile_exists(s)) {
|
|
_tcscpy(changed_prefs.cdslots[num].name, s);
|
|
_tcscpy(currprefs.cdslots[num].name, s);
|
|
}
|
|
changed_prefs.cdslots[num].type = currprefs.cdslots[num].type = type;
|
|
changed_prefs.cdslots[num].temporary = currprefs.cdslots[num].temporary = true;
|
|
}
|
|
xfree(s);
|
|
return src;
|
|
}
|
|
|
|
#endif
|
|
|