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