c9d94f261da29e976a24c0de3c100888b79d6891
[pcsx_rearmed.git] / libpcsxcore / cdriso.c
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 #include "ppf.h"
26
27 #ifdef _WIN32
28 #include <process.h>
29 #include <windows.h>
30 #else
31 #include <pthread.h>
32 #include <sys/time.h>
33 #endif
34 #include <zlib.h>
35
36 unsigned int cdrIsoMultidiskCount;
37 unsigned int cdrIsoMultidiskSelect;
38
39 static FILE *cdHandle = NULL;
40 static FILE *cddaHandle = NULL;
41 static FILE *subHandle = NULL;
42
43 static boolean subChanMixed = FALSE;
44 static boolean subChanRaw = FALSE;
45 static boolean subChanMissing = FALSE;
46
47 static unsigned char cdbuffer[DATA_SIZE];
48 static unsigned char subbuffer[SUB_FRAMESIZE];
49
50 static unsigned char sndbuffer[CD_FRAMESIZE_RAW * 10];
51
52 #define CDDA_FRAMETIME                  (1000 * (sizeof(sndbuffer) / CD_FRAMESIZE_RAW) / 75)
53
54 #ifdef _WIN32
55 static HANDLE threadid;
56 #else
57 static pthread_t threadid;
58 #endif
59 static unsigned int initial_offset = 0;
60 static boolean playing = FALSE;
61 static boolean cddaBigEndian = FALSE;
62 // offsets of cddaHandle file
63 static unsigned int cdda_cur_sector;
64 static unsigned int cdda_first_sector;
65 // toc_sector - file_sector
66 static unsigned int cdda_toc_delta;
67 /* Frame offset into CD image where pregap data would be found if it was there.
68  * If a game seeks there we must *not* return subchannel data since it's
69  * not in the CD image, so that cdrom code can fake subchannel data instead.
70  * XXX: there could be multiple pregaps but PSX dumps only have one? */
71 static unsigned int pregapOffset;
72
73 // compressed image stuff
74 static struct {
75         unsigned char buff_raw[16][CD_FRAMESIZE_RAW];
76         unsigned char buff_compressed[CD_FRAMESIZE_RAW * 16 + 100];
77         unsigned int *index_table;
78         unsigned int index_len;
79         unsigned int block_shift;
80         unsigned int current_block;
81         unsigned int sector_in_blk;
82 } *compr_img;
83
84 int (*cdimg_read_func)(FILE *f, void *dest, int sector, int offset);
85
86 char* CALLBACK CDR__getDriveLetter(void);
87 long CALLBACK CDR__configure(void);
88 long CALLBACK CDR__test(void);
89 void CALLBACK CDR__about(void);
90 long CALLBACK CDR__setfilename(char *filename);
91 long CALLBACK CDR__getStatus(struct CdrStat *stat);
92
93 static void DecodeRawSubData(void);
94
95 extern void *hCDRDriver;
96
97 struct trackinfo {
98         enum {DATA, CDDA} type;
99         char start[3];          // MSF-format
100         char length[3];         // MSF-format
101         FILE *handle;           // for multi-track images CDDA
102         int start_offset;       // sector offset from start of above file
103 };
104
105 #define MAXTRACKS 100 /* How many tracks can a CD hold? */
106
107 static int numtracks = 0;
108 static struct trackinfo ti[MAXTRACKS];
109
110 // get a sector from a msf-array
111 static unsigned int msf2sec(char *msf) {
112         return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
113 }
114
115 static void sec2msf(unsigned int s, char *msf) {
116         msf[0] = s / 75 / 60;
117         s = s - msf[0] * 75 * 60;
118         msf[1] = s / 75;
119         s = s - msf[1] * 75;
120         msf[2] = s;
121 }
122
123 // divide a string of xx:yy:zz into m, s, f
124 static void tok2msf(char *time, char *msf) {
125         char *token;
126
127         token = strtok(time, ":");
128         if (token) {
129                 msf[0] = atoi(token);
130         }
131         else {
132                 msf[0] = 0;
133         }
134
135         token = strtok(NULL, ":");
136         if (token) {
137                 msf[1] = atoi(token);
138         }
139         else {
140                 msf[1] = 0;
141         }
142
143         token = strtok(NULL, ":");
144         if (token) {
145                 msf[2] = atoi(token);
146         }
147         else {
148                 msf[2] = 0;
149         }
150 }
151
152 #ifndef _WIN32
153 static long GetTickCount(void) {
154         static time_t           initial_time = 0;
155         struct timeval          now;
156
157         gettimeofday(&now, NULL);
158
159         if (initial_time == 0) {
160                 initial_time = now.tv_sec;
161         }
162
163         return (now.tv_sec - initial_time) * 1000L + now.tv_usec / 1000L;
164 }
165 #endif
166
167 // this thread plays audio data
168 #ifdef _WIN32
169 static void playthread(void *param)
170 #else
171 static void *playthread(void *param)
172 #endif
173 {
174         long osleep, d, t, i, s;
175         unsigned char   tmp;
176         int ret = 0;
177
178         t = GetTickCount();
179
180         while (playing) {
181                 s = 0;
182                 for (i = 0; i < sizeof(sndbuffer) / CD_FRAMESIZE_RAW; i++) {
183                         d = cdimg_read_func(cddaHandle, sndbuffer + s, cdda_cur_sector, 0);
184                         if (d < CD_FRAMESIZE_RAW)
185                                 break;
186
187                         if (cdda_cur_sector < cdda_first_sector)
188                                 memset(sndbuffer + s, 0, CD_FRAMESIZE_RAW);
189
190                         s += d;
191                         cdda_cur_sector++;
192                 }
193
194                 if (s == 0) {
195                         playing = FALSE;
196                         initial_offset = 0;
197                         break;
198                 }
199
200                 if (!cdr.Muted && playing) {
201                         if (cddaBigEndian) {
202                                 for (i = 0; i < s / 2; i++) {
203                                         tmp = sndbuffer[i * 2];
204                                         sndbuffer[i * 2] = sndbuffer[i * 2 + 1];
205                                         sndbuffer[i * 2 + 1] = tmp;
206                                 }
207                         }
208
209                         do {
210                                 ret = SPU_playCDDAchannel((short *)sndbuffer, s);
211                                 if (ret == 0x7761)
212                                         usleep(6 * 1000);
213                         } while (ret == 0x7761 && playing); // rearmed_wait
214                 }
215
216                 if (ret != 0x676f) { // !rearmed_go
217                         // do approx sleep
218                         long now;
219
220                         // HACK: stop feeding data while emu is paused
221                         extern int stop;
222                         while (stop && playing)
223                                 usleep(10000);
224
225                         now = GetTickCount();
226                         osleep = t - now;
227                         if (osleep <= 0) {
228                                 osleep = 1;
229                                 t = now;
230                         }
231                         else if (osleep > CDDA_FRAMETIME) {
232                                 osleep = CDDA_FRAMETIME;
233                                 t = now;
234                         }
235
236                         usleep(osleep * 1000);
237                         t += CDDA_FRAMETIME;
238                 }
239
240         }
241
242 #ifdef _WIN32
243         _endthread();
244 #else
245         pthread_exit(0);
246         return NULL;
247 #endif
248 }
249
250 // stop the CDDA playback
251 static void stopCDDA() {
252         if (!playing) {
253                 return;
254         }
255
256         playing = FALSE;
257 #ifdef _WIN32
258         WaitForSingleObject(threadid, INFINITE);
259 #else
260         pthread_join(threadid, NULL);
261 #endif
262 }
263
264 // start the CDDA playback
265 static void startCDDA(unsigned int sector) {
266         if (playing) {
267                 stopCDDA();
268         }
269
270         cdda_cur_sector = sector;
271         playing = TRUE;
272
273 #ifdef _WIN32
274         threadid = (HANDLE)_beginthread(playthread, 0, NULL);
275 #else
276         pthread_create(&threadid, NULL, playthread, NULL);
277 #endif
278 }
279
280 // this function tries to get the .toc file of the given .bin
281 // the necessary data is put into the ti (trackinformation)-array
282 static int parsetoc(const char *isofile) {
283         char                    tocname[MAXPATHLEN];
284         FILE                    *fi;
285         char                    linebuf[256], dummy[256], name[256];
286         char                    *token;
287         char                    time[20], time2[20];
288         unsigned int    t, sector_offs;
289
290         numtracks = 0;
291
292         // copy name of the iso and change extension from .bin to .toc
293         strncpy(tocname, isofile, sizeof(tocname));
294         tocname[MAXPATHLEN - 1] = '\0';
295         if (strlen(tocname) >= 4) {
296                 strcpy(tocname + strlen(tocname) - 4, ".toc");
297         }
298         else {
299                 return -1;
300         }
301
302         if ((fi = fopen(tocname, "r")) == NULL) {
303                 // try changing extension to .cue (to satisfy some stupid tutorials)
304                 strcpy(tocname + strlen(tocname) - 4, ".cue");
305                 if ((fi = fopen(tocname, "r")) == NULL) {
306                         // if filename is image.toc.bin, try removing .bin (for Brasero)
307                         strcpy(tocname, isofile);
308                         t = strlen(tocname);
309                         if (t >= 8 && strcmp(tocname + t - 8, ".toc.bin") == 0) {
310                                 tocname[t - 4] = '\0';
311                                 if ((fi = fopen(tocname, "r")) == NULL) {
312                                         return -1;
313                                 }
314                         }
315                         else {
316                                 return -1;
317                         }
318                 }
319         }
320
321         memset(&ti, 0, sizeof(ti));
322         cddaBigEndian = TRUE; // cdrdao uses big-endian for CD Audio
323
324         sector_offs = 2 * 75;
325
326         // parse the .toc file
327         while (fgets(linebuf, sizeof(linebuf), fi) != NULL) {
328                 // search for tracks
329                 strncpy(dummy, linebuf, sizeof(linebuf));
330                 token = strtok(dummy, " ");
331
332                 if (token == NULL) continue;
333
334                 if (!strcmp(token, "TRACK")) {
335                         // get type of track
336                         token = strtok(NULL, " ");
337                         numtracks++;
338
339                         if (!strncmp(token, "MODE2_RAW", 9)) {
340                                 ti[numtracks].type = DATA;
341                                 sec2msf(2 * 75, ti[numtracks].start); // assume data track on 0:2:0
342
343                                 // check if this image contains mixed subchannel data
344                                 token = strtok(NULL, " ");
345                                 if (token != NULL && !strncmp(token, "RW_RAW", 6)) {
346                                         subChanMixed = TRUE;
347                                         subChanRaw = TRUE;
348                                 }
349                         }
350                         else if (!strncmp(token, "AUDIO", 5)) {
351                                 ti[numtracks].type = CDDA;
352                         }
353                 }
354                 else if (!strcmp(token, "DATAFILE")) {
355                         if (ti[numtracks].type == CDDA) {
356                                 sscanf(linebuf, "DATAFILE \"%[^\"]\" #%d %8s", name, &t, time2);
357                                 t /= CD_FRAMESIZE_RAW + (subChanMixed ? SUB_FRAMESIZE : 0);
358                                 ti[numtracks].start_offset = t;
359                                 t += sector_offs;
360                                 sec2msf(t, (char *)&ti[numtracks].start);
361                                 tok2msf((char *)&time2, (char *)&ti[numtracks].length);
362                         }
363                         else {
364                                 sscanf(linebuf, "DATAFILE \"%[^\"]\" %8s", name, time);
365                                 tok2msf((char *)&time, (char *)&ti[numtracks].length);
366                         }
367                 }
368                 else if (!strcmp(token, "FILE")) {
369                         sscanf(linebuf, "FILE \"%[^\"]\" #%d %8s %8s", name, &t, time, time2);
370                         tok2msf((char *)&time, (char *)&ti[numtracks].start);
371                         t /= CD_FRAMESIZE_RAW + (subChanMixed ? SUB_FRAMESIZE : 0);
372                         ti[numtracks].start_offset = t;
373                         t += msf2sec(ti[numtracks].start) + sector_offs;
374                         sec2msf(t, (char *)&ti[numtracks].start);
375                         tok2msf((char *)&time2, (char *)&ti[numtracks].length);
376                 }
377                 else if (!strcmp(token, "ZERO")) {
378                         sscanf(linebuf, "ZERO AUDIO RW_RAW %8s", time);
379                         tok2msf((char *)&time, dummy);
380                         sector_offs += msf2sec(dummy);
381                         if (numtracks > 1) {
382                                 t = ti[numtracks - 1].start_offset;
383                                 pregapOffset = t + msf2sec(ti[numtracks - 1].length);
384                         }
385                 }
386         }
387
388         fclose(fi);
389
390         return 0;
391 }
392
393 // this function tries to get the .cue file of the given .bin
394 // the necessary data is put into the ti (trackinformation)-array
395 static int parsecue(const char *isofile) {
396         char                    cuename[MAXPATHLEN];
397         char                    filepath[MAXPATHLEN];
398         char                    *incue_fname;
399         FILE                    *fi;
400         char                    *token;
401         char                    time[20];
402         char                    *tmp;
403         char                    linebuf[256], tmpb[256], dummy[256];
404         unsigned int    incue_max_len;
405         unsigned int    t, file_len, sector_offs;
406
407         numtracks = 0;
408
409         // copy name of the iso and change extension from .bin to .cue
410         strncpy(cuename, isofile, sizeof(cuename));
411         cuename[MAXPATHLEN - 1] = '\0';
412         if (strlen(cuename) >= 4) {
413                 strcpy(cuename + strlen(cuename) - 4, ".cue");
414         }
415         else {
416                 return -1;
417         }
418
419         if ((fi = fopen(cuename, "r")) == NULL) {
420                 return -1;
421         }
422
423         // Some stupid tutorials wrongly tell users to use cdrdao to rip a
424         // "bin/cue" image, which is in fact a "bin/toc" image. So let's check
425         // that...
426         if (fgets(linebuf, sizeof(linebuf), fi) != NULL) {
427                 if (!strncmp(linebuf, "CD_ROM_XA", 9)) {
428                         // Don't proceed further, as this is actually a .toc file rather
429                         // than a .cue file.
430                         fclose(fi);
431                         return parsetoc(isofile);
432                 }
433                 fseek(fi, 0, SEEK_SET);
434         }
435
436         // build a path for files referenced in .cue
437         strncpy(filepath, cuename, sizeof(filepath));
438         tmp = strrchr(filepath, '/') + 1;
439         if (tmp == NULL)
440                 tmp = strrchr(filepath, '\\') + 1;
441         if (tmp == NULL)
442                 tmp = filepath;
443         *tmp = 0;
444         filepath[sizeof(filepath) - 1] = 0;
445         incue_fname = tmp;
446         incue_max_len = sizeof(filepath) - (tmp - filepath) - 1;
447
448         memset(&ti, 0, sizeof(ti));
449
450         file_len = 0;
451         sector_offs = 2 * 75;
452
453         while (fgets(linebuf, sizeof(linebuf), fi) != NULL) {
454                 strncpy(dummy, linebuf, sizeof(linebuf));
455                 token = strtok(dummy, " ");
456
457                 if (token == NULL) {
458                         continue;
459                 }
460
461                 if (!strcmp(token, "TRACK")) {
462                         numtracks++;
463
464                         if (strstr(linebuf, "AUDIO") != NULL) {
465                                 ti[numtracks].type = CDDA;
466                         }
467                         else if (strstr(linebuf, "MODE1/2352") != NULL || strstr(linebuf, "MODE2/2352") != NULL) {
468                                 ti[numtracks].type = DATA;
469                         }
470                 }
471                 else if (!strcmp(token, "INDEX")) {
472                         sscanf(linebuf, " INDEX %02d %8s", &t, time);
473                         tok2msf(time, (char *)&ti[numtracks].start);
474
475                         t = msf2sec(ti[numtracks].start);
476                         ti[numtracks].start_offset = t;
477                         t += sector_offs;
478                         sec2msf(t, ti[numtracks].start);
479
480                         // default track length to file length
481                         t = file_len - ti[numtracks].start_offset;
482                         sec2msf(t, ti[numtracks].length);
483
484                         if (numtracks > 1 && ti[numtracks].handle == NULL) {
485                                 // this track uses the same file as the last,
486                                 // start of this track is last track's end
487                                 t = msf2sec(ti[numtracks].start) - msf2sec(ti[numtracks - 1].start);
488                                 sec2msf(t, ti[numtracks - 1].length);
489                         }
490                         if (numtracks > 1 && pregapOffset == -1)
491                                 pregapOffset = ti[numtracks].start_offset;
492                 }
493                 else if (!strcmp(token, "PREGAP")) {
494                         if (sscanf(linebuf, " PREGAP %8s", time) == 1) {
495                                 tok2msf(time, dummy);
496                                 sector_offs += msf2sec(dummy);
497                         }
498                         pregapOffset = -1; // mark to fill track start_offset
499                 }
500                 else if (!strcmp(token, "FILE")) {
501                         sscanf(linebuf, " FILE \"%[^\"]\"", tmpb);
502
503                         // absolute path?
504                         ti[numtracks + 1].handle = fopen(tmpb, "rb");
505                         if (ti[numtracks + 1].handle == NULL) {
506                                 // relative to .cue?
507                                 tmp = strrchr(tmpb, '\\');
508                                 if (tmp == NULL)
509                                         tmp = strrchr(tmpb, '/');
510                                 if (tmp != NULL)
511                                         tmp++;
512                                 else
513                                         tmp = tmpb;
514                                 strncpy(incue_fname, tmp, incue_max_len);
515                                 ti[numtracks + 1].handle = fopen(filepath, "rb");
516                         }
517
518                         // update global offset if this is not first file in this .cue
519                         if (numtracks + 1 > 1)
520                                 sector_offs += file_len;
521
522                         file_len = 0;
523                         if (ti[numtracks + 1].handle == NULL) {
524                                 SysPrintf(_("\ncould not open: %s\n"), filepath);
525                                 continue;
526                         }
527                         fseek(ti[numtracks + 1].handle, 0, SEEK_END);
528                         file_len = ftell(ti[numtracks + 1].handle) / 2352;
529
530                         if (numtracks == 0 && strlen(isofile) >= 4 &&
531                                 strcmp(isofile + strlen(isofile) - 4, ".cue") == 0)
532                         {
533                                 // user selected .cue as image file, use it's data track instead
534                                 fclose(cdHandle);
535                                 cdHandle = fopen(filepath, "rb");
536                         }
537                 }
538         }
539
540         fclose(fi);
541
542         return 0;
543 }
544
545 // this function tries to get the .ccd file of the given .img
546 // the necessary data is put into the ti (trackinformation)-array
547 static int parseccd(const char *isofile) {
548         char                    ccdname[MAXPATHLEN];
549         FILE                    *fi;
550         char                    linebuf[256];
551         unsigned int    t;
552
553         numtracks = 0;
554
555         // copy name of the iso and change extension from .img to .ccd
556         strncpy(ccdname, isofile, sizeof(ccdname));
557         ccdname[MAXPATHLEN - 1] = '\0';
558         if (strlen(ccdname) >= 4) {
559                 strcpy(ccdname + strlen(ccdname) - 4, ".ccd");
560         }
561         else {
562                 return -1;
563         }
564
565         if ((fi = fopen(ccdname, "r")) == NULL) {
566                 return -1;
567         }
568
569         memset(&ti, 0, sizeof(ti));
570
571         while (fgets(linebuf, sizeof(linebuf), fi) != NULL) {
572                 if (!strncmp(linebuf, "[TRACK", 6)){
573                         numtracks++;
574                 }
575                 else if (!strncmp(linebuf, "MODE=", 5)) {
576                         sscanf(linebuf, "MODE=%d", &t);
577                         ti[numtracks].type = ((t == 0) ? CDDA : DATA);
578                 }
579                 else if (!strncmp(linebuf, "INDEX 1=", 8)) {
580                         sscanf(linebuf, "INDEX 1=%d", &t);
581                         sec2msf(t + 2 * 75, ti[numtracks].start);
582                         ti[numtracks].start_offset = t;
583
584                         // If we've already seen another track, this is its end
585                         if (numtracks > 1) {
586                                 t = msf2sec(ti[numtracks].start) - msf2sec(ti[numtracks - 1].start);
587                                 sec2msf(t, ti[numtracks - 1].length);
588                         }
589                 }
590         }
591
592         fclose(fi);
593
594         // Fill out the last track's end based on size
595         if (numtracks >= 1) {
596                 fseek(cdHandle, 0, SEEK_END);
597                 t = ftell(cdHandle) / 2352 - msf2sec(ti[numtracks].start) + 2 * 75;
598                 sec2msf(t, ti[numtracks].length);
599         }
600
601         return 0;
602 }
603
604 // this function tries to get the .mds file of the given .mdf
605 // the necessary data is put into the ti (trackinformation)-array
606 static int parsemds(const char *isofile) {
607         char                    mdsname[MAXPATHLEN];
608         FILE                    *fi;
609         unsigned int    offset, extra_offset, l, i;
610         unsigned short  s;
611
612         numtracks = 0;
613
614         // copy name of the iso and change extension from .mdf to .mds
615         strncpy(mdsname, isofile, sizeof(mdsname));
616         mdsname[MAXPATHLEN - 1] = '\0';
617         if (strlen(mdsname) >= 4) {
618                 strcpy(mdsname + strlen(mdsname) - 4, ".mds");
619         }
620         else {
621                 return -1;
622         }
623
624         if ((fi = fopen(mdsname, "rb")) == NULL) {
625                 return -1;
626         }
627
628         memset(&ti, 0, sizeof(ti));
629
630         // check if it's a valid mds file
631         fread(&i, 1, sizeof(unsigned int), fi);
632         i = SWAP32(i);
633         if (i != 0x4944454D) {
634                 // not an valid mds file
635                 fclose(fi);
636                 return -1;
637         }
638
639         // get offset to session block
640         fseek(fi, 0x50, SEEK_SET);
641         fread(&offset, 1, sizeof(unsigned int), fi);
642         offset = SWAP32(offset);
643
644         // get total number of tracks
645         offset += 14;
646         fseek(fi, offset, SEEK_SET);
647         fread(&s, 1, sizeof(unsigned short), fi);
648         s = SWAP16(s);
649         numtracks = s;
650
651         // get offset to track blocks
652         fseek(fi, 4, SEEK_CUR);
653         fread(&offset, 1, sizeof(unsigned int), fi);
654         offset = SWAP32(offset);
655
656         // skip lead-in data
657         while (1) {
658                 fseek(fi, offset + 4, SEEK_SET);
659                 if (fgetc(fi) < 0xA0) {
660                         break;
661                 }
662                 offset += 0x50;
663         }
664
665         // check if the image contains mixed subchannel data
666         fseek(fi, offset + 1, SEEK_SET);
667         subChanMixed = (fgetc(fi) ? TRUE : FALSE);
668
669         // read track data
670         for (i = 1; i <= numtracks; i++) {
671                 fseek(fi, offset, SEEK_SET);
672
673                 // get the track type
674                 ti[i].type = ((fgetc(fi) == 0xA9) ? CDDA : DATA);
675                 fseek(fi, 8, SEEK_CUR);
676
677                 // get the track starting point
678                 ti[i].start[0] = fgetc(fi);
679                 ti[i].start[1] = fgetc(fi);
680                 ti[i].start[2] = fgetc(fi);
681
682                 fread(&extra_offset, 1, sizeof(unsigned int), fi);
683                 extra_offset = SWAP32(extra_offset);
684
685                 // get track start offset (in .mdf)
686                 fseek(fi, offset + 0x28, SEEK_SET);
687                 fread(&l, 1, sizeof(unsigned int), fi);
688                 l = SWAP32(l);
689                 ti[i].start_offset = l / CD_FRAMESIZE_RAW;
690
691                 // get pregap
692                 fseek(fi, extra_offset, SEEK_SET);
693                 fread(&l, 1, sizeof(unsigned int), fi);
694                 l = SWAP32(l);
695                 if (l != 0 && i > 1)
696                         pregapOffset = ti[i].start_offset;
697
698                 // get the track length
699                 fread(&l, 1, sizeof(unsigned int), fi);
700                 l = SWAP32(l);
701                 sec2msf(l, ti[i].length);
702
703                 offset += 0x50;
704         }
705
706         fclose(fi);
707         return 0;
708 }
709
710 static int handlepbp(const char *isofile) {
711         struct {
712                 unsigned int sig;
713                 unsigned int dontcare[8];
714                 unsigned int psar_offs;
715         } pbp_hdr;
716         struct {
717                 unsigned char type;
718                 unsigned char pad0;
719                 unsigned char track;
720                 char index0[3];
721                 char pad1;
722                 char index1[3];
723         } toc_entry;
724         struct {
725                 unsigned int offset;
726                 unsigned int size;
727                 unsigned int dontcare[6];
728         } index_entry;
729         char psar_sig[11];
730         unsigned int t, cd_length, cdimg_base;
731         unsigned int offsettab[8], psisoimg_offs;
732         const char *ext = NULL;
733         int i, ret;
734
735         if (strlen(isofile) >= 4)
736                 ext = isofile + strlen(isofile) - 4;
737         if (ext == NULL || (strcmp(ext, ".pbp") != 0 && strcmp(ext, ".PBP") != 0))
738                 return -1;
739
740         numtracks = 0;
741
742         ret = fread(&pbp_hdr, 1, sizeof(pbp_hdr), cdHandle);
743         if (ret != sizeof(pbp_hdr)) {
744                 fprintf(stderr, "failed to read pbp\n");
745                 goto fail_io;
746         }
747
748         ret = fseek(cdHandle, pbp_hdr.psar_offs, SEEK_SET);
749         if (ret != 0) {
750                 fprintf(stderr, "failed to seek to %x\n", pbp_hdr.psar_offs);
751                 goto fail_io;
752         }
753
754         psisoimg_offs = pbp_hdr.psar_offs;
755         fread(psar_sig, 1, sizeof(psar_sig), cdHandle);
756         psar_sig[10] = 0;
757         if (strcmp(psar_sig, "PSTITLEIMG") == 0) {
758                 // multidisk image?
759                 ret = fseek(cdHandle, pbp_hdr.psar_offs + 0x200, SEEK_SET);
760                 if (ret != 0) {
761                         fprintf(stderr, "failed to seek to %x\n", pbp_hdr.psar_offs + 0x200);
762                         goto fail_io;
763                 }
764
765                 if (fread(&offsettab, 1, sizeof(offsettab), cdHandle) != sizeof(offsettab)) {
766                         fprintf(stderr, "failed to read offsettab\n");
767                         goto fail_io;
768                 }
769
770                 for (i = 0; i < sizeof(offsettab) / sizeof(offsettab[0]); i++) {
771                         if (offsettab[i] == 0)
772                                 break;
773                 }
774                 cdrIsoMultidiskCount = i;
775                 if (cdrIsoMultidiskCount == 0) {
776                         fprintf(stderr, "multidisk eboot has 0 images?\n");
777                         goto fail_io;
778                 }
779
780                 if (cdrIsoMultidiskSelect >= cdrIsoMultidiskCount)
781                         cdrIsoMultidiskSelect = 0;
782
783                 psisoimg_offs += offsettab[cdrIsoMultidiskSelect];
784
785                 ret = fseek(cdHandle, psisoimg_offs, SEEK_SET);
786                 if (ret != 0) {
787                         fprintf(stderr, "failed to seek to %x\n", psisoimg_offs);
788                         goto fail_io;
789                 }
790
791                 fread(psar_sig, 1, sizeof(psar_sig), cdHandle);
792                 psar_sig[10] = 0;
793         }
794
795         if (strcmp(psar_sig, "PSISOIMG00") != 0) {
796                 fprintf(stderr, "bad psar_sig: %s\n", psar_sig);
797                 goto fail_io;
798         }
799
800         // seek to TOC
801         ret = fseek(cdHandle, psisoimg_offs + 0x800, SEEK_SET);
802         if (ret != 0) {
803                 fprintf(stderr, "failed to seek to %x\n", psisoimg_offs + 0x800);
804                 goto fail_io;
805         }
806
807         // first 3 entries are special
808         fseek(cdHandle, sizeof(toc_entry), SEEK_CUR);
809         fread(&toc_entry, 1, sizeof(toc_entry), cdHandle);
810         numtracks = btoi(toc_entry.index1[0]);
811
812         fread(&toc_entry, 1, sizeof(toc_entry), cdHandle);
813         cd_length = btoi(toc_entry.index1[0]) * 60 * 75 +
814                 btoi(toc_entry.index1[1]) * 75 + btoi(toc_entry.index1[2]);
815
816         for (i = 1; i <= numtracks; i++) {
817                 fread(&toc_entry, 1, sizeof(toc_entry), cdHandle);
818
819                 ti[i].type = (toc_entry.type == 1) ? CDDA : DATA;
820
821                 ti[i].start_offset = btoi(toc_entry.index0[0]) * 60 * 75 +
822                         btoi(toc_entry.index0[1]) * 75 + btoi(toc_entry.index0[2]);
823                 ti[i].start[0] = btoi(toc_entry.index1[0]);
824                 ti[i].start[1] = btoi(toc_entry.index1[1]);
825                 ti[i].start[2] = btoi(toc_entry.index1[2]);
826
827                 if (i > 1) {
828                         t = msf2sec(ti[i].start) - msf2sec(ti[i - 1].start);
829                         sec2msf(t, ti[i - 1].length);
830                 }
831         }
832         t = cd_length - ti[numtracks].start_offset;
833         sec2msf(t, ti[numtracks].length);
834
835         // seek to ISO index
836         ret = fseek(cdHandle, psisoimg_offs + 0x4000, SEEK_SET);
837         if (ret != 0) {
838                 fprintf(stderr, "failed to seek to ISO index\n");
839                 goto fail_io;
840         }
841
842         compr_img = calloc(1, sizeof(*compr_img));
843         if (compr_img == NULL)
844                 goto fail_io;
845
846         compr_img->block_shift = 4;
847         compr_img->current_block = (unsigned int)-1;
848
849         compr_img->index_len = (0x100000 - 0x4000) / sizeof(index_entry);
850         compr_img->index_table = malloc((compr_img->index_len + 1) * sizeof(compr_img->index_table[0]));
851         if (compr_img->index_table == NULL)
852                 goto fail_io;
853
854         cdimg_base = psisoimg_offs + 0x100000;
855         for (i = 0; i < compr_img->index_len; i++) {
856                 ret = fread(&index_entry, 1, sizeof(index_entry), cdHandle);
857                 if (ret != sizeof(index_entry)) {
858                         fprintf(stderr, "failed to read index_entry #%d\n", i);
859                         goto fail_index;
860                 }
861
862                 if (index_entry.size == 0)
863                         break;
864
865                 compr_img->index_table[i] = cdimg_base + index_entry.offset;
866         }
867         compr_img->index_table[i] = cdimg_base + index_entry.offset + index_entry.size;
868
869         return 0;
870
871 fail_index:
872         free(compr_img->index_table);
873         compr_img->index_table = NULL;
874 fail_io:
875         if (compr_img != NULL) {
876                 free(compr_img);
877                 compr_img = NULL;
878         }
879         return -1;
880 }
881
882 static int handlecbin(const char *isofile) {
883         struct
884         {
885                 char magic[4];
886                 unsigned int header_size;
887                 unsigned long long total_bytes;
888                 unsigned int block_size;
889                 unsigned char ver;              // 1
890                 unsigned char align;
891                 unsigned char rsv_06[2];
892         } ciso_hdr;
893         const char *ext = NULL;
894         unsigned int index = 0, plain;
895         int i, ret;
896
897         if (strlen(isofile) >= 5)
898                 ext = isofile + strlen(isofile) - 5;
899         if (ext == NULL || (strcasecmp(ext + 1, ".cbn") != 0 && strcasecmp(ext, ".cbin") != 0))
900                 return -1;
901
902         ret = fread(&ciso_hdr, 1, sizeof(ciso_hdr), cdHandle);
903         if (ret != sizeof(ciso_hdr)) {
904                 fprintf(stderr, "failed to read ciso header\n");
905                 return -1;
906         }
907
908         if (strncmp(ciso_hdr.magic, "CISO", 4) != 0 || ciso_hdr.total_bytes <= 0 || ciso_hdr.block_size <= 0) {
909                 fprintf(stderr, "bad ciso header\n");
910                 return -1;
911         }
912         if (ciso_hdr.header_size != 0 && ciso_hdr.header_size != sizeof(ciso_hdr)) {
913                 ret = fseek(cdHandle, ciso_hdr.header_size, SEEK_SET);
914                 if (ret != 0) {
915                         fprintf(stderr, "failed to seek to %x\n", ciso_hdr.header_size);
916                         return -1;
917                 }
918         }
919
920         compr_img = calloc(1, sizeof(*compr_img));
921         if (compr_img == NULL)
922                 goto fail_io;
923
924         compr_img->block_shift = 0;
925         compr_img->current_block = (unsigned int)-1;
926
927         compr_img->index_len = ciso_hdr.total_bytes / ciso_hdr.block_size;
928         compr_img->index_table = malloc((compr_img->index_len + 1) * sizeof(compr_img->index_table[0]));
929         if (compr_img->index_table == NULL)
930                 goto fail_io;
931
932         ret = fread(compr_img->index_table, sizeof(compr_img->index_table[0]), compr_img->index_len, cdHandle);
933         if (ret != compr_img->index_len) {
934                 fprintf(stderr, "failed to read index table\n");
935                 goto fail_index;
936         }
937
938         for (i = 0; i < compr_img->index_len + 1; i++) {
939                 index = compr_img->index_table[i];
940                 plain = index & 0x80000000;
941                 index &= 0x7fffffff;
942                 compr_img->index_table[i] = (index << ciso_hdr.align) | plain;
943         }
944         if ((long long)index << ciso_hdr.align >= 0x80000000ll)
945                 fprintf(stderr, "warning: ciso img too large, expect problems\n");
946
947         return 0;
948
949 fail_index:
950         free(compr_img->index_table);
951         compr_img->index_table = NULL;
952 fail_io:
953         if (compr_img != NULL) {
954                 free(compr_img);
955                 compr_img = NULL;
956         }
957         return -1;
958 }
959
960 // this function tries to get the .sub file of the given .img
961 static int opensubfile(const char *isoname) {
962         char            subname[MAXPATHLEN];
963
964         // copy name of the iso and change extension from .img to .sub
965         strncpy(subname, isoname, sizeof(subname));
966         subname[MAXPATHLEN - 1] = '\0';
967         if (strlen(subname) >= 4) {
968                 strcpy(subname + strlen(subname) - 4, ".sub");
969         }
970         else {
971                 return -1;
972         }
973
974         subHandle = fopen(subname, "rb");
975         if (subHandle == NULL) {
976                 return -1;
977         }
978
979         return 0;
980 }
981
982 static int opensbifile(const char *isoname) {
983         char            sbiname[MAXPATHLEN];
984         int             s;
985
986         strncpy(sbiname, isoname, sizeof(sbiname));
987         sbiname[MAXPATHLEN - 1] = '\0';
988         if (strlen(sbiname) >= 4) {
989                 strcpy(sbiname + strlen(sbiname) - 4, ".sbi");
990         }
991         else {
992                 return -1;
993         }
994
995         fseek(cdHandle, 0, SEEK_END);
996         s = ftell(cdHandle) / 2352;
997
998         return LoadSBI(sbiname, s);
999 }
1000
1001 static int cdread_normal(FILE *f, void *dest, int sector, int offset)
1002 {
1003         fseek(f, sector * CD_FRAMESIZE_RAW + offset, SEEK_SET);
1004         return fread(dest, 1, CD_FRAMESIZE_RAW - offset, f);
1005 }
1006
1007 static int cdread_sub_mixed(FILE *f, void *dest, int sector, int offset)
1008 {
1009         int ret;
1010
1011         fseek(f, sector * (CD_FRAMESIZE_RAW + SUB_FRAMESIZE) + offset, SEEK_SET);
1012         ret = fread(dest, 1, CD_FRAMESIZE_RAW - offset, f);
1013         fread(subbuffer, 1, SUB_FRAMESIZE, f);
1014
1015         if (subChanRaw) DecodeRawSubData();
1016
1017         return ret;
1018 }
1019
1020 static int uncompress2(void *out, unsigned long *out_size, void *in, unsigned long in_size)
1021 {
1022         static z_stream z;
1023         int ret = 0;
1024
1025         if (z.zalloc == NULL) {
1026                 // XXX: one-time leak here..
1027                 z.next_in = Z_NULL;
1028                 z.avail_in = 0;
1029                 z.zalloc = Z_NULL;
1030                 z.zfree = Z_NULL;
1031                 z.opaque = Z_NULL;
1032                 ret = inflateInit2(&z, -15);
1033         }
1034         else
1035                 ret = inflateReset(&z);
1036         if (ret != Z_OK)
1037                 return ret;
1038
1039         z.next_in = in;
1040         z.avail_in = in_size;
1041         z.next_out = out;
1042         z.avail_out = *out_size;
1043
1044         ret = inflate(&z, Z_NO_FLUSH);
1045         //inflateEnd(&z);
1046
1047         *out_size -= z.avail_out;
1048         return ret == 1 ? 0 : ret;
1049 }
1050
1051 static int cdread_compressed(FILE *f, void *dest, int sector, int offset)
1052 {
1053         unsigned long cdbuffer_size, cdbuffer_size_expect;
1054         unsigned int start_byte, size;
1055         int is_compressed;
1056         int ret, block;
1057
1058         block = sector >> compr_img->block_shift;
1059         compr_img->sector_in_blk = sector & ((1 << compr_img->block_shift) - 1);
1060
1061         if (block == compr_img->current_block) {
1062                 //printf("hit sect %d\n", sector);
1063                 goto finish;
1064         }
1065
1066         if (sector >= compr_img->index_len * 16) {
1067                 fprintf(stderr, "sector %d is past img end\n", sector);
1068                 return -1;
1069         }
1070
1071         start_byte = compr_img->index_table[block] & 0x7fffffff;
1072         if (fseek(cdHandle, start_byte, SEEK_SET) != 0) {
1073                 fprintf(stderr, "seek error for block %d at %x: ",
1074                         block, start_byte);
1075                 perror(NULL);
1076                 return -1;
1077         }
1078
1079         is_compressed = !(compr_img->index_table[block] & 0x80000000);
1080         size = (compr_img->index_table[block + 1] & 0x7fffffff) - start_byte;
1081         if (size > sizeof(compr_img->buff_compressed)) {
1082                 fprintf(stderr, "block %d is too large: %u\n", block, size);
1083                 return -1;
1084         }
1085
1086         if (fread(is_compressed ? compr_img->buff_compressed : compr_img->buff_raw[0],
1087                                 1, size, cdHandle) != size) {
1088                 fprintf(stderr, "read error for block %d at %x: ", block, start_byte);
1089                 perror(NULL);
1090                 return -1;
1091         }
1092
1093         if (is_compressed) {
1094                 cdbuffer_size_expect = sizeof(compr_img->buff_raw[0]) << compr_img->block_shift;
1095                 cdbuffer_size = cdbuffer_size_expect;
1096                 ret = uncompress2(compr_img->buff_raw[0], &cdbuffer_size, compr_img->buff_compressed, size);
1097                 if (ret != 0) {
1098                         fprintf(stderr, "uncompress failed with %d for block %d, sector %d\n",
1099                                         ret, block, sector);
1100                         return -1;
1101                 }
1102                 if (cdbuffer_size != cdbuffer_size_expect)
1103                         fprintf(stderr, "cdbuffer_size: %lu != %lu, sector %d\n", cdbuffer_size,
1104                                         cdbuffer_size_expect, sector);
1105         }
1106
1107         // done at last!
1108         compr_img->current_block = block;
1109
1110 finish:
1111         if (dest != cdbuffer) // copy avoid HACK
1112                 memcpy(dest, compr_img->buff_raw[compr_img->sector_in_blk] + offset,
1113                         CD_FRAMESIZE_RAW - offset);
1114         return CD_FRAMESIZE_RAW - offset;
1115 }
1116
1117 static int cdread_2048(FILE *f, void *dest, int sector, int offset)
1118 {
1119         int ret;
1120
1121         fseek(f, sector * 2048, SEEK_SET);
1122         ret = fread((char *)dest + 12, 1, 2048, f);
1123
1124         // not really necessary, fake mode 2 header
1125         memset(cdbuffer, 0, 12);
1126         sec2msf(sector + 2 * 75, (char *)cdbuffer);
1127         cdbuffer[3] = 1;
1128
1129         return ret;
1130 }
1131
1132 static unsigned char * CALLBACK ISOgetBuffer_compr(void) {
1133         return compr_img->buff_raw[compr_img->sector_in_blk] + 12;
1134 }
1135
1136 static unsigned char * CALLBACK ISOgetBuffer(void) {
1137         return cdbuffer;
1138 }
1139
1140 static void PrintTracks(void) {
1141         int i;
1142
1143         for (i = 1; i <= numtracks; i++) {
1144                 SysPrintf(_("Track %.2d (%s) - Start %.2d:%.2d:%.2d, Length %.2d:%.2d:%.2d\n"),
1145                         i, (ti[i].type == DATA ? "DATA" : "AUDIO"),
1146                         ti[i].start[0], ti[i].start[1], ti[i].start[2],
1147                         ti[i].length[0], ti[i].length[1], ti[i].length[2]);
1148         }
1149 }
1150
1151 // This function is invoked by the front-end when opening an ISO
1152 // file for playback
1153 static long CALLBACK ISOopen(void) {
1154         boolean isMode1ISO = FALSE;
1155
1156         if (cdHandle != NULL) {
1157                 return 0; // it's already open
1158         }
1159
1160         cdHandle = fopen(GetIsoFile(), "rb");
1161         if (cdHandle == NULL) {
1162                 return -1;
1163         }
1164
1165         SysPrintf(_("Loaded CD Image: %s"), GetIsoFile());
1166
1167         cddaBigEndian = FALSE;
1168         subChanMixed = FALSE;
1169         subChanRaw = FALSE;
1170         pregapOffset = 0;
1171         cdrIsoMultidiskCount = 1;
1172
1173         CDR_getBuffer = ISOgetBuffer;
1174         cdimg_read_func = cdread_normal;
1175
1176         if (parsecue(GetIsoFile()) == 0) {
1177                 SysPrintf("[+cue]");
1178         }
1179         else if (parsetoc(GetIsoFile()) == 0) {
1180                 SysPrintf("[+toc]");
1181         }
1182         else if (parseccd(GetIsoFile()) == 0) {
1183                 SysPrintf("[+ccd]");
1184         }
1185         else if (parsemds(GetIsoFile()) == 0) {
1186                 SysPrintf("[+mds]");
1187         }
1188         if (handlepbp(GetIsoFile()) == 0) {
1189                 SysPrintf("[pbp]");
1190                 CDR_getBuffer = ISOgetBuffer_compr;
1191                 cdimg_read_func = cdread_compressed;
1192         }
1193         else if (handlecbin(GetIsoFile()) == 0) {
1194                 SysPrintf("[cbin]");
1195                 CDR_getBuffer = ISOgetBuffer_compr;
1196                 cdimg_read_func = cdread_compressed;
1197         }
1198
1199         if (!subChanMixed && opensubfile(GetIsoFile()) == 0) {
1200                 SysPrintf("[+sub]");
1201         }
1202         if (opensbifile(GetIsoFile()) == 0) {
1203                 SysPrintf("[+sbi]");
1204         }
1205
1206         // guess whether it is mode1/2048
1207         fseek(cdHandle, 0, SEEK_END);
1208         if (ftell(cdHandle) % 2048 == 0) {
1209                 unsigned int modeTest = 0;
1210                 fseek(cdHandle, 0, SEEK_SET);
1211                 fread(&modeTest, 4, 1, cdHandle);
1212                 if (SWAP32(modeTest) != 0xffffff00) {
1213                         SysPrintf("[2048]");
1214                         isMode1ISO = TRUE;
1215                 }
1216         }
1217         fseek(cdHandle, 0, SEEK_SET);
1218
1219         SysPrintf(".\n");
1220
1221         PrintTracks();
1222
1223         if (subChanMixed)
1224                 cdimg_read_func = cdread_sub_mixed;
1225         else if (isMode1ISO)
1226                 cdimg_read_func = cdread_2048;
1227
1228         // make sure we have another handle open for cdda
1229         if (numtracks > 1 && ti[1].handle == NULL) {
1230                 ti[1].handle = fopen(GetIsoFile(), "rb");
1231         }
1232         cdda_cur_sector = cdda_toc_delta = 0;
1233
1234         return 0;
1235 }
1236
1237 static long CALLBACK ISOclose(void) {
1238         int i;
1239
1240         if (cdHandle != NULL) {
1241                 fclose(cdHandle);
1242                 cdHandle = NULL;
1243         }
1244         if (subHandle != NULL) {
1245                 fclose(subHandle);
1246                 subHandle = NULL;
1247         }
1248         stopCDDA();
1249         cddaHandle = NULL;
1250
1251         if (compr_img != NULL) {
1252                 free(compr_img->index_table);
1253                 free(compr_img);
1254                 compr_img = NULL;
1255         }
1256
1257         for (i = 1; i <= numtracks; i++) {
1258                 if (ti[i].handle != NULL) {
1259                         fclose(ti[i].handle);
1260                         ti[i].handle = NULL;
1261                 }
1262         }
1263         numtracks = 0;
1264         UnloadSBI();
1265
1266         return 0;
1267 }
1268
1269 static long CALLBACK ISOinit(void) {
1270         assert(cdHandle == NULL);
1271         assert(subHandle == NULL);
1272
1273         return 0; // do nothing
1274 }
1275
1276 static long CALLBACK ISOshutdown(void) {
1277         ISOclose();
1278         return 0;
1279 }
1280
1281 // return Starting and Ending Track
1282 // buffer:
1283 //  byte 0 - start track
1284 //  byte 1 - end track
1285 static long CALLBACK ISOgetTN(unsigned char *buffer) {
1286         buffer[0] = 1;
1287
1288         if (numtracks > 0) {
1289                 buffer[1] = numtracks;
1290         }
1291         else {
1292                 buffer[1] = 1;
1293         }
1294
1295         return 0;
1296 }
1297
1298 // return Track Time
1299 // buffer:
1300 //  byte 0 - frame
1301 //  byte 1 - second
1302 //  byte 2 - minute
1303 static long CALLBACK ISOgetTD(unsigned char track, unsigned char *buffer) {
1304         if (track == 0) {
1305                 // CD length according pcsxr-svn (done a bit different here)
1306                 unsigned int sect;
1307                 unsigned char time[3];
1308                 sect = msf2sec(ti[numtracks].start) + msf2sec(ti[numtracks].length);
1309                 sec2msf(sect, (char *)time);
1310                 buffer[2] = time[0];
1311                 buffer[1] = time[1];
1312                 buffer[0] = time[2];
1313         }
1314         else if (numtracks > 0 && track <= numtracks) {
1315                 buffer[2] = ti[track].start[0];
1316                 buffer[1] = ti[track].start[1];
1317                 buffer[0] = ti[track].start[2];
1318         }
1319         else {
1320                 buffer[2] = 0;
1321                 buffer[1] = 2;
1322                 buffer[0] = 0;
1323         }
1324
1325         return 0;
1326 }
1327
1328 // decode 'raw' subchannel data ripped by cdrdao
1329 static void DecodeRawSubData(void) {
1330         unsigned char subQData[12];
1331         int i;
1332
1333         memset(subQData, 0, sizeof(subQData));
1334
1335         for (i = 0; i < 8 * 12; i++) {
1336                 if (subbuffer[i] & (1 << 6)) { // only subchannel Q is needed
1337                         subQData[i >> 3] |= (1 << (7 - (i & 7)));
1338                 }
1339         }
1340
1341         memcpy(&subbuffer[12], subQData, 12);
1342 }
1343
1344 // read track
1345 // time: byte 0 - minute; byte 1 - second; byte 2 - frame
1346 // uses bcd format
1347 static long CALLBACK ISOreadTrack(unsigned char *time) {
1348         int sector = MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2]));
1349
1350         if (cdHandle == NULL) {
1351                 return -1;
1352         }
1353
1354         if (pregapOffset) {
1355                 subChanMissing = FALSE;
1356                 if (sector >= pregapOffset) {
1357                         sector -= 2 * 75;
1358                         if (sector < pregapOffset)
1359                                 subChanMissing = TRUE;
1360                 }
1361         }
1362
1363         cdimg_read_func(cdHandle, cdbuffer, sector, 12);
1364
1365         if (subHandle != NULL) {
1366                 fseek(subHandle, sector * SUB_FRAMESIZE, SEEK_SET);
1367                 fread(subbuffer, 1, SUB_FRAMESIZE, subHandle);
1368
1369                 if (subChanRaw) DecodeRawSubData();
1370         }
1371
1372         return 0;
1373 }
1374
1375 // plays cdda audio
1376 // sector: byte 0 - minute; byte 1 - second; byte 2 - frame
1377 // does NOT uses bcd format
1378 static long CALLBACK ISOplay(unsigned char *time) {
1379         unsigned int i, abs_sect, start_sect = 0;
1380         int track_offset, file_sect;
1381
1382         if (numtracks <= 1)
1383                 return 0;
1384
1385         // find the track
1386         abs_sect = msf2sec((char *)time);
1387         for (i = numtracks; i > 1; i--) {
1388                 start_sect = msf2sec(ti[i].start);
1389                 if (start_sect <= abs_sect + 2 * 75)
1390                         break;
1391         }
1392
1393         track_offset = abs_sect - start_sect;
1394         file_sect = ti[i].start_offset + track_offset;
1395         if (file_sect < 0)
1396                 file_sect = 0;
1397
1398         // find the file that contains this track
1399         for (; i > 1; i--)
1400                 if (ti[i].handle != NULL)
1401                         break;
1402
1403         cdda_first_sector = 0;
1404         if (i == 1)
1405                 cdda_first_sector = file_sect - track_offset;
1406
1407         cdda_toc_delta = abs_sect - file_sect;
1408         cddaHandle = ti[i].handle;
1409
1410         if (SPU_playCDDAchannel != NULL) {
1411                 startCDDA(file_sect);
1412         }
1413         return 0;
1414 }
1415
1416 // stops cdda audio
1417 static long CALLBACK ISOstop(void) {
1418         stopCDDA();
1419         return 0;
1420 }
1421
1422 // gets subchannel data
1423 static unsigned char* CALLBACK ISOgetBufferSub(void) {
1424         if ((subHandle != NULL || subChanMixed) && !subChanMissing) {
1425                 return subbuffer;
1426         }
1427
1428         return NULL;
1429 }
1430
1431 static long CALLBACK ISOgetStatus(struct CdrStat *stat) {
1432         int sec;
1433
1434         CDR__getStatus(stat);
1435
1436         if (playing) {
1437                 stat->Type = 0x02;
1438                 stat->Status |= 0x80;
1439         }
1440         else {
1441                 stat->Type = 0x01;
1442         }
1443
1444         sec = cdda_cur_sector + cdda_toc_delta;
1445         sec2msf(sec, (char *)stat->Time);
1446
1447         return 0;
1448 }
1449
1450 void cdrIsoInit(void) {
1451         CDR_init = ISOinit;
1452         CDR_shutdown = ISOshutdown;
1453         CDR_open = ISOopen;
1454         CDR_close = ISOclose;
1455         CDR_getTN = ISOgetTN;
1456         CDR_getTD = ISOgetTD;
1457         CDR_readTrack = ISOreadTrack;
1458         CDR_getBuffer = ISOgetBuffer;
1459         CDR_play = ISOplay;
1460         CDR_stop = ISOstop;
1461         CDR_getBufferSub = ISOgetBufferSub;
1462         CDR_getStatus = ISOgetStatus;
1463
1464         CDR_getDriveLetter = CDR__getDriveLetter;
1465         CDR_configure = CDR__configure;
1466         CDR_test = CDR__test;
1467         CDR_about = CDR__about;
1468         CDR_setfilename = CDR__setfilename;
1469
1470         numtracks = 0;
1471 }
1472
1473 int cdrIsoActive(void) {
1474         return (cdHandle != NULL);
1475 }