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