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