pcsx2/plugins/CDVDlinuz/Src/Linux/CD.c

735 lines
11 KiB
C

/* CD.c
* Copyright (C) 2002-2005 PCSX2 Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
* PCSX2 members can be contacted through their website at www.pcsx2.net.
*/
#include <errno.h> // errno
#include <stddef.h> // NULL
#include <string.h> // strerror()
#include <fcntl.h> // open()
#include <sys/ioctl.h> // ioctl()
#include <sys/stat.h> // open()
#include <sys/types.h> // lseek(), open()
#include <unistd.h> // close(), lseek(), (sleep())
#include <linux/cdrom.h> // CD/DVD based ioctl() and defines.
#include "../convert.h"
#include "logfile.h"
#include "device.h"
#include "CD.h"
// Constants
u8 *playstationcdname = "PLAYSTATION\0";
u8 *ps1name = "CD-XA001\0";
// CD-ROM temp storage structures (see linux/cdrom.h for details)
struct cdrom_tochdr cdheader;
struct cdrom_tocentry cdtrack;
struct cdrom_subchnl subchannel;
u8 cdtempbuffer[2352];
int cdmode; // mode of last CDVDreadTrack call (important for CDs)
// Internal Functions
void InitCDSectorInfo() {
cdmode = -1;
} // END InitSectorInfo();
// Function Calls from CDVD.c
void InitCDInfo() {
InitCDSectorInfo();
} // END InitDiscType()
s32 CDreadTrack(u32 lsn, int mode, u8 *buffer) {
s32 s32result;
#ifdef VERBOSE_FUNCTION
PrintLog("CDVD driver: CDreadTrack(%i)", lsn);
#endif /* VERBOSE_FUNCTION */
s32result = 0;
if(buffer == NULL) return(-1);
// The CD way of figuring out where to read.
LBAtoMSF(lsn, buffer);
switch(mode) {
case CDVD_MODE_2048:
case CDVD_MODE_2328:
case CDVD_MODE_2340:
case CDVD_MODE_2352:
errno = 4; // Interrupted system call... (simulated the first time)
while(errno == 4) {
errno = 0;
s32result = ioctl(devicehandle, CDROMREADRAW, buffer);
} // ENDWHILE- Continually being interrupted by the system...
break;
case CDVD_MODE_2368: // Unimplemented... as yet.
default:
#ifdef VERBOSE_WARNINGS
PrintLog("CDVD driver: Unknown Mode %i", mode);
#endif /* VERBOSE_WARNINGS */
return(-1); // Illegal Read Mode? Abort
break;
} // ENDSWITCH- Which read mode should we choose?
if((s32result == -1) || (errno != 0)) {
#ifdef VERBOSE_WARNINGS
PrintLog("CDVD driver: Error reading CD: %i:%s", errno, strerror(errno));
#endif /* VERBOSE_WARNINGS */
InitCDSectorInfo();
return(-1);
} // ENDIF- Trouble getting a track count?
cdmode = mode; // Save mode for buffer positioning later.
return(0); // Call accomplished
} // END CDreadTrack()
s32 CDgetBufferOffset() {
#ifdef VERBOSE_FUNCTION
PrintLog("CDVD driver: CDgetBufferOffset()");
#endif /* VERBOSE_FUNCTION */
switch(cdmode) {
case CDVD_MODE_2048:
return(0+24);
case CDVD_MODE_2328:
return(0+24);
case CDVD_MODE_2340:
return(0+12);
case CDVD_MODE_2352:
return(0+0);
case CDVD_MODE_2368: // Unimplemented... as yet.
default:
#ifdef VERBOSE_WARNINGS
PrintLog("CDVD driver: Unknown Mode %i", cdmode);
#endif /* VERBOSE_WARNINGS */
return(0); // Not to worry. for now.
} // ENDSWITCH- where should we put the buffer pointer?
} // END CDgetBuffer()
// I, personally, don't see the big deal with SubQ
// However, sooner or later I'll incorporate it into the Cache Buffer system
// (backward compatibility, and all that)
s32 CDreadSubQ(u32 lsn, cdvdSubQ *subq) {
int tempmode;
s32 s32result;
s32result = 0;
#ifdef VERBOSE_FUNCTION
PrintLog("CDVD driver: CDreadSubQ()");
#endif /* VERBOSE_FUNCTION */
tempmode = cdmode;
if(tempmode == -1) tempmode = CDVD_MODE_2352;
CDreadTrack(lsn, tempmode, cdtempbuffer);
if((s32result == -1) || (errno != 0)) {
#ifdef VERBOSE_WARNINGS
PrintLog("CDVD driver: Error prepping CD SubQ: %i:%s", errno, strerror(errno));
#endif /* VERBOSE_WARNINGS */
return(s32result);
} // ENDIF- Trouble?
subchannel.cdsc_format = CDROM_MSF;
s32result = ioctl(devicehandle, CDROMSUBCHNL, &subchannel);
if((s32result == -1) || (errno != 0)) {
#ifdef VERBOSE_WARNINGS
PrintLog("CDVD driver: Error reading CD SubQ: %i:%s", errno, strerror(errno));
#endif /* VERBOSE_WARNINGS */
return(s32result);
} // ENDIF- Trouble?
if(subq != NULL) {
subq->mode = subchannel.cdsc_adr;
subq->ctrl = subchannel.cdsc_ctrl;
subq->trackNum = subchannel.cdsc_trk;
subq->trackIndex = subchannel.cdsc_ind;
subq->trackM = subchannel.cdsc_reladdr.msf.minute;
subq->trackS = subchannel.cdsc_reladdr.msf.second;
subq->trackF = subchannel.cdsc_reladdr.msf.frame;
subq->discM = subchannel.cdsc_absaddr.msf.minute;
subq->discS = subchannel.cdsc_absaddr.msf.second;
subq->discF = subchannel.cdsc_absaddr.msf.frame;
} // ENDIF- Did the caller want all this data?
return(0);
} // END CDVDreadSubQ()
s32 CDgetTN(cdvdTN *cdvdtn) {
#ifdef VERBOSE_FUNCTION
PrintLog("CDVD driver: CDgetTN()");
#endif /* VERBOSE_FUNCTION */
if(cdvdtn != NULL) {
cdvdtn->strack = cdheader.cdth_trk0;
cdvdtn->etrack = cdheader.cdth_trk1;
} // ENDIF- programmer actually WANTS this info?
return(0); // Call accomplished
} // END CDVDgetTN()
s32 CDgetTD(u8 newtrack, cdvdTD *cdvdtd) {
u8 j;
u16 k;
char temptime[3];
#ifdef VERBOSE_FUNCTION
PrintLog("CDVD driver: CDgetTD()");
#endif /* VERBOSE_FUNCTION */
j = newtrack;
if(j == CDROM_LEADOUT) j = 0;
if(j == 0) {
k = 27;
} else {
k = j * 10 + 37;
} // ENDIF- Where to start hunting for this number?
if(cdvdtd != NULL) {
cdvdtd->type = tocbuffer[j*10 + 30];
temptime[0] = BCDTOHEX(tocbuffer[k]);
temptime[1] = BCDTOHEX(tocbuffer[k + 1]);
temptime[2] = BCDTOHEX(tocbuffer[k + 2]);
cdvdtd->lsn = MSFtoLBA(temptime);
} // ENDIF- Does the caller REALLY want this data?
return(0); // Call accomplished
} // END CDVDgetTD()
s32 CALLBACK CDgetDiskType(s32 ioctldisktype) {
s32 offset;
s32 s32result;
int i;
u8 j;
int tempdisctype;
offset = 0;
errno = 0;
i = 0;
j = 0;
tempdisctype = CDVD_TYPE_UNKNOWN;
#ifdef VERBOSE_FUNCTION
PrintLog("CDVD driver: CDgetDiskType()");
#endif /* VERBOSE_FUNCTION */
s32result = CDreadTrack(16, CDVD_MODE_2352, cdtempbuffer);
if((s32result != 0) || (errno != 0)) {
return(-1);
} // ENDIF- Cannot read the CD's ISO9660 volume sector? Abort
disctype = CDVD_TYPE_DETCTCD;
switch(ioctldisktype) {
case CDS_AUDIO:
#ifdef VERBOSE_DISC_TYPE
PrintLog("CDVD driver: Detected CDDA Audio disc.");
#endif /* VERBOSE_DISC_TYPE */
tempdisctype = CDVD_TYPE_CDDA;
tocbuffer[0] = 0x01;
break;
case CDS_DATA_1:
case CDS_MIXED:
#ifdef VERBOSE_DISC_TYPE
PrintLog("CDVD driver: Detected CD disc.");
#endif /* VERBOSE_DISC_TYPE */
tocbuffer[0] = 0x41;
CDreadTrack(16, CDVD_MODE_2048, cdtempbuffer);
offset = CDgetBufferOffset();
i = 0;
while((*(playstationcdname + i) != 0) &&
(*(playstationcdname + i) == cdtempbuffer[offset + 8 + i])) i++;
if(*(playstationcdname + i) == 0) {
i = 0;
while((*(ps1name + i) != 0) &&
(*(ps1name + i) == cdtempbuffer[offset + 1024 + i])) i++;
if(*(ps1name + i) == 0) {
#ifdef VERBOSE_DISC_TYPE
PrintLog("CDVD driver: Detected Playstation CD disc.");
#endif /* VERBOSE_DISC_TYPE */
tempdisctype = CDVD_TYPE_PSCD;
} else {
#ifdef VERBOSE_DISC_TYPE
PrintLog("CDVD driver: Detected Playstation 2 CD disc.");
#endif /* VERBOSE_DISC_TYPE */
tempdisctype = CDVD_TYPE_PS2CD;
} // ENDIF- Did we find the CD ident? (For Playstation 1 CDs)
} else {
tempdisctype = CDVD_TYPE_UNKNOWN;
} // ENDIF- Did we find the Playstation name?
break;
default:
return(-1);
} // ENDSWITCH- What has ioctl disc type come up with?
// Collect TN data
cdheader.cdth_trk0 = 0;
cdheader.cdth_trk1 = 0;
s32result = ioctl(devicehandle, CDROMREADTOCHDR, &cdheader);
if((s32result == -1) || (errno != 0)) {
#ifdef VERBOSE_WARNINGS
PrintLog("CDVD driver: Error reading TN: (%i) %i:%s",
s32result, errno, strerror(errno));
#endif /* VERBOSE_WARNINGS */
cdheader.cdth_trk0 = 1;
cdheader.cdth_trk1 = 1;
} // ENDIF- Failed to read in track count? Assume 1 track.
#ifdef VERBOSE_DISC_INFO
PrintLog("CDVD driver: Track Number Range: %i-%i",
cdheader.cdth_trk0, cdheader.cdth_trk1);
#endif /* VERBOSE_DISC_INFO */
tocbuffer[2] = 0xA0;
tocbuffer[7] = HEXTOBCD(cdheader.cdth_trk0);
tocbuffer[12] = 0xA1;
tocbuffer[17] = HEXTOBCD(cdheader.cdth_trk1);
// Collect disc TD data
cdtrack.cdte_track = CDROM_LEADOUT;
cdtrack.cdte_format = CDROM_LBA;
s32result = ioctl(devicehandle, CDROMREADTOCENTRY, &cdtrack);
if((s32result == -1) || (errno != 0)) {
#ifdef VERBOSE_WARNINGS
PrintLog("CDVD driver: Error reading TD for disc: (%i) %i:%s",
s32result, errno, strerror(errno));
#endif /* VERBOSE_WARNINGS */
return(-1);
} // ENDIF- Trouble getting a track count?
LBAtoMSF(cdtrack.cdte_addr.lba, &tocbuffer[27]);
#ifdef VERBOSE_DISC_INFO
PrintLog("CDVD driver: Total Time: %i:%i",
tocbuffer[27], tocbuffer[28]);
#endif /* VERBOSE_DISC_INFO */
tocbuffer[27] = HEXTOBCD(tocbuffer[27]);
tocbuffer[28] = HEXTOBCD(tocbuffer[28]);
tocbuffer[29] = HEXTOBCD(tocbuffer[29]);
// Collect track TD data
for(j = cdheader.cdth_trk0; j <= cdheader.cdth_trk1; j++) {
cdtrack.cdte_track = j; // j-1?
cdtrack.cdte_format = CDROM_LBA;
s32result = ioctl(devicehandle, CDROMREADTOCENTRY, &cdtrack);
if((s32result == -1) || (errno != 0)) {
#ifdef VERBOSE_WARNINGS
PrintLog("CDVD driver: Error reading TD for track %i: (%i) %i:%s",
j, s32result, errno, strerror(errno));
#endif /* VERBOSE_WARNINGS */
// No more here...
} else {
LBAtoMSF(cdtrack.cdte_addr.lba, &tocbuffer[j*10 + 37]);
#ifdef VERBOSE_DISC_INFO
PrintLog("CDVD driver: Track %i: Data Mode %i Disc Start Time:%i:%i.%i\n",
j,
cdtrack.cdte_datamode,
tocbuffer[j*10+37],
tocbuffer[j*10+38],
tocbuffer[j*10+39]);
#endif /* VERBOSE_DISC_INFO */
tocbuffer[j*10 + 30] = cdtrack.cdte_datamode;
tocbuffer[j*10 + 32] = HEXTOBCD(j);
tocbuffer[j*10 + 37] = HEXTOBCD(tocbuffer[j*10 + 37]);
tocbuffer[j*10 + 38] = HEXTOBCD(tocbuffer[j*10 + 38]);
tocbuffer[j*10 + 39] = HEXTOBCD(tocbuffer[j*10 + 39]);
} // ENDIF- Trouble getting a track count?
} // NEXT j- Reading each track's info in turn
errno = 0;
disctype = tempdisctype; // Trigger the fact we have the info (finally)
return(disctype);
} // END CDVDgetDiskType()