/* 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 #include // IOCTL_CDROM... #include // 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()