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

775 lines
12 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 <windows.h>
#include <ddk/ntddcdrm.h> // IOCTL_CDROM...
#include <sys/types.h> // off64_t
#define CDVDdefs
#include "PS2Edefs.h"
#include "logfile.h"
#include "../convert.h" // MSFtoLBA(), HEXTOBCD()
#include "actualfile.h"
#include "device.h" // tocbuffer[], FinishCommand()
#include "CD.h"
int actualcdmode; // -1=ReadFile, 0=YellowMode2, 1=XAForm2, 2=CDDA
DWORD cdblocksize; // 2048 to 2352
int cdoffset; // 0, 8, 16, or 24
int cdmode;
void InitCDInfo() {
actualcdmode = -1;
cdblocksize = 2048;
cdmode = -1;
} // END InitCDInfo()
s32 CDreadTrackPass(u32 lsn, int mode, u8 *buffer) {
RAW_READ_INFO rawinfo;
BOOL boolresult;
DWORD byteswritten;
DWORD errcode;
LARGE_INTEGER targetpos;
int i;
if((actualcdmode < -1) || (actualcdmode > 2)) return(-1);
#ifdef VERBOSE_FUNCTION_DEVICE
PrintLog("CDVDlinuz CD: CDreadTrack(%llu, %i)", lsn, actualcdmode);
#endif /* VERBOSE_FUNCTION_DEVICE */
if(actualcdmode >= 0) {
rawinfo.DiskOffset.QuadPart = lsn * 2048; // Yes, 2048.
rawinfo.SectorCount = 1;
rawinfo.TrackMode = actualcdmode;
boolresult = DeviceIoControl(devicehandle,
IOCTL_CDROM_RAW_READ,
&rawinfo,
sizeof(RAW_READ_INFO),
buffer,
2352,
&byteswritten,
&waitevent);
errcode = FinishCommand(boolresult);
if(errcode != 0) {
#ifdef VERBOSE_WARNING_DEVICE
PrintLog("CDVDlinuz CD: Couldn't read a sector raw!");
PrintError("CDVDlinuz CD", errcode);
#endif /* VERBOSE_WARNING_DEVICE */
return(-1);
} // ENDIF- Couldn't read a raw sector? Maybe not a CD.
} else {
targetpos.QuadPart = lsn * 2048;
waitevent.Offset = targetpos.LowPart;
waitevent.OffsetHigh = targetpos.HighPart;
boolresult = ReadFile(devicehandle,
buffer + 24,
2048,
&byteswritten,
&waitevent);
errcode = FinishCommand(boolresult);
if(errcode != 0) {
#ifdef VERBOSE_WARNING_DEVICE
PrintLog("CDVDlinuz CD: Couldn't read a cooked sector!");
PrintError("CDVDlinuz CD", errcode);
#endif /* VERBOSE_WARNING_DEVICE */
return(-1);
} // ENDIF- Trouble with seek? Report it.
for(i = 0; i < 24; i++) *(buffer + i) = 0;
for(i = 24 + 2048; i < 2352; i++) *(buffer + i) = 0;
} // ENDIF- Could we read a raw sector? Read another one!
if(boolresult == FALSE) {
boolresult = GetOverlappedResult(devicehandle,
&waitevent,
&byteswritten,
FALSE);
} // ENDIF- Did the initial call not complete? Get byteswritten for
// the completed call.
if(byteswritten < 2048) {
#ifdef VERBOSE_WARNING_DEVICE
errcode = GetLastError();
PrintLog("CDVDlinuz CD: Short block! only got %u out of %u bytes",
byteswritten, cdblocksize);
PrintError("CDVDlinuz CD", errcode);
#endif /* VERBOSE_WARNING_DEVICE */
return(-1);
} // ENDIF- Couldn't read a raw sector? Maybe not a CD.
cdmode = mode;
cdblocksize = byteswritten;
return(0);
} // END CDreadTrackPass()
s32 CDreadTrack(u32 lsn, int mode, u8 *buffer) {
int retval;
int lastmode;
int i;
retval = CDreadTrackPass(lsn, mode, buffer);
if(retval >= 0) return(retval);
#ifdef VERBOSE_WARNING_DEVICE
PrintLog("CDVDlinuz CD: Same mode doesn't work. Scanning...");
#endif /* VERBOSE_WARNING_DEVICE */
lastmode = actualcdmode;
actualcdmode = 2;
while(actualcdmode >= -1) {
retval = CDreadTrackPass(lsn, mode, buffer);
if(retval >= 0) return(retval);
actualcdmode--;
} // ENDWHILE- Searching each mode for a way to read the sector
actualcdmode = lastmode; // None worked? Go back to first mode.
#ifdef VERBOSE_WARNING_DEVICE
PrintLog("CDVDlinuz CD: No modes work. Failing sector!");
#endif /* VERBOSE_WARNING_DEVICE */
for(i = 0; i < 2352; i++) *(buffer + i) = 0;
return(-1);
} // END CDreadTrack()
s32 CDgetBufferOffset() {
#ifdef VERBOSE_FUNCTION_DEVICE
PrintLog("CDVD CD: CDgetBufferOffset()");
#endif /* VERBOSE_FUNCTION_DEVICE */
// Send a whole CDDA record in?
if((actualcdmode == CDDA) && (cdmode == CDVD_MODE_2352)) return(0);
// Otherwise, send the start of the data block in...
return(cdoffset);
} // END CDgetBufferOffset()
s32 CDreadSubQ() {
return(-1);
} // END CDreadSubQ()
s32 CDgetTN(cdvdTN *cdvdtn) {
cdvdtn->strack = BCDTOHEX(tocbuffer[7]);
cdvdtn->etrack = BCDTOHEX(tocbuffer[17]);
return(0);
} // END CDgetTN()
s32 CDgetTD(u8 newtrack, cdvdTD *cdvdtd) {
u8 actualtrack;
int pos;
char temptime[3];
#ifdef VERBOSE_FUNCTION_DEVICE
PrintLog("CDVDlinuz CD: CDgetTD()");
#endif /* VERBOSE_FUNCTION_DEVICE */
actualtrack = newtrack;
if(actualtrack == 0xaa) actualtrack = 0;
if(actualtrack == 0) {
cdvdtd->type = 0;
temptime[0] = BCDTOHEX(tocbuffer[27]);
temptime[1] = BCDTOHEX(tocbuffer[28]);
temptime[2] = BCDTOHEX(tocbuffer[29]);
cdvdtd->lsn = MSFtoLBA(temptime);
} else {
pos = actualtrack * 10;
pos += 30;
cdvdtd->type = tocbuffer[pos];
temptime[0] = BCDTOHEX(tocbuffer[pos + 7]);
temptime[1] = BCDTOHEX(tocbuffer[pos + 8]);
temptime[2] = BCDTOHEX(tocbuffer[pos + 9]);
cdvdtd->lsn = MSFtoLBA(temptime);
} // ENDIF- Whole disc? (or single track?)
return(0);
} // END CDgetTD()
s32 CDgetDiskType() {
CDROM_TOC cdinfo;
BOOL boolresult;
int retval;
DWORD byteswritten;
DWORD errcode;
u8 iso9660name[] = "CD001\0";
u8 playstationname[] = "PLAYSTATION\0";
u8 ps1name[] = "CD-XA001\0";
u8 tempbuffer[2448];
int tempdisctype;
int i;
int pos;
unsigned long volumesize;
#ifdef VERBOSE_FUNCTION_DEVICE
PrintLog("CDVDlinuz CD: CDgetDiskType()");
#endif /* VERBOSE_FUNCTION_DEVICE */
tempdisctype = CDVD_TYPE_UNKNOWN;
actualcdmode = 2;
retval = CDreadTrack(16, CDVD_MODE_2048, tempbuffer);
if(retval < 0) {
disctype = tempdisctype;
return(-1);
} // ENDIF- Couldn't read the directory sector? Abort.
disctype = CDVD_TYPE_DETCTCD;
tempdisctype = CDVD_TYPE_CDDA;
cdoffset = 0;
i = 0;
while((cdoffset <= 24) && (iso9660name[i] != 0)) {
i = 0;
while((iso9660name[i] != 0) &&
(iso9660name[i] == tempbuffer[cdoffset + 1 + i])) i++;
if(iso9660name[i] != 0) cdoffset += 8;
} // ENDWHILE- Trying to find a working offset for a ISO9660 record.
if(iso9660name[i] != 0) {
#ifdef VERBOSE_DISC_TYPE
PrintLog("Detected a CDDA (Music CD).");
#endif /* VERBOSE_DISC_TYPE */
tempdisctype = CDVD_TYPE_CDDA;
tocbuffer[0] = 0x01;
} else {
tocbuffer[0] = 0x41;
i = 0;
while((playstationname[i] != 0) &&
(playstationname[i] == tempbuffer[cdoffset + 8 + i])) i++;
if(playstationname[i] != 0) {
#ifdef VERBOSE_DISC_TYPE
PrintLog("Detected a non-Playstation CD.");
#endif /* VERBOSE_DISC_TYPE */
tempdisctype = CDVD_TYPE_UNKNOWN;
} else {
i = 0;
while((ps1name[i] != 0) &&
(ps1name[i] == tempbuffer[cdoffset + 1024 + i])) i++;
if(ps1name[i] != 0) {
#ifdef VERBOSE_DISC_TYPE
PrintLog("Detected a Playstation 2 CD.");
#endif /* VERBOSE_DISC_TYPE */
tempdisctype = CDVD_TYPE_PS2CD;
} else {
#ifdef VERBOSE_DISC_TYPE
PrintLog("Detected a Playstation CD.");
#endif /* VERBOSE_DISC_TYPE */
tempdisctype = CDVD_TYPE_PSCD;
} // ENDIF- Is this not a PlayStation Disc?
} // ENDIF- Is this not a PlayStation Disc?
} // ENDIF- Is this not an ISO9660 CD? (a CDDA, in other words?)
// Build the Fake TOC
tocbuffer[2] = 0xA0;
tocbuffer[7] = HEXTOBCD(1); // Starting Track
tocbuffer[12] = 0xA1;
tocbuffer[17] = HEXTOBCD(1); // Ending Track
volumesize = tempbuffer[84]; // Volume size (big endian)
volumesize *= 256;
volumesize += tempbuffer[85];
volumesize *= 256;
volumesize += tempbuffer[86];
volumesize *= 256;
volumesize += tempbuffer[87];
#ifdef VERBOSE_DISC_INFO
if(tempdisctype != CDVD_TYPE_CDDA) {
PrintLog("CDVDlinuz CD: ISO9660 size %llu", volumesize);
} // ENDIF- Data CD? Print size in blocks.
#endif /* VERBOSE_DISC_INFO */
LBAtoMSF(volumesize, &tocbuffer[27]);
tocbuffer[27] = HEXTOBCD(tocbuffer[27]);
tocbuffer[28] = HEXTOBCD(tocbuffer[28]);
tocbuffer[29] = HEXTOBCD(tocbuffer[29]);
tocbuffer[40] = 0x02; // Data Mode
tocbuffer[42] = 0x01; // Track #
LBAtoMSF(0, &tocbuffer[47]);
tocbuffer[47] = HEXTOBCD(tocbuffer[47]);
tocbuffer[48] = HEXTOBCD(tocbuffer[48]);
tocbuffer[49] = HEXTOBCD(tocbuffer[49]);
// Can we get the REAL TOC?
boolresult = DeviceIoControl(devicehandle,
IOCTL_CDROM_READ_TOC,
NULL,
0,
&cdinfo,
sizeof(CDROM_TOC),
&byteswritten,
NULL);
if(boolresult == FALSE) {
#ifdef VERBOSE_WARNING_DEVICE
errcode = GetLastError();
PrintLog("CDVDlinuz CD: Can't get TOC!");
PrintError("CDVDlinuz CD", errcode);
#endif /* VERBOSE_WARNING_DEVICE */
disctype = tempdisctype;
return(disctype);
} // ENDIF- Can't read the TOC? Accept the fake TOC then.
// Fill in the pieces of the REAL TOC.
#ifdef VERBOSE_DISC_INFO
PrintLog("CDVDlinuz CD: TOC First Track: %u Last Track: %u",
cdinfo.FirstTrack, cdinfo.LastTrack);
#endif /* VERBOSE_DISC_INFO */
tocbuffer[7] = HEXTOBCD(cdinfo.FirstTrack);
tocbuffer[17] = HEXTOBCD(cdinfo.LastTrack);
// for(i = cdinfo.FirstTrack; i <= cdinfo.LastTrack; i++) {
for(i = 0; i <= cdinfo.LastTrack - cdinfo.FirstTrack; i++) {
#ifdef VERBOSE_DISC_INFO
PrintLog("CDVDlinuz CD: TOC Track %u Disc Size: %u:%u.%u Data Mode %u",
cdinfo.TrackData[i].TrackNumber,
cdinfo.TrackData[i].Address[1] * 1,
cdinfo.TrackData[i].Address[2] * 1,
cdinfo.TrackData[i].Address[3] * 1,
cdinfo.TrackData[i].Control * 1);
#endif /* VERBOSE_DISC_INFO */
pos = i * 10 + 40;
tocbuffer[pos] = cdinfo.TrackData[i].Control;
tocbuffer[pos + 2] = HEXTOBCD(i + 1);
tocbuffer[pos + 7] = HEXTOBCD(cdinfo.TrackData[i].Address[1]);
tocbuffer[pos + 8] = HEXTOBCD(cdinfo.TrackData[i].Address[2]);
tocbuffer[pos + 9] = HEXTOBCD(cdinfo.TrackData[i].Address[3]);
} // NEXT i- Transferring Track data to the PS2 TOC
#ifdef VERBOSE_DISC_INFO
PrintLog("CDVDlinuz CD: TOC Disc Size: %u:%u.%u",
cdinfo.TrackData[i].Address[1] * 1,
cdinfo.TrackData[i].Address[2] * 1,
cdinfo.TrackData[i].Address[3] * 1);
#endif /* VERBOSE_DISC_INFO */
tocbuffer[27] = HEXTOBCD(cdinfo.TrackData[i].Address[1]);
tocbuffer[28] = HEXTOBCD(cdinfo.TrackData[i].Address[2]);
tocbuffer[29] = HEXTOBCD(cdinfo.TrackData[i].Address[3]);
disctype = tempdisctype;
return(disctype);
} // END CDgetDiskType()