1 /*
2 * Copyright (c) 2010, Wei Mingzhi <whistler_wmz@users.sf.net>.
3 * All Rights Reserved.
4 *
5 * Based on: Cdrom for Psemu Pro like Emulators
6 * By: linuzappz <linuzappz@hotmail.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses>.
20 */
21
22 #include "cdr.h"
23 #if defined(__linux__)
24 #include <sys/types.h>
25 #include <sys/wait.h>
26 #endif
27
28 #ifndef USE_NULL
29 static char *LibName = N_("CD-ROM Drive Reader");
30 #else
31 static char *LibName = N_("CDR NULL Plugin");
32 #endif
33
34 int initial_time = 0;
35
36 pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
37 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
38
39 volatile CacheData *cdcache;
40 volatile int cacheaddr;
41
42 volatile unsigned char *cdbuffer;
43
44 crdata cr;
45
46 unsigned char lastTime[3];
47 pthread_t thread;
48 int subqread;
49 volatile int stopth, found, locked, playing;
50
51 long (*ReadTrackT[])() = {
52 ReadNormal,
53 ReadThreaded,
54 };
55
56 unsigned char* (*GetBufferT[])() = {
57 GetBNormal,
58 GetBThreaded,
59 };
60
61 long (*fReadTrack)();
62 unsigned char* (*fGetBuffer)();
63
64 void *CdrThread(void *arg);
65
CDRinit(void)66 long CDRinit(void) {
67 thread = (pthread_t)-1;
68 return 0;
69 }
70
CDRshutdown(void)71 long CDRshutdown(void) {
72 return 0;
73 }
74
CDRopen(void)75 long CDRopen(void) {
76 LoadConf();
77
78 #ifndef _MACOSX
79 if (IsCdHandleOpen())
80 return 0; // it's already open
81 #endif
82
83 if (OpenCdHandle(CdromDev) == -1) { // if we can't open the cdrom we'll works as a null plugin
84 fprintf(stderr, "CDR: Could not open %s\n", CdromDev);
85 }
86
87 fReadTrack = ReadTrackT[ReadMode];
88 fGetBuffer = GetBufferT[ReadMode];
89
90 if (ReadMode == THREADED) {
91 cdcache = (CacheData *)malloc(CacheSize * sizeof(CacheData));
92 if (cdcache == NULL) return -1;
93 memset((void *)cdcache, 0, CacheSize * sizeof(CacheData));
94
95 found = 0;
96 } else {
97 cdbuffer = cr.buf + 12; /* skip sync data */
98 }
99
100 if (ReadMode == THREADED) {
101 pthread_attr_t attr;
102
103 pthread_mutex_init(&mut, NULL);
104 pthread_cond_init(&cond, NULL);
105 locked = 0;
106
107 pthread_attr_init(&attr);
108 pthread_create(&thread, &attr, CdrThread, NULL);
109
110 cacheaddr = -1;
111 } else thread = (pthread_t)-1;
112
113 playing = 0;
114 stopth = 0;
115 initial_time = 0;
116
117 return 0;
118 }
119
CDRclose(void)120 long CDRclose(void) {
121 if (!IsCdHandleOpen()) return 0;
122
123 if (playing) CDRstop();
124
125 CloseCdHandle();
126
127 if (thread != (pthread_t)-1) {
128 if (locked == 0) {
129 stopth = 1;
130 while (locked == 0) usleep(5000);
131 }
132
133 stopth = 2;
134 pthread_mutex_lock(&mut);
135 pthread_cond_signal(&cond);
136 pthread_mutex_unlock(&mut);
137
138 pthread_join(thread, NULL);
139 pthread_mutex_destroy(&mut);
140 pthread_cond_destroy(&cond);
141 }
142
143 if (ReadMode == THREADED) {
144 free((void *)cdcache);
145 }
146
147 return 0;
148 }
149
150 // return Starting and Ending Track
151 // buffer:
152 // byte 0 - start track
153 // byte 1 - end track
CDRgetTN(unsigned char * buffer)154 long CDRgetTN(unsigned char *buffer) {
155 long ret;
156
157 if (!IsCdHandleOpen()) {
158 buffer[0] = 1;
159 buffer[1] = 1;
160 return 0;
161 }
162
163 if (ReadMode == THREADED) pthread_mutex_lock(&mut);
164 ret = GetTN(buffer);
165 if (ReadMode == THREADED) pthread_mutex_unlock(&mut);
166
167 return ret;
168 }
169
170 // return Track Time
171 // buffer:
172 // byte 0 - frame
173 // byte 1 - second
174 // byte 2 - minute
CDRgetTD(unsigned char track,unsigned char * buffer)175 long CDRgetTD(unsigned char track, unsigned char *buffer) {
176 long ret;
177
178 if (!IsCdHandleOpen()) {
179 memset(buffer + 1, 0, 3);
180 return 0;
181 }
182
183 if (ReadMode == THREADED) pthread_mutex_lock(&mut);
184 ret = GetTD(track, buffer);
185 if (ReadMode == THREADED) pthread_mutex_unlock(&mut);
186
187 return ret;
188 }
189
190 // normal reading
ReadNormal()191 long ReadNormal() {
192 if (ReadSector(&cr) == -1)
193 return -1;
194
195 return 0;
196 }
197
GetBNormal()198 unsigned char *GetBNormal() {
199 return (unsigned char *)cdbuffer;
200 }
201
202 // threaded reading (with cache)
ReadThreaded()203 long ReadThreaded() {
204 int addr = msf_to_lba(cr.msf.cdmsf_min0, cr.msf.cdmsf_sec0, cr.msf.cdmsf_frame0);
205 int i;
206
207 if (addr >= cacheaddr && addr < (cacheaddr + CacheSize) && cacheaddr != -1) {
208 i = addr - cacheaddr;
209 PRINTF("found %d\n", (addr - cacheaddr));
210 cdbuffer = cdcache[i].cr.buf + 12;
211 while (cdcache[i].msf[0] != cr.msf.cdmsf_min0 ||
212 cdcache[i].msf[1] != cr.msf.cdmsf_sec0 ||
213 cdcache[i].msf[2] != cr.msf.cdmsf_frame0) {
214 if (locked == 1) {
215 if (cdcache[i].ret == 0) break;
216 return -1;
217 }
218 usleep(5000);
219 }
220 PRINTF("%d:%d:%d, %p, %p\n", cdcache[i].msf[0], cdcache[i].msf[1], cdcache[i].msf[2], cdbuffer, cdcache);
221 found = 1;
222
223 return 0;
224 } else found = 0;
225
226 if (locked == 0) {
227 stopth = 1;
228 while (locked == 0) { usleep(5000); }
229 stopth = 0;
230 }
231
232 // not found in cache
233 locked = 0;
234
235 pthread_mutex_lock(&mut);
236 pthread_cond_signal(&cond);
237 pthread_mutex_unlock(&mut);
238
239 return 0;
240 }
241
GetBThreaded()242 unsigned char *GetBThreaded() {
243 PRINTF("threadc %d\n", found);
244
245 if (found == 1) return (unsigned char *)cdbuffer;
246
247 cdbuffer = cdcache[0].cr.buf + 12;
248
249 while (cdcache[0].msf[0] != cr.msf.cdmsf_min0 ||
250 cdcache[0].msf[1] != cr.msf.cdmsf_sec0 ||
251 cdcache[0].msf[2] != cr.msf.cdmsf_frame0) {
252 if (locked == 1) return NULL;
253 usleep(5000);
254 }
255 if (cdcache[0].ret == -1) return NULL;
256
257 return (unsigned char *)cdbuffer;
258 }
259
CdrThread(void * arg)260 void *CdrThread(void *arg) {
261 unsigned char curTime[3];
262 int i;
263
264 for (;;) {
265 pthread_mutex_lock(&mut);
266 locked = 1;
267
268 pthread_cond_wait(&cond, &mut);
269
270 if (stopth == 2) pthread_exit(NULL);
271 // refill the buffer
272 cacheaddr = msf_to_lba(cr.msf.cdmsf_min0, cr.msf.cdmsf_sec0, cr.msf.cdmsf_frame0);
273
274 memcpy(curTime, &cr.msf, 3);
275
276 PRINTF("start thc %d:%d:%d\n", curTime[0], curTime[1], curTime[2]);
277
278 for (i = 0; i < CacheSize; i++) {
279 cdcache[i].cr.msf.cdmsf_min0 = curTime[0];
280 cdcache[i].cr.msf.cdmsf_sec0 = curTime[1];
281 cdcache[i].cr.msf.cdmsf_frame0 = curTime[2];
282
283 PRINTF("reading %d:%d:%d\n", curTime[0], curTime[1], curTime[2]);
284
285 cdcache[i].ret = (int)ReadSector((crdata *)&cdcache[i].cr);
286 if (cdcache[i].ret == -1) break;
287
288 PRINTF("readed %x:%x:%x\n", cdcache[i].cr.buf[12], cdcache[i].cr.buf[13], cdcache[i].cr.buf[14]);
289
290 cdcache[i].msf[0] = curTime[0];
291 cdcache[i].msf[1] = curTime[1];
292 cdcache[i].msf[2] = curTime[2];
293
294 curTime[2]++;
295 if (curTime[2] == 75) {
296 curTime[2] = 0;
297 curTime[1]++;
298 if (curTime[1] == 60) {
299 curTime[1] = 0;
300 curTime[0]++;
301 }
302 }
303
304 if (stopth) break;
305 }
306
307 pthread_mutex_unlock(&mut);
308 }
309
310 return NULL;
311 }
312
313 // read track
314 // time:
315 // byte 0 - minute
316 // byte 1 - second
317 // byte 2 - frame
318 // uses bcd format
CDRreadTrack(unsigned char * time)319 long CDRreadTrack(unsigned char *time) {
320 if (!IsCdHandleOpen()) {
321 memset(cr.buf, 0, DATA_SIZE);
322 return 0;
323 }
324
325 PRINTF("CDRreadTrack %d:%d:%d\n", btoi(time[0]), btoi(time[1]), btoi(time[2]));
326
327 if (UseSubQ) memcpy(lastTime, time, 3);
328 subqread = 0;
329
330 cr.msf.cdmsf_min0 = btoi(time[0]);
331 cr.msf.cdmsf_sec0 = btoi(time[1]);
332 cr.msf.cdmsf_frame0 = btoi(time[2]);
333
334 return fReadTrack();
335 }
336
337 // return readed track
CDRgetBuffer(void)338 unsigned char *CDRgetBuffer(void) {
339 return fGetBuffer();
340 }
341
342 // plays cdda audio
343 // sector:
344 // byte 0 - minute
345 // byte 1 - second
346 // byte 2 - frame
347 // does NOT uses bcd format
CDRplay(unsigned char * sector)348 long CDRplay(unsigned char *sector) {
349 long ret;
350
351 if (!IsCdHandleOpen())
352 return 0;
353
354 // If play was called with the same time as the previous call,
355 // don't restart it. Of course, if play is called with a different
356 // track, stop playing the current stream.
357 if (playing) {
358 if (msf_to_lba(sector[0], sector[1], sector[2]) == initial_time)
359 return 0;
360 else
361 CDRstop();
362 }
363
364 initial_time = msf_to_lba(sector[0], sector[1], sector[2]);
365
366 if (ReadMode == THREADED) pthread_mutex_lock(&mut);
367 ret = PlayCDDA(sector);
368 if (ReadMode == THREADED) pthread_mutex_unlock(&mut);
369
370 if (ret == 0) {
371 playing = 1;
372 return 0;
373 }
374
375 return -1;
376 }
377
378 // stops cdda audio
CDRstop(void)379 long CDRstop(void) {
380 long ret;
381
382 if (!IsCdHandleOpen())
383 return 0;
384
385 if (ReadMode == THREADED) pthread_mutex_lock(&mut);
386 ret = StopCDDA();
387 if (ReadMode == THREADED) pthread_mutex_unlock(&mut);
388
389 if (ret == 0) {
390 playing = 0;
391 initial_time = 0;
392
393 return 0;
394 }
395
396 return -1;
397 }
398
399 // reads cdr status
400 // type:
401 // 0x00 - unknown
402 // 0x01 - data
403 // 0x02 - audio
404 // 0xff - no cdrom
405 // status: (only shell open supported)
406 // 0x00 - unknown
407 // 0x01 - error
408 // 0x04 - seek error
409 // 0x10 - shell open
410 // 0x20 - reading
411 // 0x40 - seeking
412 // 0x80 - playing
413 // time:
414 // byte 0 - minute
415 // byte 1 - second
416 // byte 2 - frame
417
CDRgetStatus(struct CdrStat * stat)418 long CDRgetStatus(struct CdrStat *stat) {
419 long ret;
420
421 if (!IsCdHandleOpen())
422 return -1;
423
424 if (ReadMode == THREADED) pthread_mutex_lock(&mut);
425 ret = GetStatus(playing, stat);
426 if (ReadMode == THREADED) pthread_mutex_unlock(&mut);
427
428 return ret;
429 }
430
CDRgetBufferSub(void)431 unsigned char *CDRgetBufferSub(void) {
432 static unsigned char *p = NULL;
433
434 if (!UseSubQ) return NULL;
435 if (subqread) return p;
436
437 if (ReadMode == THREADED) pthread_mutex_lock(&mut);
438 p = ReadSub(lastTime);
439 if (ReadMode == THREADED) pthread_mutex_unlock(&mut);
440
441 if (p != NULL) subqread = 1;
442
443 return p;
444 }
445
446 // read CDDA sector into buffer
CDRreadCDDA(unsigned char m,unsigned char s,unsigned char f,unsigned char * buffer)447 long CDRreadCDDA(unsigned char m, unsigned char s, unsigned char f, unsigned char *buffer) {
448 unsigned char msf[3] = {itob(m), itob(s), itob(f)};
449 unsigned char *p;
450
451 if (CDRreadTrack(msf) != 0) return -1;
452
453 p = CDRgetBuffer();
454 if (p == NULL) return -1;
455
456 memcpy(buffer, p - 12, CD_FRAMESIZE_RAW); // copy from the beginning of the sector
457 return 0;
458 }
459
460 // get Track End Time
CDRgetTE(unsigned char track,unsigned char * m,unsigned char * s,unsigned char * f)461 long CDRgetTE(unsigned char track, unsigned char *m, unsigned char *s, unsigned char *f) {
462 long ret;
463
464 if (!IsCdHandleOpen()) return -1;
465
466 if (ReadMode == THREADED) pthread_mutex_lock(&mut);
467 ret = GetTE(track, m, s, f);
468 if (ReadMode == THREADED) pthread_mutex_unlock(&mut);
469
470 return ret;
471 }
472
473 #ifndef _MACOSX
474
ExecCfg(char * arg)475 void ExecCfg(char *arg) {
476 char cfg[256];
477 struct stat buf;
478
479 strcpy(cfg, "./cfgDFCdrom");
480 if (stat(cfg, &buf) != -1) {
481 int pid = fork();
482 if (pid == 0) {
483 if (fork() == 0) {
484 execl(cfg, "cfgDFCdrom", arg, NULL);
485 }
486 exit(0);
487 } else if (pid > 0) {
488 waitpid(pid, NULL, 0);
489 }
490 return;
491 }
492
493 strcpy(cfg, "./cfg/cfgDFCdrom");
494 if (stat(cfg, &buf) != -1) {
495 int pid = fork();
496 if (pid == 0) {
497 if (fork() == 0) {
498 execl(cfg, "cfgDFCdrom", arg, NULL);
499 }
500 exit(0);
501 } else if (pid > 0) {
502 waitpid(pid, NULL, 0);
503 }
504 return;
505 }
506
507 fprintf(stderr, "cfgDFCdrom file not found!\n");
508 }
509
CDRconfigure()510 long CDRconfigure() {
511 #ifndef USE_NULL
512 ExecCfg("configure");
513 #endif
514 return 0;
515 }
516
CDRabout()517 void CDRabout() {
518 ExecCfg("about");
519 }
520
521 #endif
522
CDRtest(void)523 long CDRtest(void) {
524 #ifndef USE_NULL
525 if (OpenCdHandle(CdromDev) == -1)
526 return -1;
527 CloseCdHandle();
528 #endif
529 return 0;
530 }
531
PSEgetLibName(void)532 char *PSEgetLibName(void) {
533 return _(LibName);
534 }
535
PSEgetLibType(void)536 unsigned long PSEgetLibType(void) {
537 return PSE_LT_CDR;
538 }
539
PSEgetLibVersion(void)540 unsigned long PSEgetLibVersion(void) {
541 return 1 << 16;
542 }
543