frontend: handle double res rendering, enable on x86_64
[pcsx_rearmed.git] / plugins / cdrcimg / cdrcimg.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2010
3  *
4  * This work is licensed under the terms of any of these licenses
5  * (at your option):
6  *  - GNU GPL, version 2 or later.
7  *  - GNU LGPL, version 2.1 or later.
8  * See the COPYING file in the top-level directory.
9  */
10
11 #include <stdio.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <zlib.h>
15 #if !defined(_WIN32) && !defined(NO_DYLIB)
16 #include <dlfcn.h>
17 #endif
18
19 #include "cdrcimg.h"
20
21 #undef CALLBACK
22 #define CALLBACK
23
24 #define PFX "cdrcimg: "
25 #define err(f, ...) fprintf(stderr, PFX f, ##__VA_ARGS__)
26
27 #define CD_FRAMESIZE_RAW 2352
28
29 enum {
30         CDRC_ZLIB,
31         CDRC_ZLIB2,
32         CDRC_BZ,
33 };
34
35 static const char *cd_fname;
36 static unsigned int *cd_index_table;
37 static unsigned int  cd_index_len;
38 static unsigned int  cd_sectors_per_blk;
39 static int cd_compression;
40 static FILE *cd_file;
41
42 static int (*pBZ2_bzBuffToBuffDecompress)(char *dest, unsigned int *destLen, char *source,
43                 unsigned int sourceLen, int small, int verbosity);
44
45 static struct {
46         unsigned char raw[16][CD_FRAMESIZE_RAW];
47         unsigned char compressed[CD_FRAMESIZE_RAW * 16 + 100];
48 } *cdbuffer;
49 static int current_block, current_sect_in_blk;
50
51 struct CdrStat;
52 extern long CALLBACK CDR__getStatus(struct CdrStat *stat);
53
54 struct CdrStat
55 {
56         unsigned int Type;
57         unsigned int Status;
58         unsigned char Time[3]; // current playing time
59 };
60
61 struct trackinfo {
62         enum {DATA, CDDA} type;
63         char start[3];          // MSF-format
64         char length[3];         // MSF-format
65 };
66
67 #define MAXTRACKS 100 /* How many tracks can a CD hold? */
68
69 static int numtracks = 0;
70
71 #define btoi(b)           ((b) / 16 * 10 + (b) % 16) /* BCD to u_char */
72 #define MSF2SECT(m, s, f) (((m) * 60 + (s) - 2) * 75 + (f))
73
74 // return Starting and Ending Track
75 // buffer:
76 //  byte 0 - start track
77 //  byte 1 - end track
78 static long CDRgetTN(unsigned char *buffer)
79 {
80         buffer[0] = 1;
81         buffer[1] = numtracks > 0 ? numtracks : 1;
82
83         return 0;
84 }
85
86 // return Track Time
87 // buffer:
88 //  byte 0 - frame
89 //  byte 1 - second
90 //  byte 2 - minute
91 static long CDRgetTD(unsigned char track, unsigned char *buffer)
92 {
93         buffer[2] = 0;
94         buffer[1] = 2;
95         buffer[0] = 0;
96
97         return 0;
98 }
99
100 static int uncompress2_pcsx(void *out, unsigned long *out_size, void *in, unsigned long in_size)
101 {
102         static z_stream z;
103         int ret = 0;
104
105         if (z.zalloc == NULL) {
106                 // XXX: one-time leak here..
107                 z.next_in = Z_NULL;
108                 z.avail_in = 0;
109                 z.zalloc = Z_NULL;
110                 z.zfree = Z_NULL;
111                 z.opaque = Z_NULL;
112                 ret = inflateInit2(&z, -15);
113         }
114         else
115                 ret = inflateReset(&z);
116         if (ret != Z_OK)
117                 return ret;
118
119         z.next_in = in;
120         z.avail_in = in_size;
121         z.next_out = out;
122         z.avail_out = *out_size;
123
124         ret = inflate(&z, Z_NO_FLUSH);
125         //inflateEnd(&z);
126
127         *out_size -= z.avail_out;
128         return ret == 1 ? 0 : ret;
129 }
130
131 // read track
132 // time: byte 0 - minute; byte 1 - second; byte 2 - frame
133 // uses bcd format
134 static long CDRreadTrack(unsigned char *time)
135 {
136         unsigned int start_byte, size;
137         unsigned long cdbuffer_size;
138         int ret, sector, block;
139
140         if (cd_file == NULL)
141                 return -1;
142
143         sector = MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2]));
144
145         // avoid division if possible
146         switch (cd_sectors_per_blk) {
147         case 1:
148                 block = sector;
149                 current_sect_in_blk = 0;
150                 break;
151         case 10:
152                 block = sector / 10;
153                 current_sect_in_blk = sector % 10;
154                 break;
155         case 16:
156                 block = sector >> 4;
157                 current_sect_in_blk = sector & 15;
158                 break;
159         default:
160                 err("unhandled cd_sectors_per_blk: %d\n", cd_sectors_per_blk);
161                 return -1;
162         }
163
164         if (block == current_block) {
165                 // it's already there, nothing to do
166                 //printf("hit sect %d\n", sector);
167                 return 0;
168         }
169
170         if (sector >= cd_index_len * cd_sectors_per_blk) {
171                 err("sector %d is past track end\n", sector);
172                 return -1;
173         }
174
175         start_byte = cd_index_table[block];
176         if (fseek(cd_file, start_byte, SEEK_SET) != 0) {
177                 err("seek error for block %d at %x: ",
178                         block, start_byte);
179                 perror(NULL);
180                 return -1;
181         }
182
183         size = cd_index_table[block + 1] - start_byte;
184         if (size > sizeof(cdbuffer->compressed)) {
185                 err("block %d is too large: %u\n", block, size);
186                 return -1;
187         }
188
189         if (fread(cdbuffer->compressed, 1, size, cd_file) != size) {
190                 err("read error for block %d at %x: ", block, start_byte);
191                 perror(NULL);
192                 return -1;
193         }
194
195         cdbuffer_size = sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk;
196         switch (cd_compression) {
197         case CDRC_ZLIB:
198                 ret = uncompress(cdbuffer->raw[0], &cdbuffer_size, cdbuffer->compressed, size);
199                 break;
200         case CDRC_ZLIB2:
201                 ret = uncompress2_pcsx(cdbuffer->raw[0], &cdbuffer_size, cdbuffer->compressed, size);
202                 break;
203         case CDRC_BZ:
204                 ret = pBZ2_bzBuffToBuffDecompress((char *)cdbuffer->raw, (unsigned int *)&cdbuffer_size,
205                         (char *)cdbuffer->compressed, size, 0, 0);
206                 break;
207         default:
208                 err("bad cd_compression: %d\n", cd_compression);
209                 return -1;
210         }
211
212         if (ret != 0) {
213                 err("uncompress failed with %d for block %d, sector %d\n",
214                         ret, block, sector);
215                 return -1;
216         }
217         if (cdbuffer_size != sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk)
218                 err("cdbuffer_size: %lu != %d, sector %d\n", cdbuffer_size,
219                         (int)sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk, sector);
220
221         // done at last!
222         current_block = block;
223         return 0;
224 }
225
226 // return read track
227 static unsigned char *CDRgetBuffer(void)
228 {
229         return cdbuffer->raw[current_sect_in_blk] + 12;
230 }
231
232 // plays cdda audio
233 // sector: byte 0 - minute; byte 1 - second; byte 2 - frame
234 // does NOT uses bcd format
235 static long CDRplay(unsigned char *time)
236 {
237         return 0;
238 }
239
240 // stops cdda audio
241 static long CDRstop(void)
242 {
243         return 0;
244 }
245
246 // gets subchannel data
247 static unsigned char* CDRgetBufferSub(void)
248 {
249         return NULL;
250 }
251
252 static long CDRgetStatus(struct CdrStat *stat) {
253         CDR__getStatus(stat);
254
255         stat->Type = 0x01;
256
257         return 0;
258 }
259
260 static long CDRclose(void)
261 {
262         if (cd_file != NULL) {
263                 fclose(cd_file);
264                 cd_file = NULL;
265         }
266         if (cd_index_table != NULL) {
267                 free(cd_index_table);
268                 cd_index_table = NULL;
269         }
270         return 0;
271 }
272
273 static long CDRshutdown(void)
274 {
275         return CDRclose();
276 }
277
278 static long CDRinit(void)
279 {
280         if (cdbuffer == NULL) {
281                 cdbuffer = malloc(sizeof(*cdbuffer));
282                 if (cdbuffer == NULL) {
283                         err("OOM\n");
284                         return -1;
285                 }
286         }
287 #ifndef _WIN32
288         if (pBZ2_bzBuffToBuffDecompress == NULL) {
289                 void *h = dlopen("/usr/lib/libbz2.so.1", RTLD_LAZY);
290                 if (h == NULL)
291                         h = dlopen("./lib/libbz2.so.1", RTLD_LAZY);
292                 if (h != NULL) {
293                         pBZ2_bzBuffToBuffDecompress = dlsym(h, "BZ2_bzBuffToBuffDecompress");
294                         if (pBZ2_bzBuffToBuffDecompress == NULL) {
295                                 err("dlsym bz2: %s", dlerror());
296                                 dlclose(h);
297                         }
298                 }
299         }
300 #endif
301         return 0;
302 }
303
304 static long handle_eboot(void)
305 {
306         struct {
307                 unsigned int sig;
308                 unsigned int dontcare[8];
309                 unsigned int psar_offs;
310         } pbp_hdr;
311         struct {
312                 unsigned int offset;
313                 unsigned int size;
314                 unsigned int dontcare[6];
315         } index_entry;
316         char psar_sig[9];
317         unsigned int cdimg_base;
318         int i, ret;
319         FILE *f;
320
321         f = fopen(cd_fname, "rb");
322         if (f == NULL) {
323                 err("missing file: %s: ", cd_fname);
324                 perror(NULL);
325                 return -1;
326         }
327
328         ret = fread(&pbp_hdr, 1, sizeof(pbp_hdr), f);
329         if (ret != sizeof(pbp_hdr)) {
330                 err("failed to read pbp\n");
331                 goto fail_io;
332         }
333
334         ret = fseek(f, pbp_hdr.psar_offs, SEEK_SET);
335         if (ret != 0) {
336                 err("failed to seek to %x\n", pbp_hdr.psar_offs);
337                 goto fail_io;
338         }
339
340         ret = fread(psar_sig, 1, sizeof(psar_sig), f);
341         if (ret != sizeof(psar_sig)) {
342                 err("failed to read psar_sig\n");
343                 goto fail_io;
344         }
345
346         psar_sig[8] = 0;
347         if (strcmp(psar_sig, "PSISOIMG") != 0) {
348                 err("bad psar_sig: %s\n", psar_sig);
349                 goto fail_io;
350         }
351
352         // seek to ISO index
353         ret = fseek(f, 0x4000 - sizeof(psar_sig), SEEK_CUR);
354         if (ret != 0) {
355                 err("failed to seek to ISO index\n");
356                 goto fail_io;
357         }
358
359         cd_index_len = (0x100000 - 0x4000) / sizeof(index_entry);
360         cd_index_table = malloc((cd_index_len + 1) * sizeof(cd_index_table[0]));
361         if (cd_index_table == NULL)
362                 goto fail_io;
363
364         cdimg_base = pbp_hdr.psar_offs + 0x100000;
365         for (i = 0; i < cd_index_len; i++) {
366                 ret = fread(&index_entry, 1, sizeof(index_entry), f);
367                 if (ret != sizeof(index_entry)) {
368                         err("failed to read index_entry #%d\n", i);
369                         goto fail_index;
370                 }
371
372                 if (index_entry.size == 0)
373                         break;
374
375                 cd_index_table[i] = cdimg_base + index_entry.offset;
376         }
377         cd_index_table[i] = cdimg_base + index_entry.offset + index_entry.size;
378
379         cd_compression = CDRC_ZLIB2;
380         cd_sectors_per_blk = 16;
381         cd_file = f;
382
383         printf(PFX "Loaded EBOOT CD Image: %s.\n", cd_fname);
384         return 0;
385
386 fail_index:
387         free(cd_index_table);
388         cd_index_table = NULL;
389 fail_io:
390         fclose(f);
391         return -1;
392 }
393
394 // This function is invoked by the front-end when opening an ISO
395 // file for playback
396 static long CDRopen(void)
397 {
398         union {
399                 struct {
400                         unsigned int offset;
401                         unsigned short size;
402                 } __attribute__((packed)) ztab_entry;
403                 struct {
404                         unsigned int offset;
405                         unsigned short size;
406                         unsigned int dontcare;
407                 } __attribute__((packed)) znxtab_entry;
408                 unsigned int bztab_entry;
409         } u;
410         int tabentry_size;
411         char table_fname[256];
412         long table_size;
413         int i, ret;
414         char *ext;
415         FILE *f = NULL;
416
417         if (cd_file != NULL)
418                 return 0; // it's already open
419
420         numtracks = 0;
421         current_block = -1;
422         current_sect_in_blk = 0;
423
424         if (cd_fname == NULL)
425                 return -1;
426
427         ext = strrchr(cd_fname, '.');
428         if (ext == NULL)
429                 return -1;
430
431         if (strcasecmp(ext, ".pbp") == 0) {
432                 return handle_eboot();
433         }
434
435         // pocketiso stuff
436         else if (strcasecmp(ext, ".z") == 0) {
437                 cd_compression = CDRC_ZLIB;
438                 tabentry_size = sizeof(u.ztab_entry);
439                 snprintf(table_fname, sizeof(table_fname), "%s.table", cd_fname);
440         }
441         else if (strcasecmp(ext, ".znx") == 0) {
442                 cd_compression = CDRC_ZLIB;
443                 tabentry_size = sizeof(u.znxtab_entry);
444                 snprintf(table_fname, sizeof(table_fname), "%s.table", cd_fname);
445         }
446         else if (strcasecmp(ext, ".bz") == 0) {
447                 if (pBZ2_bzBuffToBuffDecompress == NULL) {
448                         err("libbz2 unavailable for .bz2 handling\n");
449                         return -1;
450                 }
451                 cd_compression = CDRC_BZ;
452                 tabentry_size = sizeof(u.bztab_entry);
453                 snprintf(table_fname, sizeof(table_fname), "%s.index", cd_fname);
454         }
455         else {
456                 err("unhandled extension: %s\n", ext);
457                 return -1;
458         }
459
460         f = fopen(table_fname, "rb");
461         if (f == NULL) {
462                 err("missing file: %s: ", table_fname);
463                 perror(NULL);
464                 return -1;
465         }
466
467         ret = fseek(f, 0, SEEK_END);
468         if (ret != 0) {
469                 err("failed to seek\n");
470                 goto fail_table_io;
471         }
472         table_size = ftell(f);
473         fseek(f, 0, SEEK_SET);
474
475         if (table_size > 4 * 1024 * 1024) {
476                 err(".table too large\n");
477                 goto fail_table_io;
478         }
479
480         cd_index_len = table_size / tabentry_size;
481
482         cd_index_table = malloc((cd_index_len + 1) * sizeof(cd_index_table[0]));
483         if (cd_index_table == NULL)
484                 goto fail_table_io;
485
486         switch (cd_compression) {
487         case CDRC_ZLIB:
488                 // a Z.table file is binary where each element represents
489                 // one compressed frame.  
490                 //    4 bytes: the offset of the frame in the .Z file
491                 //    2 bytes: the length of the compressed frame
492                 // .znx.table has 4 additional bytes (xa header??)
493                 u.znxtab_entry.dontcare = 0;
494                 for (i = 0; i < cd_index_len; i++) {
495                         ret = fread(&u, 1, tabentry_size, f);
496                         if (ret != tabentry_size) {
497                                 err(".table read failed on entry %d/%d\n", i, cd_index_len);
498                                 goto fail_table_io_read;
499                         }
500                         cd_index_table[i] = u.ztab_entry.offset;
501                         //if (u.znxtab_entry.dontcare != 0)
502                         //      printf("znx %08x!\n", u.znxtab_entry.dontcare);
503                 }
504                 // fake entry, so that we know last compressed block size
505                 cd_index_table[i] = u.ztab_entry.offset + u.ztab_entry.size;
506                 cd_sectors_per_blk = 1;
507                 break;
508         case CDRC_BZ:
509                 // the .BZ.table file is arranged so that one entry represents
510                 // 10 compressed frames. Each element is a 4 byte unsigned integer
511                 // representing the offset in the .BZ file. Last entry is the size
512                 // of the compressed file.
513                 for (i = 0; i < cd_index_len; i++) {
514                         ret = fread(&u.bztab_entry, 1, sizeof(u.bztab_entry), f);
515                         if (ret != sizeof(u.bztab_entry)) {
516                                 err(".table read failed on entry %d/%d\n", i, cd_index_len);
517                                 goto fail_table_io_read;
518                         }
519                         cd_index_table[i] = u.bztab_entry;
520                 }
521                 cd_sectors_per_blk = 10;
522                 break;
523         }
524
525         cd_file = fopen(cd_fname, "rb");
526         if (cd_file == NULL) {
527                 err("failed to open: %s: ", table_fname);
528                 perror(NULL);
529                 goto fail_img;
530         }
531         fclose(f);
532
533         printf(PFX "Loaded compressed CD Image: %s.\n", cd_fname);
534
535         return 0;
536
537 fail_img:
538 fail_table_io_read:
539         free(cd_index_table);
540         cd_index_table = NULL;
541 fail_table_io:
542         fclose(f);
543         return -1;
544 }
545
546 #define FUNC(n) { #n, n }
547
548 static const struct {
549         const char *name;
550         void *func;
551 } plugin_funcs[] = {
552         /* CDR */
553         FUNC(CDRinit),
554         FUNC(CDRshutdown),
555         FUNC(CDRopen),
556         FUNC(CDRclose),
557         FUNC(CDRgetTN),
558         FUNC(CDRgetTD),
559         FUNC(CDRreadTrack),
560         FUNC(CDRgetBuffer),
561         FUNC(CDRgetBufferSub),
562         FUNC(CDRplay),
563         FUNC(CDRstop),
564         FUNC(CDRgetStatus),
565 };
566
567 void cdrcimg_set_fname(const char *fname)
568 {
569         cd_fname = fname;
570 }
571
572 void *cdrcimg_get_sym(const char *sym)
573 {
574         int i;
575         for (i = 0; i < sizeof(plugin_funcs) / sizeof(plugin_funcs[0]); i++)
576                 if (strcmp(plugin_funcs[i].name, sym) == 0)
577                         return plugin_funcs[i].func;
578         return NULL;
579 }
580