/* * PSX-Tools Bundle Pack * Copyright (C) 2002-2003 Nicolas "Pixel" Noble * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* $Id: cdreader.cpp,v 1.20 2004-11-27 21:44:48 pixel Exp $ */ #include #include #include #include #include #include #include "cdreader.h" #include "Exceptions.h" #include "generic.h" #ifdef HAVE_CONFIG_H #include "config.h" #else #define _(x) x #endif #include "cdabstract.h" #define nsectorsarow 1 #define cachesize 2048 bool cdreader::CanWrite() const { return 0; } bool cdreader::CanRead() const { return 1; } bool cdreader::CanSeek() const { return 1; } String cdreader::GetName() const { return n + " raw reading"; } ssize_t cdreader::GetSize() const { return -1; } off_t cdreader::seek(off_t offset, int whence) throw (GeneralException) { switch (whence) { case SEEK_SET: itell = offset; break; case SEEK_CUR: itell += offset; break; case SEEK_END: throw GeneralException("Can not yet seek from the end of a CD."); } return itell; } ssize_t cdreader::read(void *buf, size_t count) throw (GeneralException) { char sector[2352]; size_t r = count; size_t remain = itell % 2352; sectorseek(itell / 2352); while (count > 0) { size_t n = MIN(2352 - remain, count); fetchsector(sector); bcopy(sector + remain, buf, n); buf = ((char *) buf) + n; itell += n; count -= n; remain = 0; } return r; } void cdreader::sectorseek(int sec) { sector = sec; } void cdreader::fetchsector(void * buf, int sec) { if (sec >= 0) sector = sec; if (sectors[sector]) { actualize(sectors[sector]); memcpy(buf, sectors[sector]->sector, 2352); return; } Byte buffers[2352 * nsectorsarow]; getsector(buffers, sec = sector, nsectorsarow); for (int i = nsectorsarow - 1; i >= 0; i--) { introduce(buffers + i * 2352, sec + i); } memcpy(buf, buffers, 2352); while (nsectors > cachesize) { removetail(); } } cdreader::~cdreader() { while (nsectors) { removetail(); } } void cdreader::removetail() { if (!tail) return; sectors[tail->n] = 0; if (head == tail) { free(tail); head = tail = 0; nsectors = 0; } else { cachedsector * p; (p = tail->prev)->next = 0; free(tail); tail = p; nsectors--; } } void cdreader::actualize(cachedsector * s) { if (s == head) return; if (s != tail) { s->next->prev = s->prev; } else { tail = s->prev; } s->prev->next = s->next; head->prev = s; s->prev = 0; s->next = head; head = s; } void cdreader::introduce(Byte * datas, int n) { cachedsector * s; s = (cachedsector *) malloc(sizeof(cachedsector)); memcpy(s->sector, datas, 2352); s->n = n; if (head) { s->next = head; head->prev = s; head = s; } else { head = tail = s; s->prev = s->next = 0; } sectors[n] = s; nsectors++; } #ifdef __linux__ #include #include #include cdreader::cdreader(const String & no) throw (GeneralException) : Handle(open(no.to_charp(), O_RDONLY)), n(no), sector(0) { int i; #ifdef DEBUG printm(M_INFO, "Opening cdrom device " + no + "\n"); #endif if (GetHandle() < 0) { throw IOGeneral(String(_("Error opening file ")) + no + _(" for reading: ") + strerror(errno)); } struct stat s; fstat(GetHandle(), &s); if (!S_ISBLK(s.st_mode)) { throw GeneralException(no + " is not a block device."); } for (i = 0; i < 400000; i++) { sectors[i] = 0; } head = tail = 0; nsectors = 0; } cdreader::cdreader(const cdreader & inp) : Handle(inp), n(inp.n) { int i; for (i = 0; i < 400000; i++) { sectors[i] = 0; } head = tail = 0; nsectors = 0; } void cdreader::getsector(void *buf, int sec, int nb) throw (GeneralException) { struct cdrom_msf * msf = (struct cdrom_msf *) buf; if (sec >= 0) sector = sec; sector += 150; msf->cdmsf_min0 = sector /CD_SECS /CD_FRAMES; msf->cdmsf_sec0 = (sector /CD_FRAMES)%CD_SECS; msf->cdmsf_frame0= sector %CD_FRAMES; msf->cdmsf_min1 = (sector + nb)/CD_SECS /CD_FRAMES; msf->cdmsf_sec1 = ((sector + nb)/CD_FRAMES)%CD_SECS; msf->cdmsf_frame1= (sector + nb)%CD_FRAMES; sector -= 150; if (ioctl(GetHandle(), CDROMREADRAW, buf) < 0) { throw GeneralException(String("unable to read cd sector ") + sector + ": " + strerror(errno)); } sector += nb; } #endif #if defined (_MSC_VER) || defined (__MINGW32__) cdreader::cdreader(const String & no) throw (GeneralException) : Handle(-1), n(no), sector(0) { if (!(hFile = cdabstract::OpenIOCTLFile(no[0]))) throw GeneralException("Error opening device " + no); for (int i = 0; i < 400000; i++) { sectors[i] = 0; } head = tail = 0; nsectors = 0; } cdreader::cdreader(const cdreader & i) : Handle(i), n(i.n) { hFile = i.hFile; for (int x = 0; x < 400000; x++) { sectors[x] = 0; } head = tail = 0; nsectors = 0; } void cdreader::close() throw (GeneralException) { CloseHandle(hFile); } typedef enum _TRACK_MODE_TYPE { YellowMode2, XAForm2, CDDA } TRACK_MODE_TYPE, *PTRACK_MODE_TYPE; typedef struct _RAW_READ_INFO { LARGE_INTEGER DiskOffset; ULONG SectorCount; TRACK_MODE_TYPE TrackMode; } RAW_READ_INFO, *PRAW_READ_INFO; void cdreader::getsector(void *buf, int sec, int nb) throw (GeneralException) { RAW_READ_INFO rawIOCTL; DWORD dwRet; BOOL bStat; bool done = false; if (sec >= 0) sector = sec; rawIOCTL.DiskOffset.QuadPart = sector * 2048; rawIOCTL.SectorCount = nb; rawIOCTL.TrackMode = YellowMode2; while (!done) { SetLastError(0); bStat = DeviceIoControl(hFile, IOCTL_CDROM_RAW_READ, &rawIOCTL, sizeof(RAW_READ_INFO), buf, 2352 * nb, &dwRet, NULL); if (!bStat) { DWORD dwErrCode = GetLastError(); if (dwErrCode == ERROR_INVALID_FUNCTION) { if (rawIOCTL.TrackMode == YellowMode2) { rawIOCTL.TrackMode = XAForm2; continue; } } if (dwErrCode != ERROR_IO_PENDING) { LPVOID lpMsgBuf; if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &lpMsgBuf, 0, NULL )) throw GeneralException("Gave up on reading CD: unknown error"); String errmsg = (LPCTSTR) lpMsgBuf; LocalFree(lpMsgBuf); throw GeneralException("Gave up on reading CD: " + errmsg); } } else { done = true; } } sector += nb; } #endif