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