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