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