Fixed CD-ROM sector reading from CUE/BIN images with 2048-byte sectors; Fixed existing CD-ROM Mode 2 support and adding support for the remaining variants of Mode 2; The Windows CD-ROM IOCTL code now correctly determines the track type, fixes CD Audio over IOCTL; Applied all PCem sound-related commits; Sound Blaster 16/AWE32 mixer effects now also apply to CD Audio when the Sound Blaster 16 or AWE32 is selected.
1288 lines
36 KiB
C
1288 lines
36 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.
|
|
*
|
|
* Implementation of the CD-ROM host drive IOCTL interface for
|
|
* Windows using SCSI Passthrough Direct.
|
|
*
|
|
* Version: @(#)cdrom_ioctl.c 1.0.11 2018/02/15
|
|
*
|
|
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
|
|
* Miran Grca, <mgrca8@gmail.com>
|
|
*
|
|
* Copyright 2008-2018 Sarah Walker.
|
|
* Copyright 2016-2018 Miran Grca.
|
|
*/
|
|
#define WINVER 0x0600
|
|
#include <windows.h>
|
|
#include <io.h>
|
|
#include <ntddcdrm.h>
|
|
#include <ntddscsi.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
#include "../86box.h"
|
|
#include "../device.h"
|
|
#include "../scsi/scsi.h"
|
|
#include "../cdrom/cdrom.h"
|
|
#include "../plat.h"
|
|
|
|
|
|
#define MSFtoLBA(m,s,f) ((((m*60)+s)*75)+f)
|
|
|
|
|
|
enum {
|
|
CD_STOPPED = 0,
|
|
CD_PLAYING,
|
|
CD_PAUSED
|
|
};
|
|
|
|
|
|
typedef struct {
|
|
HANDLE hIOCTL;
|
|
CDROM_TOC toc;
|
|
int is_playing;
|
|
} cdrom_ioctl_windows_t;
|
|
|
|
|
|
cdrom_ioctl_windows_t cdrom_ioctl_windows[CDROM_NUM];
|
|
|
|
#ifdef ENABLE_CDROM_LOG
|
|
int cdrom_ioctl_do_log = ENABLE_CDROM_LOG;
|
|
#endif
|
|
|
|
|
|
static CDROM ioctl_cdrom;
|
|
|
|
|
|
static void
|
|
cdrom_ioctl_log(const char *format, ...)
|
|
{
|
|
#ifdef ENABLE_CDROM_LOG
|
|
if (cdrom_ioctl_do_log) {
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
vfprintf(stdlog, format, ap);
|
|
va_end(ap);
|
|
fflush(stdlog);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static int ioctl_hopen(uint8_t id);
|
|
|
|
void ioctl_audio_callback(uint8_t id, int16_t *output, int len)
|
|
{
|
|
RAW_READ_INFO in;
|
|
DWORD count;
|
|
|
|
if (!cdrom_drives[id].sound_on || (cdrom_ioctl[id].cd_state != CD_PLAYING))
|
|
{
|
|
if (cdrom_ioctl[id].cd_state == CD_PLAYING)
|
|
{
|
|
cdrom[id].seek_pos += (len >> 11);
|
|
}
|
|
memset(output, 0, len * 2);
|
|
return;
|
|
}
|
|
while (cdrom_ioctl[id].cd_buflen < len)
|
|
{
|
|
if (cdrom[id].seek_pos < cdrom_ioctl[id].cd_end)
|
|
{
|
|
in.DiskOffset.LowPart = (cdrom[id].seek_pos - 150) * 2048;
|
|
in.DiskOffset.HighPart = 0;
|
|
in.SectorCount = 1;
|
|
in.TrackMode = CDDA;
|
|
if (!DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL, IOCTL_CDROM_RAW_READ, &in, sizeof(in), &(cdrom_ioctl[id].cd_buffer[cdrom_ioctl[id].cd_buflen]), 2352, &count, NULL))
|
|
{
|
|
memset(&(cdrom_ioctl[id].cd_buffer[cdrom_ioctl[id].cd_buflen]), 0, (BUF_SIZE - cdrom_ioctl[id].cd_buflen) * 2);
|
|
cdrom_ioctl_windows[id].is_playing = 0;
|
|
ioctl_close(id);
|
|
cdrom_ioctl[id].cd_state = CD_STOPPED;
|
|
cdrom_ioctl[id].cd_buflen = len;
|
|
}
|
|
else
|
|
{
|
|
cdrom[id].seek_pos++;
|
|
cdrom_ioctl[id].cd_buflen += (2352 / 2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memset(&(cdrom_ioctl[id].cd_buffer[cdrom_ioctl[id].cd_buflen]), 0, (BUF_SIZE - cdrom_ioctl[id].cd_buflen) * 2);
|
|
cdrom_ioctl_windows[id].is_playing = 0;
|
|
ioctl_close(id);
|
|
cdrom_ioctl[id].cd_state = CD_STOPPED;
|
|
cdrom_ioctl[id].cd_buflen = len;
|
|
}
|
|
}
|
|
memcpy(output, cdrom_ioctl[id].cd_buffer, len * 2);
|
|
memcpy(&cdrom_ioctl[id].cd_buffer[0], &(cdrom_ioctl[id].cd_buffer[len]), (BUF_SIZE - len) * 2);
|
|
cdrom_ioctl[id].cd_buflen -= len;
|
|
}
|
|
|
|
void ioctl_audio_stop(uint8_t id)
|
|
{
|
|
cdrom_ioctl_windows[id].is_playing = 0;
|
|
ioctl_close(id);
|
|
cdrom_ioctl[id].cd_state = CD_STOPPED;
|
|
}
|
|
|
|
static int get_track_nr(uint8_t id, uint32_t pos)
|
|
{
|
|
int c;
|
|
int track = 0;
|
|
|
|
if (!cdrom_ioctl[id].tocvalid)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (cdrom_ioctl[id].last_track_pos == pos)
|
|
{
|
|
return cdrom_ioctl[id].last_track_nr;
|
|
}
|
|
|
|
for (c = cdrom_ioctl_windows[id].toc.FirstTrack; c < cdrom_ioctl_windows[id].toc.LastTrack; c++)
|
|
{
|
|
uint32_t track_address = cdrom_ioctl_windows[id].toc.TrackData[c].Address[3] +
|
|
(cdrom_ioctl_windows[id].toc.TrackData[c].Address[2] * 75) +
|
|
(cdrom_ioctl_windows[id].toc.TrackData[c].Address[1] * 75 * 60);
|
|
|
|
if (track_address <= pos)
|
|
{
|
|
track = c;
|
|
}
|
|
}
|
|
cdrom_ioctl[id].last_track_pos = pos;
|
|
cdrom_ioctl[id].last_track_nr = track;
|
|
|
|
return track;
|
|
}
|
|
|
|
static uint32_t get_track_msf(uint8_t id, uint32_t track_no)
|
|
{
|
|
int c;
|
|
|
|
if (!cdrom_ioctl[id].tocvalid)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for (c = cdrom_ioctl_windows[id].toc.FirstTrack; c < cdrom_ioctl_windows[id].toc.LastTrack; c++)
|
|
{
|
|
if (c == track_no)
|
|
{
|
|
return cdrom_ioctl_windows[id].toc.TrackData[c].Address[3] + (cdrom_ioctl_windows[id].toc.TrackData[c].Address[2] << 8) + (cdrom_ioctl_windows[id].toc.TrackData[c].Address[1] << 16);
|
|
}
|
|
}
|
|
return 0xffffffff;
|
|
}
|
|
|
|
static void ioctl_playaudio(uint8_t id, uint32_t pos, uint32_t len, int ismsf)
|
|
{
|
|
int m = 0, s = 0, f = 0;
|
|
uint32_t start_msf = 0, end_msf = 0;
|
|
if (!cdrom_drives[id].host_drive)
|
|
{
|
|
return;
|
|
}
|
|
if (ismsf == 2)
|
|
{
|
|
start_msf = get_track_msf(id, pos);
|
|
end_msf = get_track_msf(id, len);
|
|
if (start_msf == 0xffffffff)
|
|
{
|
|
return;
|
|
}
|
|
if (end_msf == 0xffffffff)
|
|
{
|
|
return;
|
|
}
|
|
m = (start_msf >> 16) & 0xff;
|
|
s = (start_msf >> 8) & 0xff;
|
|
f = start_msf & 0xff;
|
|
pos = MSFtoLBA(m, s, f);
|
|
m = (end_msf >> 16) & 0xff;
|
|
s = (end_msf >> 8) & 0xff;
|
|
f = end_msf & 0xff;
|
|
len = MSFtoLBA(m, s, f);
|
|
}
|
|
else if (ismsf == 1)
|
|
{
|
|
m = (pos >> 16) & 0xff;
|
|
s = (pos >> 8) & 0xff;
|
|
f = pos & 0xff;
|
|
|
|
if (pos == 0xffffff)
|
|
{
|
|
cdrom_ioctl_log("Playing from current position (MSF)\n");
|
|
pos = cdrom[id].seek_pos;
|
|
}
|
|
else
|
|
{
|
|
pos = MSFtoLBA(m, s, f);
|
|
}
|
|
|
|
m = (len >> 16) & 0xff;
|
|
s = (len >> 8) & 0xff;
|
|
f = len & 0xff;
|
|
len = MSFtoLBA(m, s, f);
|
|
}
|
|
else if (ismsf == 0)
|
|
{
|
|
if (pos == 0xffffffff)
|
|
{
|
|
cdrom_ioctl_log("Playing from current position\n");
|
|
pos = cdrom[id].seek_pos;
|
|
}
|
|
len += pos;
|
|
}
|
|
cdrom[id].seek_pos = pos;
|
|
cdrom_ioctl[id].cd_end = len;
|
|
if (cdrom[id].seek_pos < 150)
|
|
{
|
|
/* Adjust because the host expects a minimum adjusted LBA of 0 which is equivalent to an absolute LBA of 150. */
|
|
cdrom[id].seek_pos = 150;
|
|
}
|
|
if (!cdrom_ioctl_windows[id].is_playing)
|
|
{
|
|
ioctl_hopen(id);
|
|
cdrom_ioctl_windows[id].is_playing = 1;
|
|
}
|
|
cdrom_ioctl[id].cd_state = CD_PLAYING;
|
|
}
|
|
|
|
static void ioctl_pause(uint8_t id)
|
|
{
|
|
if (!cdrom_drives[id].host_drive)
|
|
{
|
|
return;
|
|
}
|
|
if (cdrom_ioctl[id].cd_state == CD_PLAYING)
|
|
{
|
|
cdrom_ioctl[id].cd_state = CD_PAUSED;
|
|
}
|
|
}
|
|
|
|
static void ioctl_resume(uint8_t id)
|
|
{
|
|
if (!cdrom_drives[id].host_drive)
|
|
{
|
|
return;
|
|
}
|
|
if (cdrom_ioctl[id].cd_state == CD_PAUSED)
|
|
{
|
|
cdrom_ioctl[id].cd_state = CD_PLAYING;
|
|
}
|
|
}
|
|
|
|
static void ioctl_stop(uint8_t id)
|
|
{
|
|
if (!cdrom_drives[id].host_drive)
|
|
{
|
|
return;
|
|
}
|
|
if (cdrom_ioctl_windows[id].is_playing)
|
|
{
|
|
cdrom_ioctl_windows[id].is_playing = 0;
|
|
ioctl_close(id);
|
|
}
|
|
cdrom_ioctl[id].cd_state = CD_STOPPED;
|
|
}
|
|
|
|
static int ioctl_ready(uint8_t id)
|
|
{
|
|
unsigned long size;
|
|
int temp;
|
|
CDROM_TOC ltoc;
|
|
if (!cdrom_drives[id].host_drive)
|
|
{
|
|
return 0;
|
|
}
|
|
if (cdrom_ioctl_windows[id].hIOCTL == NULL)
|
|
{
|
|
ioctl_hopen(id);
|
|
temp = DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL, IOCTL_CDROM_READ_TOC, NULL, 0, <oc, sizeof(ltoc), &size, NULL);
|
|
ioctl_close(id);
|
|
}
|
|
else
|
|
{
|
|
temp = DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL, IOCTL_CDROM_READ_TOC, NULL, 0, <oc, sizeof(ltoc), &size, NULL);
|
|
}
|
|
if (!temp)
|
|
{
|
|
return 0;
|
|
}
|
|
if ((ltoc.TrackData[ltoc.LastTrack].Address[1] != cdrom_ioctl_windows[id].toc.TrackData[cdrom_ioctl_windows[id].toc.LastTrack].Address[1]) ||
|
|
(ltoc.TrackData[ltoc.LastTrack].Address[2] != cdrom_ioctl_windows[id].toc.TrackData[cdrom_ioctl_windows[id].toc.LastTrack].Address[2]) ||
|
|
(ltoc.TrackData[ltoc.LastTrack].Address[3] != cdrom_ioctl_windows[id].toc.TrackData[cdrom_ioctl_windows[id].toc.LastTrack].Address[3]) ||
|
|
!cdrom_ioctl[id].tocvalid || (cdrom_drives[id].host_drive != cdrom_drives[id].prev_host_drive))
|
|
{
|
|
cdrom_ioctl[id].cd_state = CD_STOPPED;
|
|
if (cdrom_drives[id].host_drive != cdrom_drives[id].prev_host_drive)
|
|
{
|
|
cdrom_drives[id].prev_host_drive = cdrom_drives[id].host_drive;
|
|
}
|
|
return 1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int ioctl_get_last_block(uint8_t id, unsigned char starttrack, int msf, int maxlen, int single)
|
|
{
|
|
unsigned long size;
|
|
int c, d = 0;
|
|
CDROM_TOC lbtoc;
|
|
int lb = 0;
|
|
if (!cdrom_drives[id].host_drive)
|
|
{
|
|
return 0;
|
|
}
|
|
cdrom_ioctl[id].cd_state = CD_STOPPED;
|
|
ioctl_hopen(id);
|
|
DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL, IOCTL_CDROM_READ_TOC, NULL, 0, &lbtoc, sizeof(lbtoc), &size, NULL);
|
|
ioctl_close(id);
|
|
cdrom_ioctl[id].tocvalid=1;
|
|
for (c=d; c <= lbtoc.LastTrack; c++)
|
|
{
|
|
uint32_t address;
|
|
address = MSFtoLBA(cdrom_ioctl_windows[id].toc.TrackData[c].Address[1], cdrom_ioctl_windows[id].toc.TrackData[c].Address[2], cdrom_ioctl_windows[id].toc.TrackData[c].Address[3]);
|
|
if (address > lb)
|
|
{
|
|
lb = address;
|
|
}
|
|
}
|
|
return lb;
|
|
}
|
|
|
|
static void ioctl_read_capacity(uint8_t id, uint8_t *b);
|
|
|
|
static int ioctl_medium_changed(uint8_t id)
|
|
{
|
|
unsigned long size;
|
|
int temp;
|
|
CDROM_TOC ltoc;
|
|
if (!cdrom_drives[id].host_drive)
|
|
{
|
|
return 0; /* This will be handled by the not ready handler instead. */
|
|
}
|
|
ioctl_hopen(id);
|
|
temp = DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL, IOCTL_CDROM_READ_TOC, NULL, 0, <oc,sizeof(ltoc), &size, NULL);
|
|
ioctl_close(id);
|
|
if (!temp)
|
|
{
|
|
return 0; /* Drive empty, a not ready handler matter, not disc change. */
|
|
}
|
|
if (!cdrom_ioctl[id].tocvalid || (cdrom_drives[id].host_drive != cdrom_drives[id].prev_host_drive))
|
|
{
|
|
cdrom_ioctl[id].cd_state = CD_STOPPED;
|
|
cdrom_ioctl_windows[id].toc = ltoc;
|
|
cdrom_ioctl[id].tocvalid = 1;
|
|
if (cdrom_drives[id].host_drive != cdrom_drives[id].prev_host_drive)
|
|
{
|
|
cdrom_drives[id].prev_host_drive = cdrom_drives[id].host_drive;
|
|
}
|
|
ioctl_hopen(id);
|
|
cdrom_ioctl[id].capacity_read=0; /* With this two lines, we read the READ CAPACITY command output from the host drive into our cache buffer. */
|
|
ioctl_read_capacity(id, NULL);
|
|
ioctl_close(id);
|
|
cdrom_ioctl[id].cdrom_capacity = ioctl_get_last_block(id, 0, 0, 4096, 0);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
if ((ltoc.TrackData[ltoc.LastTrack].Address[1] != cdrom_ioctl_windows[id].toc.TrackData[cdrom_ioctl_windows[id].toc.LastTrack].Address[1]) ||
|
|
(ltoc.TrackData[ltoc.LastTrack].Address[2] != cdrom_ioctl_windows[id].toc.TrackData[cdrom_ioctl_windows[id].toc.LastTrack].Address[2]) ||
|
|
(ltoc.TrackData[ltoc.LastTrack].Address[3] != cdrom_ioctl_windows[id].toc.TrackData[cdrom_ioctl_windows[id].toc.LastTrack].Address[3]))
|
|
{
|
|
cdrom_ioctl[id].cd_state = CD_STOPPED;
|
|
cdrom_ioctl_log("Setting TOC...\n");
|
|
cdrom_ioctl_windows[id].toc = ltoc;
|
|
ioctl_hopen(id);
|
|
cdrom_ioctl[id].capacity_read=0; /* With this two lines, we read the READ CAPACITY command output from the host drive into our cache buffer. */
|
|
ioctl_read_capacity(id, NULL);
|
|
ioctl_close(id);
|
|
cdrom_ioctl[id].cdrom_capacity = ioctl_get_last_block(id, 0, 0, 4096, 0);
|
|
return 1; /* TOC mismatches. */
|
|
}
|
|
}
|
|
return 0; /* None of the above, return 0. */
|
|
}
|
|
|
|
static uint8_t ioctl_getcurrentsubchannel(uint8_t id, uint8_t *b, int msf)
|
|
{
|
|
CDROM_SUB_Q_DATA_FORMAT insub;
|
|
SUB_Q_CHANNEL_DATA sub;
|
|
unsigned long size;
|
|
int pos = 0, track;
|
|
uint32_t cdpos, track_address, dat;
|
|
|
|
if (!cdrom_drives[id].host_drive) return 0;
|
|
|
|
cdpos = cdrom[id].seek_pos;
|
|
|
|
if (cdrom_ioctl[id].last_subchannel_pos == cdpos)
|
|
{
|
|
memcpy(&insub, cdrom_ioctl[id].sub_q_data_format, sizeof(insub));
|
|
memcpy(&sub, cdrom_ioctl[id].sub_q_channel_data, sizeof(sub));
|
|
}
|
|
else
|
|
{
|
|
insub.Format = IOCTL_CDROM_CURRENT_POSITION;
|
|
ioctl_hopen(id);
|
|
DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL,IOCTL_CDROM_READ_Q_CHANNEL,&insub,sizeof(insub),&sub,sizeof(sub),&size,NULL);
|
|
ioctl_close(id);
|
|
memset(cdrom_ioctl[id].sub_q_data_format, 0, 16);
|
|
memcpy(cdrom_ioctl[id].sub_q_data_format, &insub, sizeof(insub));
|
|
memset(cdrom_ioctl[id].sub_q_channel_data, 0, 256);
|
|
memcpy(cdrom_ioctl[id].sub_q_channel_data, &sub, sizeof(sub));
|
|
cdrom_ioctl[id].last_subchannel_pos = cdpos;
|
|
}
|
|
|
|
if (cdrom_ioctl[id].cd_state == CD_PLAYING || cdrom_ioctl[id].cd_state == CD_PAUSED)
|
|
{
|
|
track = get_track_nr(id, cdpos);
|
|
track_address = cdrom_ioctl_windows[id].toc.TrackData[track].Address[3] + (cdrom_ioctl_windows[id].toc.TrackData[track].Address[2] * 75) + (cdrom_ioctl_windows[id].toc.TrackData[track].Address[1] * 75 * 60);
|
|
|
|
cdrom_ioctl_log("cdpos = %i, track = %i, track_address = %i\n", cdpos, track, track_address);
|
|
|
|
b[pos++] = sub.CurrentPosition.Control;
|
|
b[pos++] = track + 1;
|
|
b[pos++] = sub.CurrentPosition.IndexNumber;
|
|
|
|
if (msf)
|
|
{
|
|
dat = cdpos + 150;
|
|
b[pos + 3] = (uint8_t)(dat % 75); dat /= 75;
|
|
b[pos + 2] = (uint8_t)(dat % 60); dat /= 60;
|
|
b[pos + 1] = (uint8_t)dat;
|
|
b[pos] = 0;
|
|
pos += 4;
|
|
dat = cdpos - track_address;
|
|
b[pos + 3] = (uint8_t)(dat % 75); dat /= 75;
|
|
b[pos + 2] = (uint8_t)(dat % 60); dat /= 60;
|
|
b[pos + 1] = (uint8_t)dat;
|
|
b[pos] = 0;
|
|
pos += 4;
|
|
}
|
|
else
|
|
{
|
|
b[pos++] = (cdpos >> 24) & 0xff;
|
|
b[pos++] = (cdpos >> 16) & 0xff;
|
|
b[pos++] = (cdpos >> 8) & 0xff;
|
|
b[pos++] = cdpos & 0xff;
|
|
cdpos -= track_address;
|
|
b[pos++] = (cdpos >> 24) & 0xff;
|
|
b[pos++] = (cdpos >> 16) & 0xff;
|
|
b[pos++] = (cdpos >> 8) & 0xff;
|
|
b[pos++] = cdpos & 0xff;
|
|
}
|
|
|
|
if (cdrom_ioctl[id].cd_state == CD_PLAYING) return 0x11;
|
|
return 0x12;
|
|
}
|
|
|
|
b[pos++]=sub.CurrentPosition.Control;
|
|
b[pos++]=sub.CurrentPosition.TrackNumber;
|
|
b[pos++]=sub.CurrentPosition.IndexNumber;
|
|
|
|
cdrom_ioctl_log("cdpos = %i, track_address = %i\n", MSFtoLBA(sub.CurrentPosition.AbsoluteAddress[1], sub.CurrentPosition.AbsoluteAddress[2], sub.CurrentPosition.AbsoluteAddress[3]), MSFtoLBA(sub.CurrentPosition.TrackRelativeAddress[1], sub.CurrentPosition.TrackRelativeAddress[2], sub.CurrentPosition.TrackRelativeAddress[3]));
|
|
|
|
if (msf)
|
|
{
|
|
int c;
|
|
for (c = 0; c < 4; c++)
|
|
{
|
|
b[pos++] = sub.CurrentPosition.AbsoluteAddress[c];
|
|
}
|
|
for (c = 0; c < 4; c++)
|
|
{
|
|
b[pos++] = sub.CurrentPosition.TrackRelativeAddress[c];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint32_t temp = MSFtoLBA(sub.CurrentPosition.AbsoluteAddress[1], sub.CurrentPosition.AbsoluteAddress[2], sub.CurrentPosition.AbsoluteAddress[3]);
|
|
b[pos++] = temp >> 24;
|
|
b[pos++] = temp >> 16;
|
|
b[pos++] = temp >> 8;
|
|
b[pos++] = temp;
|
|
temp = MSFtoLBA(sub.CurrentPosition.TrackRelativeAddress[1], sub.CurrentPosition.TrackRelativeAddress[2], sub.CurrentPosition.TrackRelativeAddress[3]);
|
|
b[pos++] = temp >> 24;
|
|
b[pos++] = temp >> 16;
|
|
b[pos++] = temp >> 8;
|
|
b[pos++] = temp;
|
|
}
|
|
|
|
return 0x13;
|
|
}
|
|
|
|
static void ioctl_eject(uint8_t id)
|
|
{
|
|
unsigned long size;
|
|
if (!cdrom_drives[id].host_drive)
|
|
{
|
|
return;
|
|
}
|
|
if (cdrom_ioctl_windows[id].is_playing)
|
|
{
|
|
cdrom_ioctl_windows[id].is_playing = 0;
|
|
ioctl_stop(id);
|
|
}
|
|
cdrom_ioctl[id].cd_state = CD_STOPPED;
|
|
ioctl_hopen(id);
|
|
DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL,IOCTL_STORAGE_EJECT_MEDIA,NULL,0,NULL,0,&size,NULL);
|
|
ioctl_close(id);
|
|
}
|
|
|
|
static void ioctl_load(uint8_t id)
|
|
{
|
|
unsigned long size;
|
|
if (!cdrom_drives[id].host_drive)
|
|
{
|
|
return;
|
|
}
|
|
if (cdrom_ioctl_windows[id].is_playing)
|
|
{
|
|
cdrom_ioctl_windows[id].is_playing = 0;
|
|
ioctl_stop(id);
|
|
}
|
|
cdrom_ioctl[id].cd_state = CD_STOPPED;
|
|
ioctl_hopen(id);
|
|
DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL,IOCTL_STORAGE_LOAD_MEDIA,NULL,0,NULL,0,&size,NULL);
|
|
cdrom_ioctl[id].capacity_read=0; /* With this two lines, we read the READ CAPACITY command output from the host drive into our cache buffer. */
|
|
ioctl_read_capacity(id, NULL);
|
|
ioctl_close(id);
|
|
cdrom_ioctl[id].cdrom_capacity = ioctl_get_last_block(id, 0, 0, 4096, 0);
|
|
}
|
|
|
|
static int ioctl_is_track_audio(uint8_t id, uint32_t pos, int ismsf)
|
|
{
|
|
int c;
|
|
int control = 0;
|
|
|
|
uint32_t track_address = 0;
|
|
|
|
if (!cdrom_ioctl[id].tocvalid)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for (c = 0; cdrom_ioctl_windows[id].toc.TrackData[c].TrackNumber != 0xaa; c++)
|
|
{
|
|
if (ismsf) {
|
|
track_address = cdrom_ioctl_windows[id].toc.TrackData[c].Address[3];
|
|
track_address |= (cdrom_ioctl_windows[id].toc.TrackData[c].Address[2] << 8);
|
|
track_address |= (cdrom_ioctl_windows[id].toc.TrackData[c].Address[1] << 16);
|
|
} else {
|
|
track_address = MSFtoLBA(cdrom_ioctl_windows[id].toc.TrackData[c].Address[1],cdrom_ioctl_windows[id].toc.TrackData[c].Address[2],cdrom_ioctl_windows[id].toc.TrackData[c].Address[3]);
|
|
track_address -= 150;
|
|
}
|
|
|
|
if (cdrom_ioctl_windows[id].toc.TrackData[c].TrackNumber >= cdrom_ioctl_windows[id].toc.FirstTrack &&
|
|
cdrom_ioctl_windows[id].toc.TrackData[c].TrackNumber <= cdrom_ioctl_windows[id].toc.LastTrack &&
|
|
track_address <= pos)
|
|
control = cdrom_ioctl_windows[id].toc.TrackData[c].Control;
|
|
}
|
|
|
|
if ((control & 0xd) <= 1)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* 00, 08, 10, 18, 20, 28, 30, 38 */
|
|
int flags_to_size[5][32] = { { 0, 0, 2352, 2352, 2352, 2352, 2352, 2352, /* 00-38 (CD-DA) */
|
|
2352, 2352, 2352, 2352, 2352, 2352, 2352, 2352, /* 40-78 */
|
|
2352, 2352, 2352, 2352, 2352, 2352, 2352, 2352, /* 80-B8 */
|
|
2352, 2352, 2352, 2352, 2352, 2352, 2352, 2352 }, /* C0-F8 */
|
|
{ 0, 0, 2048, 2336, 4, -296, 2052, 2344, /* 00-38 (Mode 1) */
|
|
8, -296, 2048, 2048, 12, -296, 2052, 2052, /* 40-78 */
|
|
-296, -296, -296, -296, 16, -296, 2064, 2344, /* 80-B8 */
|
|
-296, -296, 2048, 2048, 24, -296, 2064, 2352 }, /* C0-F8 */
|
|
{ 0, 0, 2336, 2336, 4, -296, 2340, 2340, /* 00-38 (Mode 2 non-XA) */
|
|
8, -296, 2336, 2336, 12, -296, 2340, 2340, /* 40-78 */
|
|
-296, -296, -296, -296, 16, -296, 2352, 2340, /* 80-B8 */
|
|
-296, -296, 2336, 2336, 24, -296, 2352, 2352 }, /* C0-F8 */
|
|
{ 0, 0, 2048, 2336, 4, -296, -296, -296, /* 00-38 (Mode 2 Form 1) */
|
|
8, -296, 2056, 2344, 12, -296, 2060, 2340, /* 40-78 */
|
|
-296, -296, -296, -296, 16, -296, -296, -296, /* 80-B8 */
|
|
-296, -296, -296, -296, 24, -296, 2072, 2352 }, /* C0-F8 */
|
|
{ 0, 0, 2328, 2328, 4, -296, -296, -296, /* 00-38 (Mode 2 Form 2) */
|
|
8, -296, 2336, 2336, 12, -296, 2340, 2340, /* 40-78 */
|
|
-296, -296, -296, -296, 16, -296, -296, -296, /* 80-B8 */
|
|
-296, -296, -296, -296, 24, -296, 2352, 2352 } /* C0-F8 */
|
|
};
|
|
|
|
static int ioctl_get_sector_data_type(uint8_t id, uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, int ismsf);
|
|
|
|
static void cdrom_illegal_mode(uint8_t id)
|
|
{
|
|
cdrom_sense_key = SENSE_ILLEGAL_REQUEST;
|
|
cdrom_asc = ASC_ILLEGAL_MODE_FOR_THIS_TRACK;
|
|
cdrom_ascq = 0;
|
|
}
|
|
|
|
struct sptd_with_sense
|
|
{
|
|
SCSI_PASS_THROUGH s;
|
|
ULONG Filler;
|
|
UCHAR sense[32];
|
|
UCHAR data[65536];
|
|
} sptd;
|
|
|
|
static int ioctl_get_block_length(uint8_t id, const UCHAR *cdb, int number_of_blocks, int no_length_check)
|
|
{
|
|
int sector_type = 0;
|
|
int temp_len = 0;
|
|
|
|
if (no_length_check)
|
|
{
|
|
switch (cdb[0])
|
|
{
|
|
case 0x25: /* READ CAPACITY */
|
|
case 0x44: /* READ HEADER */
|
|
return 8;
|
|
case 0x42: /* READ SUBCHANNEL */
|
|
case 0x43: /* READ TOC */
|
|
case 0x51: /* READ DISC INFORMATION */
|
|
case 0x52: /* READ TRACK INFORMATION */
|
|
case 0x5A: /* MODE SENSE (10) */
|
|
return (((uint32_t) cdb[7]) << 8) | ((uint32_t) cdb[8]);
|
|
case 0xAD: /* READ DVD STRUCTURE */
|
|
return (((uint32_t) cdb[8]) << 8) | ((uint32_t) cdb[9]);
|
|
default:
|
|
return 65534;
|
|
}
|
|
}
|
|
|
|
switch (cdb[0])
|
|
{
|
|
case 0x25: /* READ CAPACITY */
|
|
case 0x44: /* READ HEADER */
|
|
return 8;
|
|
case 0x42: /* READ SUBCHANNEL */
|
|
case 0x43: /* READ TOC */
|
|
case 0x51: /* READ DISC INFORMATION */
|
|
case 0x52: /* READ TRACK INFORMATION */
|
|
case 0x5A: /* MODE SENSE (10) */
|
|
return (((uint32_t) cdb[7]) << 8) | ((uint32_t) cdb[8]);
|
|
case 0xAD: /* READ DVD STRUCTURE */
|
|
return (((uint32_t) cdb[8]) << 8) | ((uint32_t) cdb[9]);
|
|
case 0x08:
|
|
case 0x28:
|
|
case 0xa8:
|
|
/* READ (6), READ (10), READ (12) */
|
|
return 2048 * number_of_blocks;
|
|
break;
|
|
case 0xb9:
|
|
sector_type = (cdb[1] >> 2) & 7;
|
|
if (sector_type == 0)
|
|
{
|
|
sector_type = ioctl_get_sector_data_type(id, 0, cdb[3], cdb[4], cdb[5], 1);
|
|
if (sector_type == 0)
|
|
{
|
|
cdrom_illegal_mode(id);
|
|
return -1;
|
|
}
|
|
}
|
|
goto common_handler;
|
|
case 0xbe:
|
|
/* READ CD MSF, READ CD */
|
|
sector_type = (cdb[1] >> 2) & 7;
|
|
if (sector_type == 0)
|
|
{
|
|
sector_type = ioctl_get_sector_data_type(id, cdb[2], cdb[3], cdb[4], cdb[5], 0);
|
|
if (sector_type == 0)
|
|
{
|
|
cdrom_illegal_mode(id);
|
|
return -1;
|
|
}
|
|
}
|
|
common_handler:
|
|
temp_len = flags_to_size[sector_type - 1][cdb[9] >> 3];
|
|
if ((cdb[9] & 6) == 2)
|
|
{
|
|
temp_len += 294;
|
|
}
|
|
else if ((cdb[9] & 6) == 4)
|
|
{
|
|
temp_len += 296;
|
|
}
|
|
if ((cdb[10] & 7) == 1)
|
|
{
|
|
temp_len += 96;
|
|
}
|
|
else if ((cdb[10] & 7) == 2)
|
|
{
|
|
temp_len += 16;
|
|
}
|
|
else if ((cdb[10] & 7) == 4)
|
|
{
|
|
temp_len += 96;
|
|
}
|
|
if (temp_len <= 0)
|
|
{
|
|
cdrom_illegal_mode(id);
|
|
return -1;
|
|
}
|
|
return temp_len * cdrom[id].requested_blocks;
|
|
break;
|
|
default:
|
|
/* Other commands */
|
|
return 65534;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
static int SCSICommand(uint8_t id, const UCHAR *cdb, UCHAR *buf, uint32_t *len, int no_length_check)
|
|
{
|
|
DWORD ioctl_bytes;
|
|
int ioctl_rv = 0;
|
|
|
|
SCSISense.SenseKey = 0;
|
|
SCSISense.Asc = 0;
|
|
SCSISense.Ascq = 0;
|
|
|
|
*len = 0;
|
|
memset(&sptd, 0, sizeof(sptd));
|
|
sptd.s.Length = sizeof(SCSI_PASS_THROUGH);
|
|
sptd.s.CdbLength = 12;
|
|
sptd.s.DataIn = SCSI_IOCTL_DATA_IN;
|
|
sptd.s.TimeOutValue = 80 * 60;
|
|
sptd.s.DataTransferLength = ioctl_get_block_length(id, cdb, cdrom_ioctl[id].actual_requested_blocks, no_length_check);
|
|
sptd.s.SenseInfoOffset = (uintptr_t)&sptd.sense - (uintptr_t)&sptd;
|
|
sptd.s.SenseInfoLength = 32;
|
|
sptd.s.DataBufferOffset = (uintptr_t)&sptd.data - (uintptr_t)&sptd;
|
|
|
|
memcpy(sptd.s.Cdb, cdb, 12);
|
|
ioctl_rv = DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL, IOCTL_SCSI_PASS_THROUGH, &sptd, sizeof(sptd), &sptd, sizeof(sptd), &ioctl_bytes, NULL);
|
|
|
|
if (sptd.s.SenseInfoLength)
|
|
{
|
|
cdrom_sense_key = sptd.sense[2];
|
|
cdrom_asc = sptd.sense[12];
|
|
cdrom_ascq = sptd.sense[13];
|
|
}
|
|
|
|
cdrom_ioctl_log("Transferred length: %i (command: %02X)\n", sptd.s.DataTransferLength, cdb[0]);
|
|
cdrom_ioctl_log("Sense length: %i (%02X %02X %02X %02X %02X)\n", sptd.s.SenseInfoLength, sptd.sense[0], sptd.sense[1], sptd.sense[2], sptd.sense[12], sptd.sense[13]);
|
|
cdrom_ioctl_log("IOCTL bytes: %i; SCSI status: %i, status: %i, LastError: %08X\n", ioctl_bytes, sptd.s.ScsiStatus, ioctl_rv, GetLastError());
|
|
cdrom_ioctl_log("DATA: %02X %02X %02X %02X %02X %02X\n", sptd.data[0], sptd.data[1], sptd.data[2], sptd.data[3], sptd.data[4], sptd.data[5]);
|
|
cdrom_ioctl_log(" %02X %02X %02X %02X %02X %02X\n", sptd.data[6], sptd.data[7], sptd.data[8], sptd.data[9], sptd.data[10], sptd.data[11]);
|
|
cdrom_ioctl_log(" %02X %02X %02X %02X %02X %02X\n", sptd.data[12], sptd.data[13], sptd.data[14], sptd.data[15], sptd.data[16], sptd.data[17]);
|
|
cdrom_ioctl_log("SENSE: %02X %02X %02X %02X %02X %02X\n", sptd.sense[0], sptd.sense[1], sptd.sense[2], sptd.sense[3], sptd.sense[4], sptd.sense[5]);
|
|
cdrom_ioctl_log(" %02X %02X %02X %02X %02X %02X\n", sptd.sense[6], sptd.sense[7], sptd.sense[8], sptd.sense[9], sptd.sense[10], sptd.sense[11]);
|
|
cdrom_ioctl_log(" %02X %02X %02X %02X %02X %02X\n", sptd.sense[12], sptd.sense[13], sptd.sense[14], sptd.sense[15], sptd.sense[16], sptd.sense[17]);
|
|
*len = sptd.s.DataTransferLength;
|
|
if (sptd.s.DataTransferLength != 0)
|
|
{
|
|
memcpy(buf, sptd.data, sptd.s.DataTransferLength);
|
|
}
|
|
|
|
return ioctl_rv;
|
|
}
|
|
|
|
static void ioctl_read_capacity(uint8_t id, uint8_t *b)
|
|
{
|
|
uint32_t len = 0;
|
|
|
|
const UCHAR cdb[] = { 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
UCHAR buf[16];
|
|
|
|
if (!cdrom_ioctl[id].capacity_read || (b == NULL))
|
|
{
|
|
SCSICommand(id, cdb, buf, &len, 1);
|
|
|
|
memcpy(cdrom_ioctl[id].rcbuf, buf, len);
|
|
cdrom_ioctl[id].capacity_read = 1;
|
|
}
|
|
else
|
|
{
|
|
memcpy(b, cdrom_ioctl[id].rcbuf, 16);
|
|
}
|
|
}
|
|
|
|
static int ioctl_media_type_id(uint8_t id)
|
|
{
|
|
uint8_t old_sense[3] = { 0, 0, 0 };
|
|
|
|
UCHAR msbuf[28];
|
|
uint32_t len = 0;
|
|
int sense = 0;
|
|
|
|
const UCHAR cdb[] = { 0x5A, 0x00, 0x2A, 0, 0, 0, 0, 0, 28, 0, 0, 0 };
|
|
|
|
old_sense[0] = cdrom_sense_key;
|
|
old_sense[1] = cdrom_asc;
|
|
old_sense[2] = cdrom_asc;
|
|
|
|
ioctl_hopen(id);
|
|
|
|
SCSICommand(id, cdb, msbuf, &len, 1);
|
|
|
|
ioctl_close(id);
|
|
|
|
sense = cdrom_sense_key;
|
|
cdrom_sense_key = old_sense[0];
|
|
cdrom_asc = old_sense[1];
|
|
cdrom_asc = old_sense[2];
|
|
|
|
if (sense == 0)
|
|
{
|
|
return msbuf[2];
|
|
}
|
|
else
|
|
{
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
static uint32_t msf_to_lba32(int lba)
|
|
{
|
|
int m = (lba >> 16) & 0xff;
|
|
int s = (lba >> 8) & 0xff;
|
|
int f = lba & 0xff;
|
|
return (m * 60 * 75) + (s * 75) + f;
|
|
}
|
|
|
|
static int ioctl_get_type(uint8_t id, UCHAR *cdb, UCHAR *buf)
|
|
{
|
|
int i = 0;
|
|
int ioctl_rv = 0;
|
|
|
|
uint32_t len = 0;
|
|
|
|
for (i = 2; i <= 5; i++)
|
|
{
|
|
cdb[1] = i << 2;
|
|
ioctl_rv = SCSICommand(id, cdb, buf, &len, 1); /* Bypass length check so we don't risk calling this again and getting stuck in an endless up. */
|
|
if (ioctl_rv)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ioctl_sector_data_type(uint8_t id, int sector, int ismsf)
|
|
{
|
|
int ioctl_rv = 0;
|
|
UCHAR cdb_lba[] = { 0xBE, 0, 0, 0, 0, 0, 0, 0, 1, 0x10, 0, 0 };
|
|
UCHAR cdb_msf[] = { 0xB9, 0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0, 0 };
|
|
UCHAR buf[2352];
|
|
|
|
cdb_lba[2] = (sector >> 24);
|
|
cdb_lba[3] = ((sector >> 16) & 0xff);
|
|
cdb_lba[4] = ((sector >> 8) & 0xff);
|
|
cdb_lba[5] = (sector & 0xff);
|
|
|
|
cdb_msf[3] = cdb_msf[6] = ((sector >> 16) & 0xff);
|
|
cdb_msf[4] = cdb_msf[7] = ((sector >> 8) & 0xff);
|
|
cdb_msf[5] = cdb_msf[8] = (sector & 0xff);
|
|
|
|
ioctl_hopen(id);
|
|
|
|
if (ioctl_is_track_audio(id, sector, ismsf))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (ismsf)
|
|
{
|
|
ioctl_rv = ioctl_get_type(id, cdb_msf, buf);
|
|
}
|
|
else
|
|
{
|
|
ioctl_rv = ioctl_get_type(id, cdb_lba, buf);
|
|
}
|
|
|
|
if (ioctl_rv)
|
|
{
|
|
ioctl_close(id);
|
|
return ioctl_rv;
|
|
}
|
|
|
|
if (ismsf)
|
|
{
|
|
sector = msf_to_lba32(sector);
|
|
if (sector < 150)
|
|
{
|
|
ioctl_close(id);
|
|
return 0;
|
|
}
|
|
sector -= 150;
|
|
ioctl_rv = ioctl_get_type(id, (UCHAR *) cdb_lba, buf);
|
|
}
|
|
|
|
ioctl_close(id);
|
|
return ioctl_rv;
|
|
}
|
|
|
|
static int ioctl_get_sector_data_type(uint8_t id, uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, int ismsf)
|
|
{
|
|
int sector = b3;
|
|
sector |= ((uint32_t) b2) << 8;
|
|
sector |= ((uint32_t) b1) << 16;
|
|
sector |= ((uint32_t) b0) << 24;
|
|
return ioctl_sector_data_type(id, sector, ismsf);
|
|
}
|
|
|
|
static void ioctl_validate_toc(uint8_t id)
|
|
{
|
|
unsigned long size;
|
|
if (!cdrom_drives[id].host_drive)
|
|
{
|
|
return;
|
|
}
|
|
cdrom_ioctl[id].cd_state = CD_STOPPED;
|
|
ioctl_hopen(id);
|
|
cdrom_ioctl_log("Validating TOC...\n");
|
|
DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL,IOCTL_CDROM_READ_TOC, NULL,0,&cdrom_ioctl_windows[id].toc,sizeof(cdrom_ioctl_windows[id].toc),&size,NULL);
|
|
ioctl_close(id);
|
|
cdrom_ioctl[id].tocvalid=1;
|
|
}
|
|
|
|
UCHAR buf[262144];
|
|
|
|
static int ioctl_pass_through(uint8_t id, uint8_t *in_cdb, uint8_t *b, uint32_t *len)
|
|
{
|
|
const UCHAR cdb[12];
|
|
|
|
int ret = 0;
|
|
|
|
int temp_block_length = 0;
|
|
int buffer_pos = 0;
|
|
|
|
uint32_t temp_len = 0;
|
|
|
|
int i = 0;
|
|
|
|
if (in_cdb[0] == 0x43)
|
|
{
|
|
/* This is a read TOC, so we have to validate the TOC to make the rest of the emulator happy. */
|
|
ioctl_validate_toc(id);
|
|
}
|
|
|
|
ioctl_hopen(id);
|
|
|
|
memcpy((void *) cdb, in_cdb, 12);
|
|
|
|
temp_len = 0;
|
|
temp_block_length = ioctl_get_block_length(id, cdb, cdrom[id].requested_blocks, 0);
|
|
if (temp_block_length != -1) {
|
|
cdrom_ioctl[id].actual_requested_blocks = 1;
|
|
if ((cdb[0] == 0x08) || (cdb[0] == 0x28) || (cdb[0] == 0xA8) || (cdb[0] == 0xB9) || (cdb[0] == 0xBE)) {
|
|
buffer_pos = 0;
|
|
temp_len = 0;
|
|
|
|
for (i = 0; i < cdrom[id].requested_blocks; i++)
|
|
{
|
|
cdrom_ioctl_log("CD-ROM %i: ioctl_pass_through(): Transferring block...\n", id, cdrom_ioctl[id].actual_requested_blocks);
|
|
cdrom_update_cdb((uint8_t *) cdb, cdrom[id].sector_pos + i, 1);
|
|
ret = SCSICommand(id, cdb, b + buffer_pos, &temp_len, 0);
|
|
buffer_pos += temp_len;
|
|
}
|
|
*len = buffer_pos;
|
|
} else {
|
|
cdrom_ioctl_log("CD-ROM %i: ioctl_pass_through(): Expected transfer length %i is smaller than 65534, transferring all at once...\n", id, temp_block_length);
|
|
ret = SCSICommand(id, cdb, b, len, 0);
|
|
cdrom_ioctl_log("CD-ROM %i: ioctl_pass_through(): Single transfer done\n", id);
|
|
}
|
|
}
|
|
|
|
cdrom_ioctl_log("IOCTL DATA: %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]);
|
|
|
|
ioctl_close(id);
|
|
|
|
cdrom_ioctl_log("IOCTL Returned value: %i\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ioctl_readtoc(uint8_t id, unsigned char *b, unsigned char starttrack, int msf, int maxlen, int single)
|
|
{
|
|
int len=4;
|
|
DWORD size;
|
|
int c,d;
|
|
uint32_t temp;
|
|
uint32_t last_block;
|
|
if (!cdrom_drives[id].host_drive)
|
|
{
|
|
return 0;
|
|
}
|
|
cdrom_ioctl[id].cd_state = CD_STOPPED;
|
|
ioctl_hopen(id);
|
|
DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL,IOCTL_CDROM_READ_TOC, NULL,0,&cdrom_ioctl_windows[id].toc,sizeof(cdrom_ioctl_windows[id].toc),&size,NULL);
|
|
ioctl_close(id);
|
|
cdrom_ioctl[id].tocvalid = 1;
|
|
b[2]=cdrom_ioctl_windows[id].toc.FirstTrack;
|
|
b[3]=cdrom_ioctl_windows[id].toc.LastTrack;
|
|
d=0;
|
|
for (c=0;c<=cdrom_ioctl_windows[id].toc.LastTrack;c++)
|
|
{
|
|
if (cdrom_ioctl_windows[id].toc.TrackData[c].TrackNumber>=starttrack)
|
|
{
|
|
d=c;
|
|
break;
|
|
}
|
|
}
|
|
last_block = 0;
|
|
for (c=d;c<=cdrom_ioctl_windows[id].toc.LastTrack;c++)
|
|
{
|
|
uint32_t address;
|
|
if ((len+8)>maxlen) break;
|
|
b[len++]=0; /*Reserved*/
|
|
b[len++]=(cdrom_ioctl_windows[id].toc.TrackData[c].Adr<<4)|cdrom_ioctl_windows[id].toc.TrackData[c].Control;
|
|
b[len++]=cdrom_ioctl_windows[id].toc.TrackData[c].TrackNumber;
|
|
b[len++]=0; /*Reserved*/
|
|
address = MSFtoLBA(cdrom_ioctl_windows[id].toc.TrackData[c].Address[1],cdrom_ioctl_windows[id].toc.TrackData[c].Address[2],cdrom_ioctl_windows[id].toc.TrackData[c].Address[3]);
|
|
if (address > last_block)
|
|
last_block = address;
|
|
|
|
if (msf)
|
|
{
|
|
b[len++]=cdrom_ioctl_windows[id].toc.TrackData[c].Address[0];
|
|
b[len++]=cdrom_ioctl_windows[id].toc.TrackData[c].Address[1];
|
|
b[len++]=cdrom_ioctl_windows[id].toc.TrackData[c].Address[2];
|
|
b[len++]=cdrom_ioctl_windows[id].toc.TrackData[c].Address[3];
|
|
}
|
|
else
|
|
{
|
|
temp=MSFtoLBA(cdrom_ioctl_windows[id].toc.TrackData[c].Address[1],cdrom_ioctl_windows[id].toc.TrackData[c].Address[2],cdrom_ioctl_windows[id].toc.TrackData[c].Address[3]) - 150;
|
|
b[len++]=temp>>24;
|
|
b[len++]=temp>>16;
|
|
b[len++]=temp>>8;
|
|
b[len++]=temp;
|
|
}
|
|
if (single) break;
|
|
}
|
|
b[0] = (uint8_t)(((len-2) >> 8) & 0xff);
|
|
b[1] = (uint8_t)((len-2) & 0xff);
|
|
return len;
|
|
}
|
|
|
|
static int ioctl_readtoc_session(uint8_t id, unsigned char *b, int msf, int maxlen)
|
|
{
|
|
int len=4;
|
|
int size;
|
|
uint32_t temp;
|
|
CDROM_READ_TOC_EX toc_ex;
|
|
CDROM_TOC_SESSION_DATA toc;
|
|
if (!cdrom_drives[id].host_drive)
|
|
{
|
|
return 0;
|
|
}
|
|
cdrom_ioctl[id].cd_state = CD_STOPPED;
|
|
memset(&toc_ex,0,sizeof(toc_ex));
|
|
memset(&toc,0,sizeof(toc));
|
|
toc_ex.Format=CDROM_READ_TOC_EX_FORMAT_SESSION;
|
|
toc_ex.Msf=msf;
|
|
toc_ex.SessionTrack=0;
|
|
ioctl_hopen(id);
|
|
DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL,IOCTL_CDROM_READ_TOC_EX, &toc_ex,sizeof(toc_ex),&toc,sizeof(toc),(PDWORD)&size,NULL);
|
|
ioctl_close(id);
|
|
b[2]=toc.FirstCompleteSession;
|
|
b[3]=toc.LastCompleteSession;
|
|
b[len++]=0; /*Reserved*/
|
|
b[len++]=(toc.TrackData[0].Adr<<4)|toc.TrackData[0].Control;
|
|
b[len++]=toc.TrackData[0].TrackNumber;
|
|
b[len++]=0; /*Reserved*/
|
|
if (msf)
|
|
{
|
|
b[len++]=toc.TrackData[0].Address[0];
|
|
b[len++]=toc.TrackData[0].Address[1];
|
|
b[len++]=toc.TrackData[0].Address[2];
|
|
b[len++]=toc.TrackData[0].Address[3];
|
|
}
|
|
else
|
|
{
|
|
temp=MSFtoLBA(toc.TrackData[0].Address[1],toc.TrackData[0].Address[2],toc.TrackData[0].Address[3]) - 150;
|
|
b[len++]=temp>>24;
|
|
b[len++]=temp>>16;
|
|
b[len++]=temp>>8;
|
|
b[len++]=temp;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static int ioctl_readtoc_raw(uint8_t id, uint8_t *b, int maxlen)
|
|
{
|
|
int len=4;
|
|
int size;
|
|
int i;
|
|
CDROM_READ_TOC_EX toc_ex;
|
|
CDROM_TOC_FULL_TOC_DATA toc;
|
|
if (!cdrom_drives[id].host_drive)
|
|
{
|
|
return 0;
|
|
}
|
|
cdrom_ioctl[id].cd_state = CD_STOPPED;
|
|
memset(&toc_ex,0,sizeof(toc_ex));
|
|
memset(&toc,0,sizeof(toc));
|
|
toc_ex.Format=CDROM_READ_TOC_EX_FORMAT_FULL_TOC;
|
|
toc_ex.Msf=1;
|
|
toc_ex.SessionTrack=0;
|
|
ioctl_hopen(id);
|
|
DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL,IOCTL_CDROM_READ_TOC_EX, &toc_ex,sizeof(toc_ex),&toc,sizeof(toc),(PDWORD)&size,NULL);
|
|
ioctl_close(id);
|
|
if (maxlen >= 3) b[2]=toc.FirstCompleteSession;
|
|
if (maxlen >= 4) b[3]=toc.LastCompleteSession;
|
|
|
|
if (len >= maxlen) return len;
|
|
|
|
size -= sizeof(CDROM_TOC_FULL_TOC_DATA);
|
|
size /= sizeof(toc.Descriptors[0]);
|
|
|
|
for (i = 0; i <= size; i++)
|
|
{
|
|
b[len++]=toc.Descriptors[i].SessionNumber;
|
|
if (len == maxlen) return len;
|
|
b[len++]=(toc.Descriptors[i].Adr<<4)|toc.Descriptors[i].Control;
|
|
if (len == maxlen) return len;
|
|
b[len++]=0;
|
|
if (len == maxlen) return len;
|
|
b[len++]=toc.Descriptors[i].Reserved1; /*Reserved*/
|
|
if (len == maxlen) return len;
|
|
b[len++]=toc.Descriptors[i].MsfExtra[0];
|
|
if (len == maxlen) return len;
|
|
b[len++]=toc.Descriptors[i].MsfExtra[1];
|
|
if (len == maxlen) return len;
|
|
b[len++]=toc.Descriptors[i].MsfExtra[2];
|
|
if (len == maxlen) return len;
|
|
b[len++]=toc.Descriptors[i].Zero;
|
|
if (len == maxlen) return len;
|
|
b[len++]=toc.Descriptors[i].Msf[0];
|
|
if (len == maxlen) return len;
|
|
b[len++]=toc.Descriptors[i].Msf[1];
|
|
if (len == maxlen) return len;
|
|
b[len++]=toc.Descriptors[i].Msf[2];
|
|
if (len == maxlen) return len;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static uint32_t ioctl_size(uint8_t id)
|
|
{
|
|
uint8_t capacity_buffer[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
uint32_t capacity = 0;
|
|
ioctl_read_capacity(id, capacity_buffer);
|
|
capacity = ((uint32_t) capacity_buffer[0]) << 24;
|
|
capacity |= ((uint32_t) capacity_buffer[1]) << 16;
|
|
capacity |= ((uint32_t) capacity_buffer[2]) << 8;
|
|
capacity |= (uint32_t) capacity_buffer[3];
|
|
return capacity + 1;
|
|
}
|
|
|
|
static int ioctl_status(uint8_t id)
|
|
{
|
|
if (!(ioctl_ready(id)) && (cdrom_drives[id].host_drive <= 0))
|
|
{
|
|
return CD_STATUS_EMPTY;
|
|
}
|
|
|
|
switch(cdrom_ioctl[id].cd_state)
|
|
{
|
|
case CD_PLAYING:
|
|
return CD_STATUS_PLAYING;
|
|
case CD_PAUSED:
|
|
return CD_STATUS_PAUSED;
|
|
case CD_STOPPED:
|
|
return CD_STATUS_STOPPED;
|
|
default:
|
|
return CD_STATUS_EMPTY;
|
|
}
|
|
}
|
|
|
|
void ioctl_reset(uint8_t id)
|
|
{
|
|
CDROM_TOC ltoc;
|
|
unsigned long size;
|
|
|
|
if (!cdrom_drives[id].host_drive)
|
|
{
|
|
cdrom_ioctl[id].tocvalid = 0;
|
|
return;
|
|
}
|
|
|
|
ioctl_hopen(id);
|
|
DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL, IOCTL_CDROM_READ_TOC, NULL, 0, <oc, sizeof(ltoc), &size, NULL);
|
|
ioctl_close(id);
|
|
|
|
cdrom_ioctl_windows[id].toc = ltoc;
|
|
cdrom_ioctl[id].tocvalid = 1;
|
|
}
|
|
|
|
int ioctl_hopen(uint8_t id)
|
|
{
|
|
if (cdrom_ioctl_windows[id].is_playing) return 0;
|
|
cdrom_ioctl_windows[id].hIOCTL = CreateFile(cdrom_ioctl[id].ioctl_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
|
|
return 0;
|
|
}
|
|
|
|
int ioctl_open(uint8_t id, char d)
|
|
{
|
|
sprintf(cdrom_ioctl[id].ioctl_path,"\\\\.\\%c:",d);
|
|
cdrom_ioctl[id].tocvalid=0;
|
|
cdrom_ioctl_windows[id].hIOCTL = CreateFile(cdrom_ioctl[id].ioctl_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
|
|
cdrom_drives[id].handler = &ioctl_cdrom;
|
|
cdrom_ioctl[id].ioctl_inited=1;
|
|
cdrom_ioctl[id].capacity_read=0; /* With this two lines, we read the READ CAPACITY command output from the host drive into our cache buffer. */
|
|
ioctl_read_capacity(id, NULL);
|
|
CloseHandle(cdrom_ioctl_windows[id].hIOCTL);
|
|
cdrom_ioctl_windows[id].hIOCTL = NULL;
|
|
return 0;
|
|
}
|
|
|
|
void ioctl_close(uint8_t id)
|
|
{
|
|
if (cdrom_ioctl_windows[id].is_playing) return;
|
|
if (cdrom_ioctl_windows[id].hIOCTL)
|
|
{
|
|
CloseHandle(cdrom_ioctl_windows[id].hIOCTL);
|
|
cdrom_ioctl_windows[id].hIOCTL = NULL;
|
|
}
|
|
}
|
|
|
|
static void ioctl_exit(uint8_t id)
|
|
{
|
|
cdrom_ioctl_windows[id].is_playing = 0;
|
|
ioctl_stop(id);
|
|
cdrom_ioctl[id].ioctl_inited=0;
|
|
cdrom_ioctl[id].tocvalid=0;
|
|
}
|
|
|
|
static CDROM ioctl_cdrom=
|
|
{
|
|
ioctl_ready,
|
|
ioctl_medium_changed,
|
|
ioctl_media_type_id,
|
|
ioctl_audio_callback,
|
|
ioctl_audio_stop,
|
|
ioctl_readtoc,
|
|
ioctl_readtoc_session,
|
|
ioctl_readtoc_raw,
|
|
ioctl_getcurrentsubchannel,
|
|
ioctl_pass_through,
|
|
NULL,
|
|
ioctl_playaudio,
|
|
ioctl_load,
|
|
ioctl_eject,
|
|
ioctl_pause,
|
|
ioctl_resume,
|
|
ioctl_size,
|
|
ioctl_status,
|
|
ioctl_is_track_audio,
|
|
ioctl_stop,
|
|
ioctl_exit
|
|
};
|