Commit | Line | Data |
---|---|---|
ef79bbde P |
1 | /*************************************************************************** |
2 | * Copyright (C) 2007 PCSX-df Team * | |
3 | * Copyright (C) 2009 Wei Mingzhi * | |
4 | * * | |
5 | * This program is free software; you can redistribute it and/or modify * | |
6 | * it under the terms of the GNU General Public License as published by * | |
7 | * the Free Software Foundation; either version 2 of the License, or * | |
8 | * (at your option) any later version. * | |
9 | * * | |
10 | * This program is distributed in the hope that it will be useful, * | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
13 | * GNU General Public License for more details. * | |
14 | * * | |
15 | * You should have received a copy of the GNU General Public License * | |
16 | * along with this program; if not, write to the * | |
17 | * Free Software Foundation, Inc., * | |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA. * | |
19 | ***************************************************************************/ | |
20 | ||
21 | #include "psxcommon.h" | |
22 | #include "plugins.h" | |
23 | #include "cdrom.h" | |
24 | #include "cdriso.h" | |
25 | ||
26 | #ifdef _WIN32 | |
27 | #include <process.h> | |
28 | #include <windows.h> | |
29 | #else | |
30 | #include <pthread.h> | |
31 | #include <sys/time.h> | |
32 | #endif | |
33 | ||
34 | static FILE *cdHandle = NULL; | |
35 | static FILE *cddaHandle = NULL; | |
36 | static FILE *subHandle = NULL; | |
37 | ||
38 | static boolean subChanMixed = FALSE; | |
39 | static boolean subChanRaw = FALSE; | |
40 | ||
41 | static unsigned char cdbuffer[DATA_SIZE]; | |
42 | static unsigned char subbuffer[SUB_FRAMESIZE]; | |
43 | ||
44 | static unsigned char sndbuffer[CD_FRAMESIZE_RAW * 10]; | |
45 | ||
46 | #define CDDA_FRAMETIME (1000 * (sizeof(sndbuffer) / CD_FRAMESIZE_RAW) / 75) | |
47 | ||
48 | #ifdef _WIN32 | |
49 | static HANDLE threadid; | |
50 | #else | |
51 | static pthread_t threadid; | |
52 | #endif | |
53 | static unsigned int initial_offset = 0; | |
54 | static volatile boolean playing = FALSE; | |
55 | static boolean cddaBigEndian = FALSE; | |
56 | static volatile unsigned int cddaCurOffset = 0; | |
57 | ||
58 | char* CALLBACK CDR__getDriveLetter(void); | |
59 | long CALLBACK CDR__configure(void); | |
60 | long CALLBACK CDR__test(void); | |
61 | void CALLBACK CDR__about(void); | |
62 | long CALLBACK CDR__setfilename(char *filename); | |
63 | long CALLBACK CDR__getStatus(struct CdrStat *stat); | |
64 | ||
65 | extern void *hCDRDriver; | |
66 | ||
67 | struct trackinfo { | |
68 | enum {DATA, CDDA} type; | |
69 | char start[3]; // MSF-format | |
70 | char length[3]; // MSF-format | |
71 | }; | |
72 | ||
73 | #define MAXTRACKS 100 /* How many tracks can a CD hold? */ | |
74 | ||
75 | static int numtracks = 0; | |
76 | static struct trackinfo ti[MAXTRACKS]; | |
77 | ||
78 | // get a sector from a msf-array | |
79 | static unsigned int msf2sec(char *msf) { | |
80 | return ((msf[0] * 60 + msf[1]) * 75) + msf[2]; | |
81 | } | |
82 | ||
83 | static void sec2msf(unsigned int s, char *msf) { | |
84 | msf[0] = s / 75 / 60; | |
85 | s = s - msf[0] * 75 * 60; | |
86 | msf[1] = s / 75; | |
87 | s = s - msf[1] * 75; | |
88 | msf[2] = s; | |
89 | } | |
90 | ||
91 | // divide a string of xx:yy:zz into m, s, f | |
92 | static void tok2msf(char *time, char *msf) { | |
93 | char *token; | |
94 | ||
95 | token = strtok(time, ":"); | |
96 | if (token) { | |
97 | msf[0] = atoi(token); | |
98 | } | |
99 | else { | |
100 | msf[0] = 0; | |
101 | } | |
102 | ||
103 | token = strtok(NULL, ":"); | |
104 | if (token) { | |
105 | msf[1] = atoi(token); | |
106 | } | |
107 | else { | |
108 | msf[1] = 0; | |
109 | } | |
110 | ||
111 | token = strtok(NULL, ":"); | |
112 | if (token) { | |
113 | msf[2] = atoi(token); | |
114 | } | |
115 | else { | |
116 | msf[2] = 0; | |
117 | } | |
118 | } | |
119 | ||
120 | #ifndef _WIN32 | |
121 | static long GetTickCount(void) { | |
122 | static time_t initial_time = 0; | |
123 | struct timeval now; | |
124 | ||
125 | gettimeofday(&now, NULL); | |
126 | ||
127 | if (initial_time == 0) { | |
128 | initial_time = now.tv_sec; | |
129 | } | |
130 | ||
131 | return (now.tv_sec - initial_time) * 1000L + now.tv_usec / 1000L; | |
132 | } | |
133 | #endif | |
134 | ||
135 | // this thread plays audio data | |
136 | #ifdef _WIN32 | |
137 | static void playthread(void *param) | |
138 | #else | |
139 | static void *playthread(void *param) | |
140 | #endif | |
141 | { | |
142 | long d, t, i, s; | |
143 | unsigned char tmp; | |
144 | ||
145 | t = GetTickCount(); | |
146 | ||
147 | while (playing) { | |
148 | d = t - (long)GetTickCount(); | |
149 | if (d <= 0) { | |
150 | d = 1; | |
151 | } | |
152 | else if (d > CDDA_FRAMETIME) { | |
153 | d = CDDA_FRAMETIME; | |
154 | } | |
155 | #ifdef _WIN32 | |
156 | Sleep(d); | |
157 | #else | |
158 | usleep(d * 1000); | |
159 | #endif | |
160 | ||
161 | t = GetTickCount() + CDDA_FRAMETIME; | |
162 | ||
163 | if (subChanMixed) { | |
164 | s = 0; | |
165 | ||
166 | for (i = 0; i < sizeof(sndbuffer) / CD_FRAMESIZE_RAW; i++) { | |
167 | // read one sector | |
168 | d = fread(sndbuffer + CD_FRAMESIZE_RAW * i, 1, CD_FRAMESIZE_RAW, cddaHandle); | |
169 | if (d < CD_FRAMESIZE_RAW) { | |
170 | break; | |
171 | } | |
172 | ||
173 | s += d; | |
174 | ||
175 | // skip the subchannel data | |
176 | fseek(cddaHandle, SUB_FRAMESIZE, SEEK_CUR); | |
177 | } | |
178 | } | |
179 | else { | |
180 | s = fread(sndbuffer, 1, sizeof(sndbuffer), cddaHandle); | |
181 | } | |
182 | ||
183 | if (s == 0) { | |
184 | playing = FALSE; | |
185 | fclose(cddaHandle); | |
186 | cddaHandle = NULL; | |
187 | initial_offset = 0; | |
188 | break; | |
189 | } | |
190 | ||
191 | if (!cdr.Muted && playing) { | |
192 | if (cddaBigEndian) { | |
193 | for (i = 0; i < s / 2; i++) { | |
194 | tmp = sndbuffer[i * 2]; | |
195 | sndbuffer[i * 2] = sndbuffer[i * 2 + 1]; | |
196 | sndbuffer[i * 2 + 1] = tmp; | |
197 | } | |
198 | } | |
199 | ||
200 | SPU_playCDDAchannel((short *)sndbuffer, s); | |
201 | } | |
202 | ||
203 | cddaCurOffset += s; | |
204 | } | |
205 | ||
206 | #ifdef _WIN32 | |
207 | _endthread(); | |
208 | #else | |
209 | pthread_exit(0); | |
210 | return NULL; | |
211 | #endif | |
212 | } | |
213 | ||
214 | // stop the CDDA playback | |
215 | static void stopCDDA() { | |
216 | if (!playing) { | |
217 | return; | |
218 | } | |
219 | ||
220 | playing = FALSE; | |
221 | #ifdef _WIN32 | |
222 | WaitForSingleObject(threadid, INFINITE); | |
223 | #else | |
224 | pthread_join(threadid, NULL); | |
225 | #endif | |
226 | ||
227 | if (cddaHandle != NULL) { | |
228 | fclose(cddaHandle); | |
229 | cddaHandle = NULL; | |
230 | } | |
231 | ||
232 | initial_offset = 0; | |
233 | } | |
234 | ||
235 | // start the CDDA playback | |
236 | static void startCDDA(unsigned int offset) { | |
237 | if (playing) { | |
238 | if (initial_offset == offset) { | |
239 | return; | |
240 | } | |
241 | stopCDDA(); | |
242 | } | |
243 | ||
244 | cddaHandle = fopen(GetIsoFile(), "rb"); | |
245 | if (cddaHandle == NULL) { | |
246 | return; | |
247 | } | |
248 | ||
249 | initial_offset = offset; | |
250 | cddaCurOffset = initial_offset; | |
251 | fseek(cddaHandle, initial_offset, SEEK_SET); | |
252 | ||
253 | playing = TRUE; | |
254 | ||
255 | #ifdef _WIN32 | |
256 | threadid = (HANDLE)_beginthread(playthread, 0, NULL); | |
257 | #else | |
258 | pthread_create(&threadid, NULL, playthread, NULL); | |
259 | #endif | |
260 | } | |
261 | ||
262 | // this function tries to get the .toc file of the given .bin | |
263 | // the necessary data is put into the ti (trackinformation)-array | |
264 | static int parsetoc(const char *isofile) { | |
265 | char tocname[MAXPATHLEN]; | |
266 | FILE *fi; | |
267 | char linebuf[256], dummy[256], name[256]; | |
268 | char *token; | |
269 | char time[20], time2[20]; | |
270 | unsigned int t; | |
271 | ||
272 | numtracks = 0; | |
273 | ||
274 | // copy name of the iso and change extension from .bin to .toc | |
275 | strncpy(tocname, isofile, sizeof(tocname)); | |
276 | tocname[MAXPATHLEN - 1] = '\0'; | |
277 | if (strlen(tocname) >= 4) { | |
278 | strcpy(tocname + strlen(tocname) - 4, ".toc"); | |
279 | } | |
280 | else { | |
281 | return -1; | |
282 | } | |
283 | ||
284 | if ((fi = fopen(tocname, "r")) == NULL) { | |
285 | // try changing extension to .cue (to satisfy some stupid tutorials) | |
286 | strcpy(tocname + strlen(tocname) - 4, ".cue"); | |
287 | if ((fi = fopen(tocname, "r")) == NULL) { | |
288 | // if filename is image.toc.bin, try removing .bin (for Brasero) | |
289 | strcpy(tocname, isofile); | |
290 | t = strlen(tocname); | |
291 | if (t >= 8 && strcmp(tocname + t - 8, ".toc.bin") == 0) { | |
292 | tocname[t - 4] = '\0'; | |
293 | if ((fi = fopen(tocname, "r")) == NULL) { | |
294 | return -1; | |
295 | } | |
296 | } | |
297 | else { | |
298 | return -1; | |
299 | } | |
300 | } | |
301 | } | |
302 | ||
303 | memset(&ti, 0, sizeof(ti)); | |
304 | cddaBigEndian = TRUE; // cdrdao uses big-endian for CD Audio | |
305 | ||
306 | // parse the .toc file | |
307 | while (fgets(linebuf, sizeof(linebuf), fi) != NULL) { | |
308 | // search for tracks | |
309 | strncpy(dummy, linebuf, sizeof(linebuf)); | |
310 | token = strtok(dummy, " "); | |
311 | ||
312 | if (token == NULL) continue; | |
313 | ||
314 | if (!strcmp(token, "TRACK")) { | |
315 | // get type of track | |
316 | token = strtok(NULL, " "); | |
317 | numtracks++; | |
318 | ||
319 | if (!strncmp(token, "MODE2_RAW", 9)) { | |
320 | ti[numtracks].type = DATA; | |
321 | sec2msf(2 * 75, ti[numtracks].start); // assume data track on 0:2:0 | |
322 | ||
323 | // check if this image contains mixed subchannel data | |
324 | token = strtok(NULL, " "); | |
325 | if (token != NULL && !strncmp(token, "RW_RAW", 6)) { | |
326 | subChanMixed = TRUE; | |
327 | subChanRaw = TRUE; | |
328 | } | |
329 | } | |
330 | else if (!strncmp(token, "AUDIO", 5)) { | |
331 | ti[numtracks].type = CDDA; | |
332 | } | |
333 | } | |
334 | else if (!strcmp(token, "DATAFILE")) { | |
335 | if (ti[numtracks].type == CDDA) { | |
336 | sscanf(linebuf, "DATAFILE \"%[^\"]\" #%d %8s", name, &t, time2); | |
337 | t /= CD_FRAMESIZE_RAW + (subChanMixed ? SUB_FRAMESIZE : 0); | |
338 | t += 2 * 75; | |
339 | sec2msf(t, (char *)&ti[numtracks].start); | |
340 | tok2msf((char *)&time2, (char *)&ti[numtracks].length); | |
341 | } | |
342 | else { | |
343 | sscanf(linebuf, "DATAFILE \"%[^\"]\" %8s", name, time); | |
344 | tok2msf((char *)&time, (char *)&ti[numtracks].length); | |
345 | } | |
346 | } | |
347 | else if (!strcmp(token, "FILE")) { | |
348 | sscanf(linebuf, "FILE \"%[^\"]\" #%d %8s %8s", name, &t, time, time2); | |
349 | tok2msf((char *)&time, (char *)&ti[numtracks].start); | |
350 | t /= CD_FRAMESIZE_RAW + (subChanMixed ? SUB_FRAMESIZE : 0); | |
351 | t += msf2sec(ti[numtracks].start) + 2 * 75; | |
352 | sec2msf(t, (char *)&ti[numtracks].start); | |
353 | tok2msf((char *)&time2, (char *)&ti[numtracks].length); | |
354 | } | |
355 | } | |
356 | ||
357 | fclose(fi); | |
358 | ||
359 | return 0; | |
360 | } | |
361 | ||
362 | // this function tries to get the .cue file of the given .bin | |
363 | // the necessary data is put into the ti (trackinformation)-array | |
364 | static int parsecue(const char *isofile) { | |
365 | char cuename[MAXPATHLEN]; | |
366 | FILE *fi; | |
367 | char *token; | |
368 | char time[20]; | |
369 | char *tmp; | |
370 | char linebuf[256], dummy[256]; | |
371 | unsigned int t; | |
372 | ||
373 | numtracks = 0; | |
374 | ||
375 | // copy name of the iso and change extension from .bin to .cue | |
376 | strncpy(cuename, isofile, sizeof(cuename)); | |
377 | cuename[MAXPATHLEN - 1] = '\0'; | |
378 | if (strlen(cuename) >= 4) { | |
379 | strcpy(cuename + strlen(cuename) - 4, ".cue"); | |
380 | } | |
381 | else { | |
382 | return -1; | |
383 | } | |
384 | ||
385 | if ((fi = fopen(cuename, "r")) == NULL) { | |
386 | return -1; | |
387 | } | |
388 | ||
389 | // Some stupid tutorials wrongly tell users to use cdrdao to rip a | |
390 | // "bin/cue" image, which is in fact a "bin/toc" image. So let's check | |
391 | // that... | |
392 | if (fgets(linebuf, sizeof(linebuf), fi) != NULL) { | |
393 | if (!strncmp(linebuf, "CD_ROM_XA", 9)) { | |
394 | // Don't proceed further, as this is actually a .toc file rather | |
395 | // than a .cue file. | |
396 | fclose(fi); | |
397 | return parsetoc(isofile); | |
398 | } | |
399 | fseek(fi, 0, SEEK_SET); | |
400 | } | |
401 | ||
402 | memset(&ti, 0, sizeof(ti)); | |
403 | ||
404 | while (fgets(linebuf, sizeof(linebuf), fi) != NULL) { | |
405 | strncpy(dummy, linebuf, sizeof(linebuf)); | |
406 | token = strtok(dummy, " "); | |
407 | ||
408 | if (token == NULL) { | |
409 | continue; | |
410 | } | |
411 | ||
412 | if (!strcmp(token, "TRACK")){ | |
413 | numtracks++; | |
414 | ||
415 | if (strstr(linebuf, "AUDIO") != NULL) { | |
416 | ti[numtracks].type = CDDA; | |
417 | } | |
418 | else if (strstr(linebuf, "MODE1/2352") != NULL || strstr(linebuf, "MODE2/2352") != NULL) { | |
419 | ti[numtracks].type = DATA; | |
420 | } | |
421 | } | |
422 | else if (!strcmp(token, "INDEX")) { | |
423 | tmp = strstr(linebuf, "INDEX"); | |
424 | if (tmp != NULL) { | |
425 | tmp += strlen("INDEX") + 3; // 3 - space + numeric index | |
426 | while (*tmp == ' ') tmp++; | |
427 | if (*tmp != '\n') sscanf(tmp, "%8s", time); | |
428 | } | |
429 | ||
430 | tok2msf((char *)&time, (char *)&ti[numtracks].start); | |
431 | ||
432 | t = msf2sec(ti[numtracks].start) + 2 * 75; | |
433 | sec2msf(t, ti[numtracks].start); | |
434 | ||
435 | // If we've already seen another track, this is its end | |
436 | if (numtracks > 1) { | |
437 | t = msf2sec(ti[numtracks].start) - msf2sec(ti[numtracks - 1].start); | |
438 | sec2msf(t, ti[numtracks - 1].length); | |
439 | } | |
440 | } | |
441 | } | |
442 | ||
443 | fclose(fi); | |
444 | ||
445 | // Fill out the last track's end based on size | |
446 | if (numtracks >= 1) { | |
447 | fseek(cdHandle, 0, SEEK_END); | |
448 | t = ftell(cdHandle) / 2352 - msf2sec(ti[numtracks].start) + 2 * 75; | |
449 | sec2msf(t, ti[numtracks].length); | |
450 | } | |
451 | ||
452 | return 0; | |
453 | } | |
454 | ||
455 | // this function tries to get the .ccd file of the given .img | |
456 | // the necessary data is put into the ti (trackinformation)-array | |
457 | static int parseccd(const char *isofile) { | |
458 | char ccdname[MAXPATHLEN]; | |
459 | FILE *fi; | |
460 | char linebuf[256]; | |
461 | unsigned int t; | |
462 | ||
463 | numtracks = 0; | |
464 | ||
465 | // copy name of the iso and change extension from .img to .ccd | |
466 | strncpy(ccdname, isofile, sizeof(ccdname)); | |
467 | ccdname[MAXPATHLEN - 1] = '\0'; | |
468 | if (strlen(ccdname) >= 4) { | |
469 | strcpy(ccdname + strlen(ccdname) - 4, ".ccd"); | |
470 | } | |
471 | else { | |
472 | return -1; | |
473 | } | |
474 | ||
475 | if ((fi = fopen(ccdname, "r")) == NULL) { | |
476 | return -1; | |
477 | } | |
478 | ||
479 | memset(&ti, 0, sizeof(ti)); | |
480 | ||
481 | while (fgets(linebuf, sizeof(linebuf), fi) != NULL) { | |
482 | if (!strncmp(linebuf, "[TRACK", 6)){ | |
483 | numtracks++; | |
484 | } | |
485 | else if (!strncmp(linebuf, "MODE=", 5)) { | |
486 | sscanf(linebuf, "MODE=%d", &t); | |
487 | ti[numtracks].type = ((t == 0) ? CDDA : DATA); | |
488 | } | |
489 | else if (!strncmp(linebuf, "INDEX 1=", 8)) { | |
490 | sscanf(linebuf, "INDEX 1=%d", &t); | |
491 | sec2msf(t + 2 * 75, ti[numtracks].start); | |
492 | ||
493 | // If we've already seen another track, this is its end | |
494 | if (numtracks > 1) { | |
495 | t = msf2sec(ti[numtracks].start) - msf2sec(ti[numtracks - 1].start); | |
496 | sec2msf(t, ti[numtracks - 1].length); | |
497 | } | |
498 | } | |
499 | } | |
500 | ||
501 | fclose(fi); | |
502 | ||
503 | // Fill out the last track's end based on size | |
504 | if (numtracks >= 1) { | |
505 | fseek(cdHandle, 0, SEEK_END); | |
506 | t = ftell(cdHandle) / 2352 - msf2sec(ti[numtracks].start) + 2 * 75; | |
507 | sec2msf(t, ti[numtracks].length); | |
508 | } | |
509 | ||
510 | return 0; | |
511 | } | |
512 | ||
513 | // this function tries to get the .mds file of the given .mdf | |
514 | // the necessary data is put into the ti (trackinformation)-array | |
515 | static int parsemds(const char *isofile) { | |
516 | char mdsname[MAXPATHLEN]; | |
517 | FILE *fi; | |
518 | unsigned int offset, extra_offset, l, i; | |
519 | unsigned short s; | |
520 | ||
521 | numtracks = 0; | |
522 | ||
523 | // copy name of the iso and change extension from .mdf to .mds | |
524 | strncpy(mdsname, isofile, sizeof(mdsname)); | |
525 | mdsname[MAXPATHLEN - 1] = '\0'; | |
526 | if (strlen(mdsname) >= 4) { | |
527 | strcpy(mdsname + strlen(mdsname) - 4, ".mds"); | |
528 | } | |
529 | else { | |
530 | return -1; | |
531 | } | |
532 | ||
533 | if ((fi = fopen(mdsname, "rb")) == NULL) { | |
534 | return -1; | |
535 | } | |
536 | ||
537 | memset(&ti, 0, sizeof(ti)); | |
538 | ||
539 | // check if it's a valid mds file | |
540 | fread(&i, 1, sizeof(unsigned int), fi); | |
541 | i = SWAP32(i); | |
542 | if (i != 0x4944454D) { | |
543 | // not an valid mds file | |
544 | fclose(fi); | |
545 | return -1; | |
546 | } | |
547 | ||
548 | // get offset to session block | |
549 | fseek(fi, 0x50, SEEK_SET); | |
550 | fread(&offset, 1, sizeof(unsigned int), fi); | |
551 | offset = SWAP32(offset); | |
552 | ||
553 | // get total number of tracks | |
554 | offset += 14; | |
555 | fseek(fi, offset, SEEK_SET); | |
556 | fread(&s, 1, sizeof(unsigned short), fi); | |
557 | s = SWAP16(s); | |
558 | numtracks = s; | |
559 | ||
560 | // get offset to track blocks | |
561 | fseek(fi, 4, SEEK_CUR); | |
562 | fread(&offset, 1, sizeof(unsigned int), fi); | |
563 | offset = SWAP32(offset); | |
564 | ||
565 | // skip lead-in data | |
566 | while (1) { | |
567 | fseek(fi, offset + 4, SEEK_SET); | |
568 | if (fgetc(fi) < 0xA0) { | |
569 | break; | |
570 | } | |
571 | offset += 0x50; | |
572 | } | |
573 | ||
574 | // check if the image contains mixed subchannel data | |
575 | fseek(fi, offset + 1, SEEK_SET); | |
576 | subChanMixed = (fgetc(fi) ? TRUE : FALSE); | |
577 | ||
578 | // read track data | |
579 | for (i = 1; i <= numtracks; i++) { | |
580 | fseek(fi, offset, SEEK_SET); | |
581 | ||
582 | // get the track type | |
583 | ti[i].type = ((fgetc(fi) == 0xA9) ? CDDA : DATA); | |
584 | fseek(fi, 8, SEEK_CUR); | |
585 | ||
586 | // get the track starting point | |
587 | ti[i].start[0] = fgetc(fi); | |
588 | ti[i].start[1] = fgetc(fi); | |
589 | ti[i].start[2] = fgetc(fi); | |
590 | ||
591 | if (i > 1) { | |
592 | l = msf2sec(ti[i].start); | |
593 | sec2msf(l - 2 * 75, ti[i].start); // ??? | |
594 | } | |
595 | ||
596 | // get the track length | |
597 | fread(&extra_offset, 1, sizeof(unsigned int), fi); | |
598 | extra_offset = SWAP32(extra_offset); | |
599 | ||
600 | fseek(fi, extra_offset + 4, SEEK_SET); | |
601 | fread(&l, 1, sizeof(unsigned int), fi); | |
602 | l = SWAP32(l); | |
603 | sec2msf(l, ti[i].length); | |
604 | ||
605 | offset += 0x50; | |
606 | } | |
607 | ||
608 | fclose(fi); | |
609 | return 0; | |
610 | } | |
611 | ||
612 | // this function tries to get the .sub file of the given .img | |
613 | static int opensubfile(const char *isoname) { | |
614 | char subname[MAXPATHLEN]; | |
615 | ||
616 | // copy name of the iso and change extension from .img to .sub | |
617 | strncpy(subname, isoname, sizeof(subname)); | |
618 | subname[MAXPATHLEN - 1] = '\0'; | |
619 | if (strlen(subname) >= 4) { | |
620 | strcpy(subname + strlen(subname) - 4, ".sub"); | |
621 | } | |
622 | else { | |
623 | return -1; | |
624 | } | |
625 | ||
626 | subHandle = fopen(subname, "rb"); | |
627 | if (subHandle == NULL) { | |
628 | return -1; | |
629 | } | |
630 | ||
631 | return 0; | |
632 | } | |
633 | ||
634 | static long CALLBACK ISOinit(void) { | |
635 | assert(cdHandle == NULL); | |
636 | assert(subHandle == NULL); | |
637 | ||
638 | return 0; // do nothing | |
639 | } | |
640 | ||
641 | static long CALLBACK ISOshutdown(void) { | |
642 | if (cdHandle != NULL) { | |
643 | fclose(cdHandle); | |
644 | cdHandle = NULL; | |
645 | } | |
646 | if (subHandle != NULL) { | |
647 | fclose(subHandle); | |
648 | subHandle = NULL; | |
649 | } | |
650 | stopCDDA(); | |
651 | return 0; | |
652 | } | |
653 | ||
654 | static void PrintTracks(void) { | |
655 | int i; | |
656 | ||
657 | for (i = 1; i <= numtracks; i++) { | |
658 | SysPrintf(_("Track %.2d (%s) - Start %.2d:%.2d:%.2d, Length %.2d:%.2d:%.2d\n"), | |
659 | i, (ti[i].type == DATA ? "DATA" : "AUDIO"), | |
660 | ti[i].start[0], ti[i].start[1], ti[i].start[2], | |
661 | ti[i].length[0], ti[i].length[1], ti[i].length[2]); | |
662 | } | |
663 | } | |
664 | ||
665 | // This function is invoked by the front-end when opening an ISO | |
666 | // file for playback | |
667 | static long CALLBACK ISOopen(void) { | |
668 | if (cdHandle != NULL) { | |
669 | return 0; // it's already open | |
670 | } | |
671 | ||
672 | cdHandle = fopen(GetIsoFile(), "rb"); | |
673 | if (cdHandle == NULL) { | |
674 | return -1; | |
675 | } | |
676 | ||
677 | SysPrintf(_("Loaded CD Image: %s"), GetIsoFile()); | |
678 | ||
679 | cddaBigEndian = FALSE; | |
680 | subChanMixed = FALSE; | |
681 | subChanRaw = FALSE; | |
682 | ||
683 | if (parsecue(GetIsoFile()) == 0) { | |
684 | SysPrintf("[+cue]"); | |
685 | } | |
686 | else if (parsetoc(GetIsoFile()) == 0) { | |
687 | SysPrintf("[+toc]"); | |
688 | } | |
689 | else if (parseccd(GetIsoFile()) == 0) { | |
690 | SysPrintf("[+ccd]"); | |
691 | } | |
692 | else if (parsemds(GetIsoFile()) == 0) { | |
693 | SysPrintf("[+mds]"); | |
694 | } | |
695 | ||
696 | if (!subChanMixed && opensubfile(GetIsoFile()) == 0) { | |
697 | SysPrintf("[+sub]"); | |
698 | } | |
699 | ||
700 | SysPrintf(".\n"); | |
701 | ||
702 | PrintTracks(); | |
703 | ||
704 | return 0; | |
705 | } | |
706 | ||
707 | static long CALLBACK ISOclose(void) { | |
708 | if (cdHandle != NULL) { | |
709 | fclose(cdHandle); | |
710 | cdHandle = NULL; | |
711 | } | |
712 | if (subHandle != NULL) { | |
713 | fclose(subHandle); | |
714 | subHandle = NULL; | |
715 | } | |
716 | stopCDDA(); | |
717 | return 0; | |
718 | } | |
719 | ||
720 | // return Starting and Ending Track | |
721 | // buffer: | |
722 | // byte 0 - start track | |
723 | // byte 1 - end track | |
724 | static long CALLBACK ISOgetTN(unsigned char *buffer) { | |
725 | buffer[0] = 1; | |
726 | ||
727 | if (numtracks > 0) { | |
728 | buffer[1] = numtracks; | |
729 | } | |
730 | else { | |
731 | buffer[1] = 1; | |
732 | } | |
733 | ||
734 | return 0; | |
735 | } | |
736 | ||
737 | // return Track Time | |
738 | // buffer: | |
739 | // byte 0 - frame | |
740 | // byte 1 - second | |
741 | // byte 2 - minute | |
742 | static long CALLBACK ISOgetTD(unsigned char track, unsigned char *buffer) { | |
743 | if (numtracks > 0 && track <= numtracks) { | |
744 | buffer[2] = ti[track].start[0]; | |
745 | buffer[1] = ti[track].start[1]; | |
746 | buffer[0] = ti[track].start[2]; | |
747 | } | |
748 | else { | |
749 | buffer[2] = 0; | |
750 | buffer[1] = 2; | |
751 | buffer[0] = 0; | |
752 | } | |
753 | ||
754 | return 0; | |
755 | } | |
756 | ||
757 | // decode 'raw' subchannel data ripped by cdrdao | |
758 | static void DecodeRawSubData(void) { | |
759 | unsigned char subQData[12]; | |
760 | int i; | |
761 | ||
762 | memset(subQData, 0, sizeof(subQData)); | |
763 | ||
764 | for (i = 0; i < 8 * 12; i++) { | |
765 | if (subbuffer[i] & (1 << 6)) { // only subchannel Q is needed | |
766 | subQData[i >> 3] |= (1 << (7 - (i & 7))); | |
767 | } | |
768 | } | |
769 | ||
770 | memcpy(&subbuffer[12], subQData, 12); | |
771 | } | |
772 | ||
773 | // read track | |
774 | // time: byte 0 - minute; byte 1 - second; byte 2 - frame | |
775 | // uses bcd format | |
776 | static long CALLBACK ISOreadTrack(unsigned char *time) { | |
777 | if (cdHandle == NULL) { | |
778 | return -1; | |
779 | } | |
780 | ||
781 | if (subChanMixed) { | |
782 | fseek(cdHandle, MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2])) * (CD_FRAMESIZE_RAW + SUB_FRAMESIZE) + 12, SEEK_SET); | |
783 | fread(cdbuffer, 1, DATA_SIZE, cdHandle); | |
784 | fread(subbuffer, 1, SUB_FRAMESIZE, cdHandle); | |
785 | ||
786 | if (subChanRaw) DecodeRawSubData(); | |
787 | } | |
788 | else { | |
789 | fseek(cdHandle, MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2])) * CD_FRAMESIZE_RAW + 12, SEEK_SET); | |
790 | fread(cdbuffer, 1, DATA_SIZE, cdHandle); | |
791 | ||
792 | if (subHandle != NULL) { | |
793 | fseek(subHandle, MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2])) * SUB_FRAMESIZE, SEEK_SET); | |
794 | fread(subbuffer, 1, SUB_FRAMESIZE, subHandle); | |
795 | ||
796 | if (subChanRaw) DecodeRawSubData(); | |
797 | } | |
798 | } | |
799 | ||
800 | return 0; | |
801 | } | |
802 | ||
803 | // return readed track | |
804 | static unsigned char * CALLBACK ISOgetBuffer(void) { | |
805 | return cdbuffer; | |
806 | } | |
807 | ||
808 | // plays cdda audio | |
809 | // sector: byte 0 - minute; byte 1 - second; byte 2 - frame | |
810 | // does NOT uses bcd format | |
811 | static long CALLBACK ISOplay(unsigned char *time) { | |
7b8da7ab | 812 | if (numtracks <= 1) |
813 | return 0; | |
814 | ||
ef79bbde P |
815 | if (SPU_playCDDAchannel != NULL) { |
816 | if (subChanMixed) { | |
817 | startCDDA(MSF2SECT(time[0], time[1], time[2]) * (CD_FRAMESIZE_RAW + SUB_FRAMESIZE)); | |
818 | } | |
819 | else { | |
820 | startCDDA(MSF2SECT(time[0], time[1], time[2]) * CD_FRAMESIZE_RAW); | |
821 | } | |
822 | } | |
823 | return 0; | |
824 | } | |
825 | ||
826 | // stops cdda audio | |
827 | static long CALLBACK ISOstop(void) { | |
828 | stopCDDA(); | |
829 | return 0; | |
830 | } | |
831 | ||
832 | // gets subchannel data | |
833 | static unsigned char* CALLBACK ISOgetBufferSub(void) { | |
834 | if (subHandle != NULL || subChanMixed) { | |
835 | return subbuffer; | |
836 | } | |
837 | ||
838 | return NULL; | |
839 | } | |
840 | ||
841 | static long CALLBACK ISOgetStatus(struct CdrStat *stat) { | |
842 | int sec; | |
843 | ||
844 | CDR__getStatus(stat); | |
845 | ||
846 | if (playing) { | |
847 | stat->Type = 0x02; | |
848 | stat->Status |= 0x80; | |
849 | sec = cddaCurOffset / CD_FRAMESIZE_RAW; | |
850 | sec2msf(sec, (char *)stat->Time); | |
851 | } | |
852 | else { | |
853 | stat->Type = 0x01; | |
854 | } | |
855 | ||
856 | return 0; | |
857 | } | |
858 | ||
859 | void cdrIsoInit(void) { | |
860 | CDR_init = ISOinit; | |
861 | CDR_shutdown = ISOshutdown; | |
862 | CDR_open = ISOopen; | |
863 | CDR_close = ISOclose; | |
864 | CDR_getTN = ISOgetTN; | |
865 | CDR_getTD = ISOgetTD; | |
866 | CDR_readTrack = ISOreadTrack; | |
867 | CDR_getBuffer = ISOgetBuffer; | |
868 | CDR_play = ISOplay; | |
869 | CDR_stop = ISOstop; | |
870 | CDR_getBufferSub = ISOgetBufferSub; | |
871 | CDR_getStatus = ISOgetStatus; | |
872 | ||
873 | CDR_getDriveLetter = CDR__getDriveLetter; | |
874 | CDR_configure = CDR__configure; | |
875 | CDR_test = CDR__test; | |
876 | CDR_about = CDR__about; | |
877 | CDR_setfilename = CDR__setfilename; | |
878 | ||
879 | numtracks = 0; | |
880 | } | |
881 | ||
882 | int cdrIsoActive(void) { | |
883 | return (cdHandle != NULL); | |
884 | } |