Path: blob/master/psx/mednadisc/cdrom/CDAccess_Physical.cpp
2 views
/* Mednafen - Multi-system Emulator1*2* This program is free software; you can redistribute it and/or modify3* it under the terms of the GNU General Public License as published by4* the Free Software Foundation; either version 2 of the License, or5* (at your option) any later version.6*7* This program is distributed in the hope that it will be useful,8* but WITHOUT ANY WARRANTY; without even the implied warranty of9* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the10* GNU General Public License for more details.11*12* You should have received a copy of the GNU General Public License13* along with this program; if not, write to the Free Software14* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA15*/1617#define EXTERNAL_LIBCDIO_CONFIG_H 11819#include "../mednafen.h"20#include "../general.h"2122#include "CDAccess.h"23#include "CDAccess_Physical.h"2425#include <time.h>26#include <stdlib.h>27#include <string>28#include <vector>2930#include <cdio/cdio.h>31#include <cdio/mmc.h>32#include <cdio/logging.h>3334#if LIBCDIO_VERSION_NUM >= 8335#include <cdio/mmc_cmds.h>36#endif3738using namespace CDUtility;3940static bool Logging = false;41static std::string LogMessage;42static void LogHandler(cdio_log_level_t level, const char message[])43{44if(!Logging)45return;4647try48{49if(LogMessage.size() > 0)50LogMessage.append(" - ");5152LogMessage.append(message);53}54catch(...) // Don't throw exceptions through libcdio's code.55{56LogMessage.clear();57}58}5960static INLINE void StartLogging(void)61{62Logging = true;63LogMessage.clear();64}6566static INLINE void ClearLogging(void)67{68LogMessage.clear();69}7071static INLINE std::string StopLogging(void)72{73std::string ret = LogMessage;7475Logging = false;76LogMessage.clear();7778return(ret);79}8081void CDAccess_Physical::DetermineFeatures(void)82{83uint8 buf[256];8485mmc_cdb_t cdb = {{0, }};8687CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_MODE_SENSE_10);8889memset(buf, 0, sizeof(buf));9091cdb.field[2] = 0x2A;9293cdb.field[7] = sizeof(buf) >> 8;94cdb.field[8] = sizeof(buf) & 0xFF;9596StartLogging();97if(mmc_run_cmd ((CdIo *)p_cdio, MMC_TIMEOUT_DEFAULT,98&cdb,99SCSI_MMC_DATA_READ,100sizeof(buf),101buf))102{103throw(MDFN_Error(0, _("MMC [MODE SENSE 10] command failed: %s"), StopLogging().c_str()));104}105else106{107const uint8 *pd = &buf[8];108109StopLogging();110111if(pd[0] != 0x2A || pd[1] < 0x14)112{113throw(MDFN_Error(0, _("MMC [MODE SENSE 10] command returned bogus data for mode page 0x2A.")));114}115116if(!(pd[4] & 0x10))117{118throw(MDFN_Error(0, _("Drive does not support reading Mode 2 Form 1 sectors.")));119}120121if(!(pd[4] & 0x20))122{123throw(MDFN_Error(0, _("Drive does not support reading Mode 2 Form 2 sectors.")));124}125126if(!(pd[5] & 0x01))127{128throw(MDFN_Error(0, _("Reading CD-DA sectors via \"READ CD\" is not supported.")));129}130131if(!(pd[5] & 0x02))132{133throw(MDFN_Error(0, _("Read CD-DA sectors via \"READ CD\" are not positionally-accurate.")));134}135136if(!(pd[5] & 0x04))137{138throw(MDFN_Error(0, _("Reading raw subchannel data via \"READ CD\" is not supported.")));139}140}141}142143void CDAccess_Physical::PreventAllowMediumRemoval(bool prevent)144{145#if 0146mmc_cdb_t cdb = {{0, }};147uint8 buf[8];148149cdb.field[0] = 0x1E;150cdb.field[1] = 0x00;151cdb.field[2] = 0x00;152cdb.field[3] = 0x00;153cdb.field[4] = 0x00; //prevent;154cdb.field[5] = 0x00;155156printf("%d\n", mmc_run_cmd_len (p_cdio, MMC_TIMEOUT_DEFAULT,157&cdb, 6,158SCSI_MMC_DATA_READ, 0, buf));159assert(0);160#endif161}162163164// To be used in the future for constructing semi-raw TOC data.165#if 0166static uint8 cond_hex_to_bcd(uint8 val)167{168if( ((val & 0xF) > 0x9) || ((val & 0xF0) > 0x90) )169return val;170171return U8_to_BCD(val);172}173#endif174175void CDAccess_Physical::ReadPhysDiscInfo(unsigned retry)176{177mmc_cdb_t cdb = {{0, }};178std::vector<uint8> toc_buffer;179int64 start_time = time(NULL);180int cdio_rc;181182toc_buffer.resize(0x3FFF); // (2**(8 * 2 - 1 - 1)) - 1, in case the drive has buggy firmware which chops upper bits off or overflows with values near183// the max of a 16-bit signed value184185cdb.field[0] = 0x43; // Read TOC186cdb.field[1] = 0x00;187cdb.field[2] = 0x02; // Format 0010b188cdb.field[3] = 0x00;189cdb.field[4] = 0x00;190cdb.field[5] = 0x00;191cdb.field[6] = 0x01; // First session number192cdb.field[7] = toc_buffer.size() >> 8;193cdb.field[8] = toc_buffer.size() & 0xFF;194cdb.field[9] = 0x00;195196StartLogging();197while((cdio_rc = mmc_run_cmd ((CdIo *)p_cdio, MMC_TIMEOUT_DEFAULT,198&cdb,199SCSI_MMC_DATA_READ,200toc_buffer.size(),201&toc_buffer[0])))202{203if(!retry || time(NULL) >= (start_time + retry))204{205throw(MDFN_Error(0, _("Error reading disc TOC: %s"), StopLogging().c_str()));206}207else208ClearLogging();209}210StopLogging();211212PhysTOC.Clear();213214{215int32 len_counter = MDFN_de16msb(&toc_buffer[0]) - 2;216uint8 *tbi = &toc_buffer[4];217218if(len_counter < 0 || (len_counter % 11) != 0)219throw MDFN_Error(0, _("READ TOC command response data is of an invalid length."));220221while(len_counter)222{223// Ref: MMC-3 draft revision 10g, page 221224uint8 sess MDFN_NOWARN_UNUSED = tbi[0];225uint8 adr_ctrl = tbi[1];226uint8 tno MDFN_NOWARN_UNUSED = tbi[2];227uint8 point = tbi[3];228uint8 min MDFN_NOWARN_UNUSED = tbi[4];229uint8 sec MDFN_NOWARN_UNUSED = tbi[5];230uint8 frame MDFN_NOWARN_UNUSED = tbi[6];231uint8 hour_phour MDFN_NOWARN_UNUSED = tbi[7];232uint8 pmin = tbi[8];233uint8 psec = tbi[9];234uint8 pframe = tbi[10];235236if((adr_ctrl >> 4) == 1)237{238switch(((adr_ctrl >> 4) << 8) | point)239{240case 0x101 ... 0x163:241PhysTOC.tracks[point].adr = adr_ctrl >> 4;242PhysTOC.tracks[point].control = adr_ctrl & 0xF;243PhysTOC.tracks[point].lba = AMSF_to_LBA(pmin, psec, pframe);244break;245246case 0x1A0:247PhysTOC.first_track = pmin;248PhysTOC.disc_type = psec;249break;250251case 0x1A1:252PhysTOC.last_track = pmin;253break;254255case 0x1A2:256PhysTOC.tracks[100].adr = adr_ctrl >> 4;257PhysTOC.tracks[100].control = adr_ctrl & 0xF;258PhysTOC.tracks[100].lba = AMSF_to_LBA(pmin, psec, pframe);259break;260261default:262//MDFN_printf("%02x %02x\n", adr_ctrl >> 4, point);263break;264}265}266267tbi += 11;268len_counter -= 11;269}270}271272273if(PhysTOC.first_track < 1 || PhysTOC.first_track > 99)274{275throw(MDFN_Error(0, _("Invalid first track: %d\n"), PhysTOC.first_track));276}277278if(PhysTOC.last_track > 99 || PhysTOC.last_track < PhysTOC.first_track)279{280throw(MDFN_Error(0, _("Invalid last track: %d\n"), PhysTOC.last_track));281}282283// Convenience leadout track duplication.284if(PhysTOC.last_track < 99)285PhysTOC.tracks[PhysTOC.last_track + 1] = PhysTOC.tracks[100];286}287288void CDAccess_Physical::Read_TOC(TOC *toc)289{290*toc = PhysTOC;291}292293void CDAccess_Physical::Read_Raw_Sector(uint8 *buf, int32 lba)294{295mmc_cdb_t cdb = {{0, }};296int cdio_rc;297298CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_CD);299CDIO_MMC_SET_READ_TYPE (cdb.field, CDIO_MMC_READ_TYPE_ANY);300CDIO_MMC_SET_READ_LBA (cdb.field, lba);301CDIO_MMC_SET_READ_LENGTH24(cdb.field, 1);302303StartLogging();304if(SkipSectorRead[(lba >> 3) & 0xFFFF] & (1 << (lba & 7)))305{306printf("Read(skipped): %d\n", lba);307memset(buf, 0, 2352);308309cdb.field[9] = 0x00;310cdb.field[10] = 0x01;311312if((cdio_rc = mmc_run_cmd ((CdIo *)p_cdio, MMC_TIMEOUT_DEFAULT,313&cdb,314SCSI_MMC_DATA_READ,31596,316buf + 2352)))317{318throw(MDFN_Error(0, _("MMC Read Error: %s"), StopLogging().c_str()));319}320}321else322{323cdb.field[9] = 0xF8;324cdb.field[10] = 0x01;325326if((cdio_rc = mmc_run_cmd ((CdIo *)p_cdio, MMC_TIMEOUT_DEFAULT,327&cdb,328SCSI_MMC_DATA_READ,3292352 + 96,330buf)))331{332throw(MDFN_Error(0, _("MMC Read Error: %s"), StopLogging().c_str()));333}334}335StopLogging();336}337338CDAccess_Physical::CDAccess_Physical(const std::string& path)339{340char **devices = NULL;341char **parseit = NULL;342343p_cdio = NULL;344345cdio_init();346cdio_log_set_handler(LogHandler);347348//349//350//351try352{353devices = cdio_get_devices(DRIVER_DEVICE);354parseit = devices;355if(parseit)356{357MDFN_printf(_("Connected physical devices:\n"));358MDFN_indent(1);359while(*parseit)360{361MDFN_printf("%s\n", *parseit);362parseit++;363}364MDFN_indent(-1);365}366367if(!parseit || parseit == devices)368{369throw(MDFN_Error(0, _("No CDROM drives detected(or no disc present).")));370}371372if(devices)373{374cdio_free_device_list(devices);375devices = NULL;376}377378StartLogging();379p_cdio = cdio_open_cd(path.c_str());380if(!p_cdio)381{382throw(MDFN_Error(0, _("Error opening physical CD: %s"), StopLogging().c_str()));383}384StopLogging();385386//PreventAllowMediumRemoval(true);387ReadPhysDiscInfo(0);388389//390// Determine how we can read this CD.391//392DetermineFeatures();393394memset(SkipSectorRead, 0, sizeof(SkipSectorRead));395}396catch(std::exception &e)397{398if(devices)399cdio_free_device_list(devices);400401if(p_cdio)402cdio_destroy((CdIo *)p_cdio);403404throw;405}406}407408CDAccess_Physical::~CDAccess_Physical()409{410cdio_destroy((CdIo *)p_cdio);411}412413bool CDAccess_Physical::Is_Physical(void) throw()414{415return(true);416}417418void CDAccess_Physical::Eject(bool eject_status)419{420int cdio_rc;421422StartLogging();423#if LIBCDIO_VERSION_NUM >= 83424if((cdio_rc = mmc_start_stop_unit((CdIo *)p_cdio, eject_status, false, 0, 0)) != 0)425{426if(cdio_rc != DRIVER_OP_UNSUPPORTED) // Don't error out if it's just an unsupported operation.427throw(MDFN_Error(0, _("Error ejecting medium: %s"), StopLogging().c_str()));428}429#else430if((cdio_rc = mmc_start_stop_media((CdIo *)p_cdio, eject_status, false, 0)) != 0)431{432if(cdio_rc != DRIVER_OP_UNSUPPORTED) // Don't error out if it's just an unsupported operation.433throw(MDFN_Error(0, _("Error ejecting medium: %s"), StopLogging().c_str()));434}435#endif436StopLogging();437438if(!eject_status)439{440try441{442ReadPhysDiscInfo(10);443}444catch(std::exception &e)445{446#if LIBCDIO_VERSION_NUM >= 83447mmc_start_stop_unit((CdIo *)p_cdio, true, false, 0, 0); // Eject disc, if possible.448#else449mmc_start_stop_media((CdIo *)p_cdio, true, false, 0); // Eject disc, if possible.450#endif451throw;452}453}454}455456457458