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