/* DVD.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 #include // NULL #include // sprintf() #include // strerror(), memset(), memcpy() #include // open() #include // ioctl() #include // open() #include // lseek(), open() #include // close(), lseek(), (sleep()) #include // CD/DVD based ioctl() and defines. #include "logfile.h" #include "device.h" #include "DVD.h" #include "../PS2Etypes.h" // u8, u32 // Constants u8 *playstationname = "PLAYSTATION\0"; // DVD storage structures (see linux/cdrom.h for details) dvd_struct dvdphysical; dvd_struct dvdcopyright[DVD_LAYERS]; dvd_struct dvdbca; dvd_struct dvdmanufact[DVD_LAYERS]; u32 dvdlastlsn; u8 dvdtempbuffer[2064]; // Internal Functions void InitDVDSectorInfo() { dvdlastlsn = 0xffffffff; } // END InitSectorInfo(); void HexDump(u8 *strptr, u8 count) { int i; u8 ch[2]; char hexline[81]; int hexlinepos; ch[1] = 0; if(count == 0) count = 16; if((count < 1) || (count > 16)) return; hexlinepos = 0; hexlinepos += sprintf(&hexline[hexlinepos], "CDVD driver: "); for(i = 0; i < count; i++) { hexlinepos += sprintf(&hexline[hexlinepos], "%.2x ", (*(strptr + i)) * 1); } // NEXT i- printing each new Hex number for(i = 0; i < count; i++) { if((*(strptr + i) < 32) || (*(strptr + i) > 127)) { hexlinepos += sprintf(&hexline[hexlinepos], "."); } else { ch[0] = *(strptr + i); hexlinepos += sprintf(&hexline[hexlinepos], "%s", ch); } // ENDIF- Is this an unprintable character? } // NEXT i- printing each new character PrintLog(hexline); } // ENDIF HexDump() //// DVD Structure Functions s32 DVDreadPhysical() { s32 s32result; u8 i; errno = 0; #ifdef VERBOSE_FUNCTION PrintLog("CDVD driver: DVDreadPhysical()\n"); #endif /* VERBOSE_FUNCTION */ memset(&dvdphysical, 0, sizeof(dvd_struct)); dvdphysical.type = DVD_STRUCT_PHYSICAL; i = DVD_LAYERS; while(i > 0) { i--; dvdphysical.physical.layer_num = i; errno = 0; s32result = ioctl(devicehandle, DVD_READ_STRUCT, &dvdphysical); } // ENDWHILE- reading in all physical layers... if((s32result == -1) || (errno != 0)) { dvdphysical.type = 0xFF; #ifdef VERBOSE_WARNINGS PrintLog("CDVD driver: Error getting Physical structure: (%i) %i:%s", s32result, errno, strerror(errno)); #endif /* VERBOSE_WARNINGS */ return(-1); } // ENDIF- Problem with reading Layer 0 of the physical data? Abort i = 3; while((i > 0) && (dvdphysical.physical.layer[i].end_sector == 0)) i--; dvdphysical.physical.layer_num = i; #ifdef VERBOSE_DISC_INFO PrintLog("CDVD driver: Physical Characteristics"); PrintLog("CDVD driver: Number of Layers: %i", (s32) dvdphysical.physical.layer_num + 1); for(i = 0; i <= dvdphysical.physical.layer_num; i++) { PrintLog("CDVD driver: Layer Number %i", i); switch(dvdphysical.physical.layer[i].book_type) { case 0: PrintLog("CDVD driver: Book Type: DVD-ROM"); break; case 1: PrintLog("CDVD driver: Book Type: DVD-RAM"); break; case 2: PrintLog("CDVD driver: Book Type: DVD-R"); break; case 3: PrintLog("CDVD driver: Book Type: DVD-RW"); break; case 9: PrintLog("CDVD driver: Book Type: DVD+RW"); break; default: PrintLog("CDVD driver: Book Type: Unknown (%i)", dvdphysical.physical.layer[i].book_type); break; } // ENDSWITCH- Displaying the Book Type PrintLog("CDVD driver: Book Version %i", dvdphysical.physical.layer[i].book_version); switch(dvdphysical.physical.layer[i].min_rate) { case 0: PrintLog("CDVD driver: Use Minimum Rate for: DVD-ROM"); break; case 1: PrintLog("CDVD driver: Use Minimum Rate for: DVD-RAM"); break; case 2: PrintLog("CDVD driver: Use Minimum Rate for: DVD-R"); break; case 3: PrintLog("CDVD driver: Use Minimum Rate for: DVD-RW"); break; case 9: PrintLog("CDVD driver: Use Minimum Rate for: DVD+RW"); break; default: PrintLog("CDVD driver: Use Minimum Rate for: Unknown (%i)", dvdphysical.physical.layer[i].min_rate); break; } // ENDSWITCH- Displaying the Minimum (Spin?) Rate switch(dvdphysical.physical.layer[i].disc_size) { case 0: PrintLog("CDVD driver: Physical Disk Size: 120mm"); break; case 1: PrintLog("CDVD driver: Physical Disk Size: 80mm"); break; default: PrintLog("CDVD driver: Physical Disk Size: Unknown (%i)", dvdphysical.physical.layer[i].disc_size); break; } // ENDSWITCH- What's the Disk Size? switch(dvdphysical.physical.layer[i].layer_type) { case 1: PrintLog("CDVD driver: Layer Type: Read-Only"); break; case 2: PrintLog("CDVD driver: Layer Type: Recordable"); break; case 4: PrintLog("CDVD driver: Layer Type: Rewritable"); break; default: PrintLog("CDVD driver: Layer Type: Unknown (%i)", dvdphysical.physical.layer[i].layer_type); break; } // ENDSWITCH- Displaying the Layer Type switch(dvdphysical.physical.layer[i].track_path) { case 0: PrintLog("CDVD driver: Track Path: PTP"); break; case 1: PrintLog("CDVD driver: Track Path: OTP"); break; default: PrintLog("CDVD driver: Track Path: Unknown (%i)", dvdphysical.physical.layer[i].track_path); break; } // ENDSWITCH- What's Track Path Layout? // PrintLog("CDVD driver: Disc Size %i Layer Type %i Track Path %i Nlayers %i", // dvdphysical.physical.layer[i].nlayers); switch(dvdphysical.physical.layer[i].track_density) { case 0: PrintLog("CDVD driver: Track Density: .74 m/track"); break; case 1: PrintLog("CDVD driver: Track Density: .8 m/track"); break; case 2: PrintLog("CDVD driver: Track Density: .615 m/track"); break; default: PrintLog("CDVD driver: Track Density: Unknown (%i)", dvdphysical.physical.layer[i].track_density); break; } // ENDSWITCH- Displaying the Track Density switch(dvdphysical.physical.layer[i].linear_density) { case 0: PrintLog("CDVD driver: Linear Density: .267 m/bit"); break; case 1: PrintLog("CDVD driver: Linear Density: .293 m/bit"); break; case 2: PrintLog("CDVD driver: Linear Density: .409 to .435 m/bit"); break; case 4: PrintLog("CDVD driver: Linear Density: .280 to .291 m/bit"); break; case 8: PrintLog("CDVD driver: Linear Density: .353 m/bit"); break; default: PrintLog("CDVD driver: Linear Density: Unknown (%i)", dvdphysical.physical.layer[i].linear_density); break; } // ENDSWITCH- Displaying the Linear Density if(dvdphysical.physical.layer[i].start_sector == 0x30000) { PrintLog("CDVD driver: Starting Sector: %lu (DVD-ROM, DVD-R, DVD-RW)", dvdphysical.physical.layer[i].start_sector); } else if(dvdphysical.physical.layer[i].start_sector == 0x31000) { PrintLog("CDVD driver: Starting Sector: %lu (DVD-RAM, DVD+RW)", dvdphysical.physical.layer[i].start_sector); } else { PrintLog("CDVD driver: Starting Sector: %lu", dvdphysical.physical.layer[i].start_sector); } // ENDLONGIF- What does the starting sector tell us? PrintLog("CDVD driver: End of Layer 0: %lu", dvdphysical.physical.layer[i].end_sector_l0); PrintLog("CDVD driver: Ending Sector: %lu", dvdphysical.physical.layer[i].end_sector); if(dvdphysical.physical.layer[i].bca != 0) PrintLog("CDVD driver: BCA data present"); } // NEXT i- Work our way through each layer... #endif /* VERBOSE_DISC_INFO */ return(0); // Success. Physical data stored for perusal. } // END DVDreadPhysical() s32 DVDreadCopyright() { s32 s32result; u8 i; int successflag; #ifdef VERBOSE_FUNCTION PrintLog("CDVD driver: DVDreadCopyright()"); #endif /* VERBOSE_FUNCTION */ successflag = 0; for(i = 0; i <= dvdphysical.physical.layer_num; i++) { memset(&dvdcopyright[i], 0, sizeof(dvd_struct)); dvdcopyright[i].type = DVD_STRUCT_COPYRIGHT; dvdcopyright[i].copyright.layer_num = i; errno = 0; s32result = ioctl(devicehandle, DVD_READ_STRUCT, &dvdcopyright[i]); if(s32result == 0) { successflag = 1; } else { dvdcopyright[i].type = 0xFF; } // ENDIF- } // NEXT i- Getting copyright data for every known layer if(successflag == 0) { #ifdef VERBOSE_WARNINGS PrintLog("CDVD driver: Error getting Copyright info: (%i) %i:%s", s32result, errno, strerror(errno)); #endif /* VERBOSE_WARNINGS */ return(-1); } // ENDIF- Problem with read of physical data? #ifdef VERBOSE_DISC_INFO PrintLog("CDVD driver: Copyright Information\n"); for(i = 0; i <= dvdphysical.physical.layer_num; i++) { if(dvdcopyright[i].type != 0xFF) { PrintLog("CDVD driver: Layer Number %i CPST %i RMI %i", dvdcopyright[i].copyright.layer_num, dvdcopyright[i].copyright.cpst, dvdcopyright[i].copyright.rmi); } // ENDIF- Were we successful reading this one? } // NEXT i- Printing out all copyright info found... #endif /* VERBOSE_DISC_INFO */ errno = 0; return(0); // Success. Copyright data stored for perusal. } // END DVDreadCopyright() s32 DVDreadBCA() { s32 s32result; int i; i = 0; errno = 0; #ifdef VERBOSE_FUNCTION PrintLog("CDVD driver: DVDreadBCA()"); #endif /* VERBOSE_FUNCTION */ memset(&dvdbca, 0, sizeof(dvd_struct)); dvdbca.type = DVD_STRUCT_BCA; s32result = ioctl(devicehandle, DVD_READ_STRUCT, &dvdbca); if((s32result == -1) || (errno != 0)) { dvdbca.type = 0xFF; #ifdef VERBOSE_WARNINGS PrintLog("CDVD driver: Error getting BCA: (%i) %i:%s", s32result, errno, strerror(errno)); #endif /* VERBOSE_WARNINGS */ return(-1); } // ENDIF- Problem with read of physical data? #ifdef VERBOSE_DISC_INFO PrintLog("CDVD driver: BCA Length %i Value:", dvdbca.bca.len); for(i = 0; i < 188-15; i += 16) { HexDump(dvdbca.bca.value+i, 16); } // NEXT i- dumping whole key data #endif /* VERBOSE_DISC_INFO */ return(0); // Success. BCA data stored for perusal. } // END DVDreadBCA() s32 DVDreadManufact() { s32 s32result; u8 i; int successflag; int j; #ifdef VERBOSE_FUNCTION PrintLog("CDVD driver: DVDreadManufact()"); #endif /* VERBOSE_FUNCTION */ j = 0; successflag = 0; for(i = 0; i <= dvdphysical.physical.layer_num; i++) { memset(&dvdmanufact[i], 0, sizeof(dvd_struct)); dvdmanufact[i].type = DVD_STRUCT_MANUFACT; dvdmanufact[i].manufact.layer_num = i; errno = 0; s32result = ioctl(devicehandle, DVD_READ_STRUCT, &dvdmanufact[i]); if((s32result != 0) || (errno != 0)) { dvdmanufact[i].type = 0xFF; } else { successflag = 1; } // ENDIF- Did we fail to read in some manufacturer data? } // NEXT i- Collecting manufacturer data from all layers if(successflag == 0) { #ifdef VERBOSE_WARNINGS PrintLog("CDVD driver: Error getting Manufact: (%i) %i:%s", s32result, errno, strerror(errno)); #endif /* VERBOSE_WARNINGS */ return(-1); } // ENDIF- Problem with read of physical data? #ifdef VERBOSE_DISC_INFO PrintLog("CDVD driver: Manufact Data"); for(i = 0; i <= dvdphysical.physical.layer_num; i++) { if(dvdmanufact[i].type != 0xFF) { PrintLog("CDVD driver: Layer %i Length %i Value:", dvdmanufact[i].manufact.layer_num, dvdmanufact[i].manufact.len); for(j = 0; j < 128-15; j += 16) { HexDump(dvdmanufact[i].manufact.value+j, 16); } // NEXT j- dumping whole key data } // ENDIF- Do we have data at this layer? } // NEXT i- Running through all the layers #endif /* VERBOSE_DISC_INFO */ errno = 0; return(0); // Success. Manufacturer's data stored for perusal. } // END DVDreadManufact() // External Functions // Function Calls from CDVD.c void InitDVDInfo() { int j; dvdphysical.type = 0xFF; // Using for empty=0xff, full!=0xff test dvdbca.type = 0xFF; for(j = 0; j < DVD_LAYERS; j++) { dvdcopyright[j].type = 0xFF; dvdmanufact[j].type = 0xFF; } // NEXT j- Zeroing each layer of data InitDVDSectorInfo(); } // END InitDiscType() s32 DVDreadTrack(u32 lsn, int mode, u8 *buffer) { s32 s32result; off64_t offsettarget; off64_t offsetresult; errno = 0; s32result = 0; offsetresult = 0; #ifdef VERBOSE_FUNCTION PrintLog("CDVD driver: DVDreadTrack(%i)", lsn); #endif /* VERBOSE_FUNCTION */ if(lsn != dvdlastlsn + 1) { offsettarget = lsn; offsettarget *= 2048; errno = 4; while(errno == 4) { errno = 0; offsetresult = lseek64(devicehandle, offsettarget, SEEK_SET); } // ENDWHILE- waiting for the system interruptions to cease. if((offsetresult < 0) || (errno != 0)) { #ifdef VERBOSE_WARNINGS PrintLog("CDVD driver: Error on seek: %i:%s", errno, strerror(errno)); #endif /* VERBOSE_WARNINGS */ InitDVDSectorInfo(); return(-1); } // ENDIF- trouble with seek? Reset pointer and abort } // ENDIF- Do we have to seek a new position to read? errno = 4; while(errno == 4) { errno = 0; s32result = read(devicehandle, buffer, 2048); } // ENDWHILE- waiting for the system interruptions to cease. if((s32result != 2048) || (errno != 0)) { #ifdef VERBOSE_WARNINGS PrintLog("CDVD driver: DVD Short Block, Size: %i", s32result); PrintLog("CDVD driver: Error: %i:%s", errno, strerror(errno)); #endif /* VERBOSE_WARNINGS */ InitDVDSectorInfo(); return(-1); } // ENDIF- Trouble reading the data? Reset pointer and abort dvdlastlsn = lsn; return(0); // Call accomplished } // END DVDreadTrack() s32 DVDgetTN(cdvdTN *cdvdtn) { #ifdef VERBOSE_FUNCTION PrintLog("CDVD driver: CDVDgetTN()"); #endif /* VERBOSE_FUNCTION */ // Going to treat this as one large track for now. if(cdvdtn != NULL) { cdvdtn->strack = 1; cdvdtn->etrack = 1; } // ENDIF- programmer actually WANTS this info? return(0); // Call accomplished } // END DVDgetTN() s32 DVDgetTD(u8 newtrack, cdvdTD *cdvdtd) { #ifdef VERBOSE_FUNCTION PrintLog("CDVD driver: CDVDgetTD()"); #endif /* VERBOSE_FUNCTION */ if((newtrack >= 2) && (newtrack != CDROM_LEADOUT)) return(-1); if(cdvdtd != NULL) { cdvdtd->lsn = dvdphysical.physical.layer[0].end_sector - dvdphysical.physical.layer[0].start_sector + 1; cdvdtd->type = CDVD_MODE_2048; } // ENDIF- Does the caller REALLY want this data? return(0); // Call accomplished } // END DVDgetTD() s32 DVDgetDiskType(s32 ioctldisktype) { s32 s32result; int i; s32 tempdisctype; errno = 0; s32result = 0; i = 0; tempdisctype = CDVD_TYPE_UNKNOWN; #ifdef VERBOSE_FUNCTION PrintLog("CDVD driver: DVDgetDiskType()"); #endif /* VERBOSE_FUNCTION */ if((ioctldisktype != CDS_DATA_1) && (ioctldisktype != CDS_MIXED)) { return(-1); } // ENDIF- Not a data disc we know of? Abort then s32result = DVDreadPhysical(); if((s32result != 0) || (errno != 0)) { return(-1); } // ENDIF- Error reading the DVD physical structure? Not a DVD after all. if(dvdphysical.physical.layer[0].end_sector >= (2048*1024)) { #ifdef VERBOSE_DISC_TYPE PrintLog("CDVD driver: DVD Found (Dual-Sided)"); #endif /* VERBOSE_DISC_TYPE */ disctype = CDVD_TYPE_DETCTDVDD; } else { #ifdef VERBOSE_DISC_TYPE PrintLog("CDVD driver: DVD Found (Single-Sided)"); #endif /* VERBOSE_DISC_TYPE */ disctype = CDVD_TYPE_DETCTDVDS; } // ENDIF- Definitely a DVD. Size Test? // Read in the rest of the structures... DVDreadCopyright(); DVDreadBCA(); DVDreadManufact(); // Test for "Playstation" header s32result = DVDreadTrack(16, CDVD_MODE_2048, dvdtempbuffer); if(s32result != 0) { return(-1); } else { i = 0; while((*(playstationname + i) != 0) && (*(playstationname + i) == dvdtempbuffer[8 + i])) { i++; } // ENDWHILE- Checking each letter of PLAYSTATION name for a match if(*(playstationname + i) == 0) { #ifdef VERBOSE_DISC_TYPE PrintLog("CDVD driver: Detected Playstation 2 DVD"); #endif /* VERBOSE_DISC_TYPE */ tempdisctype = CDVD_TYPE_PS2DVD; } else { #ifdef VERBOSE_DISC_TYPE PrintLog("CDVD driver: Guessing it's a Video DVD"); #endif /* VERBOSE_DISC_TYPE */ tempdisctype = CDVD_TYPE_DVDV; } // ENDIF- Did we find the Playstation name? } // ENDIF- Error reading disc volume information? Invalidate Disc if(dvdphysical.physical.layer[0].end_sector >= (2048*1024)) { tocbuffer[0] = 0x24; // Dual-Sided DVD tocbuffer[4] = 0x41; tocbuffer[5] = 0x95; } else { tocbuffer[0] = 0x04; // Single-Sided DVD tocbuffer[4] = 0x86; tocbuffer[5] = 0x72; } // ENDIF- Are there too many sectors for a single-sided disc? tocbuffer[1] = 0x02; tocbuffer[2] = 0xF2; tocbuffer[3] = 0x00; tocbuffer[16] = 0x00; tocbuffer[17] = 0x03; tocbuffer[18] = 0x00; tocbuffer[19] = 0x00; disctype = tempdisctype; // Triggers the fact the other info is available return(disctype); } // END DVDgetDiskType()