543 lines
11 KiB
C
Executable File
543 lines
11 KiB
C
Executable File
/*
|
|
* Copyright (c) 2010, Wei Mingzhi <whistler_wmz@users.sf.net>.
|
|
* All Rights Reserved.
|
|
*
|
|
* Based on: Cdrom for Psemu Pro like Emulators
|
|
* By: linuzappz <linuzappz@hotmail.com>
|
|
*
|
|
* 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, see <http://www.gnu.org/licenses>.
|
|
*/
|
|
|
|
#include "cdr.h"
|
|
#if defined(__linux__)
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#endif
|
|
|
|
#ifndef USE_NULL
|
|
static char *LibName = N_("CD-ROM Drive Reader");
|
|
#else
|
|
static char *LibName = N_("CDR NULL Plugin");
|
|
#endif
|
|
|
|
int initial_time = 0;
|
|
|
|
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
|
|
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
|
|
|
volatile CacheData *cdcache;
|
|
volatile int cacheaddr;
|
|
|
|
volatile unsigned char *cdbuffer;
|
|
|
|
crdata cr;
|
|
|
|
unsigned char lastTime[3];
|
|
pthread_t thread;
|
|
int subqread;
|
|
volatile int stopth, found, locked, playing;
|
|
|
|
long (*ReadTrackT[])() = {
|
|
ReadNormal,
|
|
ReadThreaded,
|
|
};
|
|
|
|
unsigned char* (*GetBufferT[])() = {
|
|
GetBNormal,
|
|
GetBThreaded,
|
|
};
|
|
|
|
long (*fReadTrack)();
|
|
unsigned char* (*fGetBuffer)();
|
|
|
|
void *CdrThread(void *arg);
|
|
|
|
long CDRinit(void) {
|
|
thread = (pthread_t)-1;
|
|
return 0;
|
|
}
|
|
|
|
long CDRshutdown(void) {
|
|
return 0;
|
|
}
|
|
|
|
long CDRopen(void) {
|
|
LoadConf();
|
|
|
|
#ifndef _MACOSX
|
|
if (IsCdHandleOpen())
|
|
return 0; // it's already open
|
|
#endif
|
|
|
|
if (OpenCdHandle(CdromDev) == -1) { // if we can't open the cdrom we'll works as a null plugin
|
|
fprintf(stderr, "CDR: Could not open %s\n", CdromDev);
|
|
}
|
|
|
|
fReadTrack = ReadTrackT[ReadMode];
|
|
fGetBuffer = GetBufferT[ReadMode];
|
|
|
|
if (ReadMode == THREADED) {
|
|
cdcache = (CacheData *)malloc(CacheSize * sizeof(CacheData));
|
|
if (cdcache == NULL) return -1;
|
|
memset((void *)cdcache, 0, CacheSize * sizeof(CacheData));
|
|
|
|
found = 0;
|
|
} else {
|
|
cdbuffer = cr.buf + 12; /* skip sync data */
|
|
}
|
|
|
|
if (ReadMode == THREADED) {
|
|
pthread_attr_t attr;
|
|
|
|
pthread_mutex_init(&mut, NULL);
|
|
pthread_cond_init(&cond, NULL);
|
|
locked = 0;
|
|
|
|
pthread_attr_init(&attr);
|
|
pthread_create(&thread, &attr, CdrThread, NULL);
|
|
|
|
cacheaddr = -1;
|
|
} else thread = (pthread_t)-1;
|
|
|
|
playing = 0;
|
|
stopth = 0;
|
|
initial_time = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
long CDRclose(void) {
|
|
if (!IsCdHandleOpen()) return 0;
|
|
|
|
if (playing) CDRstop();
|
|
|
|
CloseCdHandle();
|
|
|
|
if (thread != (pthread_t)-1) {
|
|
if (locked == 0) {
|
|
stopth = 1;
|
|
while (locked == 0) usleep(5000);
|
|
}
|
|
|
|
stopth = 2;
|
|
pthread_mutex_lock(&mut);
|
|
pthread_cond_signal(&cond);
|
|
pthread_mutex_unlock(&mut);
|
|
|
|
pthread_join(thread, NULL);
|
|
pthread_mutex_destroy(&mut);
|
|
pthread_cond_destroy(&cond);
|
|
}
|
|
|
|
if (ReadMode == THREADED) {
|
|
free((void *)cdcache);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// return Starting and Ending Track
|
|
// buffer:
|
|
// byte 0 - start track
|
|
// byte 1 - end track
|
|
long CDRgetTN(unsigned char *buffer) {
|
|
long ret;
|
|
|
|
if (!IsCdHandleOpen()) {
|
|
buffer[0] = 1;
|
|
buffer[1] = 1;
|
|
return 0;
|
|
}
|
|
|
|
if (ReadMode == THREADED) pthread_mutex_lock(&mut);
|
|
ret = GetTN(buffer);
|
|
if (ReadMode == THREADED) pthread_mutex_unlock(&mut);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// return Track Time
|
|
// buffer:
|
|
// byte 0 - frame
|
|
// byte 1 - second
|
|
// byte 2 - minute
|
|
long CDRgetTD(unsigned char track, unsigned char *buffer) {
|
|
long ret;
|
|
|
|
if (!IsCdHandleOpen()) {
|
|
memset(buffer + 1, 0, 3);
|
|
return 0;
|
|
}
|
|
|
|
if (ReadMode == THREADED) pthread_mutex_lock(&mut);
|
|
ret = GetTD(track, buffer);
|
|
if (ReadMode == THREADED) pthread_mutex_unlock(&mut);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// normal reading
|
|
long ReadNormal() {
|
|
if (ReadSector(&cr) == -1)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned char *GetBNormal() {
|
|
return (unsigned char *)cdbuffer;
|
|
}
|
|
|
|
// threaded reading (with cache)
|
|
long ReadThreaded() {
|
|
int addr = msf_to_lba(cr.msf.cdmsf_min0, cr.msf.cdmsf_sec0, cr.msf.cdmsf_frame0);
|
|
int i;
|
|
|
|
if (addr >= cacheaddr && addr < (cacheaddr + CacheSize) && cacheaddr != -1) {
|
|
i = addr - cacheaddr;
|
|
PRINTF("found %d\n", (addr - cacheaddr));
|
|
cdbuffer = cdcache[i].cr.buf + 12;
|
|
while (cdcache[i].msf[0] != cr.msf.cdmsf_min0 ||
|
|
cdcache[i].msf[1] != cr.msf.cdmsf_sec0 ||
|
|
cdcache[i].msf[2] != cr.msf.cdmsf_frame0) {
|
|
if (locked == 1) {
|
|
if (cdcache[i].ret == 0) break;
|
|
return -1;
|
|
}
|
|
usleep(5000);
|
|
}
|
|
PRINTF("%d:%d:%d, %p, %p\n", cdcache[i].msf[0], cdcache[i].msf[1], cdcache[i].msf[2], cdbuffer, cdcache);
|
|
found = 1;
|
|
|
|
return 0;
|
|
} else found = 0;
|
|
|
|
if (locked == 0) {
|
|
stopth = 1;
|
|
while (locked == 0) { usleep(5000); }
|
|
stopth = 0;
|
|
}
|
|
|
|
// not found in cache
|
|
locked = 0;
|
|
|
|
pthread_mutex_lock(&mut);
|
|
pthread_cond_signal(&cond);
|
|
pthread_mutex_unlock(&mut);
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned char *GetBThreaded() {
|
|
PRINTF("threadc %d\n", found);
|
|
|
|
if (found == 1) return (unsigned char *)cdbuffer;
|
|
|
|
cdbuffer = cdcache[0].cr.buf + 12;
|
|
|
|
while (cdcache[0].msf[0] != cr.msf.cdmsf_min0 ||
|
|
cdcache[0].msf[1] != cr.msf.cdmsf_sec0 ||
|
|
cdcache[0].msf[2] != cr.msf.cdmsf_frame0) {
|
|
if (locked == 1) return NULL;
|
|
usleep(5000);
|
|
}
|
|
if (cdcache[0].ret == -1) return NULL;
|
|
|
|
return (unsigned char *)cdbuffer;
|
|
}
|
|
|
|
void *CdrThread(void *arg) {
|
|
unsigned char curTime[3];
|
|
int i;
|
|
|
|
for (;;) {
|
|
pthread_mutex_lock(&mut);
|
|
locked = 1;
|
|
|
|
pthread_cond_wait(&cond, &mut);
|
|
|
|
if (stopth == 2) pthread_exit(NULL);
|
|
// refill the buffer
|
|
cacheaddr = msf_to_lba(cr.msf.cdmsf_min0, cr.msf.cdmsf_sec0, cr.msf.cdmsf_frame0);
|
|
|
|
memcpy(curTime, &cr.msf, 3);
|
|
|
|
PRINTF("start thc %d:%d:%d\n", curTime[0], curTime[1], curTime[2]);
|
|
|
|
for (i = 0; i < CacheSize; i++) {
|
|
cdcache[i].cr.msf.cdmsf_min0 = curTime[0];
|
|
cdcache[i].cr.msf.cdmsf_sec0 = curTime[1];
|
|
cdcache[i].cr.msf.cdmsf_frame0 = curTime[2];
|
|
|
|
PRINTF("reading %d:%d:%d\n", curTime[0], curTime[1], curTime[2]);
|
|
|
|
cdcache[i].ret = (int)ReadSector((crdata *)&cdcache[i].cr);
|
|
if (cdcache[i].ret == -1) break;
|
|
|
|
PRINTF("readed %x:%x:%x\n", cdcache[i].cr.buf[12], cdcache[i].cr.buf[13], cdcache[i].cr.buf[14]);
|
|
|
|
cdcache[i].msf[0] = curTime[0];
|
|
cdcache[i].msf[1] = curTime[1];
|
|
cdcache[i].msf[2] = curTime[2];
|
|
|
|
curTime[2]++;
|
|
if (curTime[2] == 75) {
|
|
curTime[2] = 0;
|
|
curTime[1]++;
|
|
if (curTime[1] == 60) {
|
|
curTime[1] = 0;
|
|
curTime[0]++;
|
|
}
|
|
}
|
|
|
|
if (stopth) break;
|
|
}
|
|
|
|
pthread_mutex_unlock(&mut);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// read track
|
|
// time:
|
|
// byte 0 - minute
|
|
// byte 1 - second
|
|
// byte 2 - frame
|
|
// uses bcd format
|
|
long CDRreadTrack(unsigned char *time) {
|
|
if (!IsCdHandleOpen()) {
|
|
memset(cr.buf, 0, DATA_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
PRINTF("CDRreadTrack %d:%d:%d\n", btoi(time[0]), btoi(time[1]), btoi(time[2]));
|
|
|
|
if (UseSubQ) memcpy(lastTime, time, 3);
|
|
subqread = 0;
|
|
|
|
cr.msf.cdmsf_min0 = btoi(time[0]);
|
|
cr.msf.cdmsf_sec0 = btoi(time[1]);
|
|
cr.msf.cdmsf_frame0 = btoi(time[2]);
|
|
|
|
return fReadTrack();
|
|
}
|
|
|
|
// return readed track
|
|
unsigned char *CDRgetBuffer(void) {
|
|
return fGetBuffer();
|
|
}
|
|
|
|
// plays cdda audio
|
|
// sector:
|
|
// byte 0 - minute
|
|
// byte 1 - second
|
|
// byte 2 - frame
|
|
// does NOT uses bcd format
|
|
long CDRplay(unsigned char *sector) {
|
|
long ret;
|
|
|
|
if (!IsCdHandleOpen())
|
|
return 0;
|
|
|
|
// If play was called with the same time as the previous call,
|
|
// don't restart it. Of course, if play is called with a different
|
|
// track, stop playing the current stream.
|
|
if (playing) {
|
|
if (msf_to_lba(sector[0], sector[1], sector[2]) == initial_time)
|
|
return 0;
|
|
else
|
|
CDRstop();
|
|
}
|
|
|
|
initial_time = msf_to_lba(sector[0], sector[1], sector[2]);
|
|
|
|
if (ReadMode == THREADED) pthread_mutex_lock(&mut);
|
|
ret = PlayCDDA(sector);
|
|
if (ReadMode == THREADED) pthread_mutex_unlock(&mut);
|
|
|
|
if (ret == 0) {
|
|
playing = 1;
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// stops cdda audio
|
|
long CDRstop(void) {
|
|
long ret;
|
|
|
|
if (!IsCdHandleOpen())
|
|
return 0;
|
|
|
|
if (ReadMode == THREADED) pthread_mutex_lock(&mut);
|
|
ret = StopCDDA();
|
|
if (ReadMode == THREADED) pthread_mutex_unlock(&mut);
|
|
|
|
if (ret == 0) {
|
|
playing = 0;
|
|
initial_time = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// reads cdr status
|
|
// type:
|
|
// 0x00 - unknown
|
|
// 0x01 - data
|
|
// 0x02 - audio
|
|
// 0xff - no cdrom
|
|
// status: (only shell open supported)
|
|
// 0x00 - unknown
|
|
// 0x01 - error
|
|
// 0x04 - seek error
|
|
// 0x10 - shell open
|
|
// 0x20 - reading
|
|
// 0x40 - seeking
|
|
// 0x80 - playing
|
|
// time:
|
|
// byte 0 - minute
|
|
// byte 1 - second
|
|
// byte 2 - frame
|
|
|
|
long CDRgetStatus(struct CdrStat *stat) {
|
|
long ret;
|
|
|
|
if (!IsCdHandleOpen())
|
|
return -1;
|
|
|
|
if (ReadMode == THREADED) pthread_mutex_lock(&mut);
|
|
ret = GetStatus(playing, stat);
|
|
if (ReadMode == THREADED) pthread_mutex_unlock(&mut);
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned char *CDRgetBufferSub(void) {
|
|
static unsigned char *p = NULL;
|
|
|
|
if (!UseSubQ) return NULL;
|
|
if (subqread) return p;
|
|
|
|
if (ReadMode == THREADED) pthread_mutex_lock(&mut);
|
|
p = ReadSub(lastTime);
|
|
if (ReadMode == THREADED) pthread_mutex_unlock(&mut);
|
|
|
|
if (p != NULL) subqread = 1;
|
|
|
|
return p;
|
|
}
|
|
|
|
// read CDDA sector into buffer
|
|
long CDRreadCDDA(unsigned char m, unsigned char s, unsigned char f, unsigned char *buffer) {
|
|
unsigned char msf[3] = {itob(m), itob(s), itob(f)};
|
|
unsigned char *p;
|
|
|
|
if (CDRreadTrack(msf) != 0) return -1;
|
|
|
|
p = CDRgetBuffer();
|
|
if (p == NULL) return -1;
|
|
|
|
memcpy(buffer, p - 12, CD_FRAMESIZE_RAW); // copy from the beginning of the sector
|
|
return 0;
|
|
}
|
|
|
|
// get Track End Time
|
|
long CDRgetTE(unsigned char track, unsigned char *m, unsigned char *s, unsigned char *f) {
|
|
long ret;
|
|
|
|
if (!IsCdHandleOpen()) return -1;
|
|
|
|
if (ReadMode == THREADED) pthread_mutex_lock(&mut);
|
|
ret = GetTE(track, m, s, f);
|
|
if (ReadMode == THREADED) pthread_mutex_unlock(&mut);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifndef _MACOSX
|
|
|
|
void ExecCfg(char *arg) {
|
|
char cfg[256];
|
|
struct stat buf;
|
|
|
|
strcpy(cfg, "./cfgDFCdrom");
|
|
if (stat(cfg, &buf) != -1) {
|
|
int pid = fork();
|
|
if (pid == 0) {
|
|
if (fork() == 0) {
|
|
execl(cfg, "cfgDFCdrom", arg, NULL);
|
|
}
|
|
exit(0);
|
|
} else if (pid > 0) {
|
|
waitpid(pid, NULL, 0);
|
|
}
|
|
return;
|
|
}
|
|
|
|
strcpy(cfg, "./cfg/cfgDFCdrom");
|
|
if (stat(cfg, &buf) != -1) {
|
|
int pid = fork();
|
|
if (pid == 0) {
|
|
if (fork() == 0) {
|
|
execl(cfg, "cfgDFCdrom", arg, NULL);
|
|
}
|
|
exit(0);
|
|
} else if (pid > 0) {
|
|
waitpid(pid, NULL, 0);
|
|
}
|
|
return;
|
|
}
|
|
|
|
fprintf(stderr, "cfgDFCdrom file not found!\n");
|
|
}
|
|
|
|
long CDRconfigure() {
|
|
#ifndef USE_NULL
|
|
ExecCfg("configure");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void CDRabout() {
|
|
ExecCfg("about");
|
|
}
|
|
|
|
#endif
|
|
|
|
long CDRtest(void) {
|
|
#ifndef USE_NULL
|
|
if (OpenCdHandle(CdromDev) == -1)
|
|
return -1;
|
|
CloseCdHandle();
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
char *PSEgetLibName(void) {
|
|
return _(LibName);
|
|
}
|
|
|
|
unsigned long PSEgetLibType(void) {
|
|
return PSE_LT_CDR;
|
|
}
|
|
|
|
unsigned long PSEgetLibVersion(void) {
|
|
return 1 << 16;
|
|
}
|