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