Fix for compiling it against newer zlib versions.
[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
bb5cf0fc 17#include <dlfcn.h>
003cfc63 18#else
19#define WIN32_LEAN_AND_MEAN
20#include <windows.h>
21#endif
47bf65ab 22
23#include "cdrcimg.h"
24
33716956 25#define PFX "cdrcimg: "
26#define err(f, ...) fprintf(stderr, PFX f, ##__VA_ARGS__)
27
47bf65ab 28#define CD_FRAMESIZE_RAW 2352
29
33716956 30enum {
31 CDRC_ZLIB,
32 CDRC_ZLIB2,
33 CDRC_BZ,
34};
35
47bf65ab 36static const char *cd_fname;
37static unsigned int *cd_index_table;
38static unsigned int cd_index_len;
33716956 39static unsigned int cd_sectors_per_blk;
40static int cd_compression;
47bf65ab 41static FILE *cd_file;
42
bb5cf0fc 43static int (*pBZ2_bzBuffToBuffDecompress)(char *dest, unsigned int *destLen, char *source,
44 unsigned int sourceLen, int small, int verbosity);
45
33716956 46static struct {
47 unsigned char raw[16][CD_FRAMESIZE_RAW];
48 unsigned char compressed[CD_FRAMESIZE_RAW * 16 + 100];
49} *cdbuffer;
50static int current_block, current_sect_in_blk;
47bf65ab 51
52struct CdrStat;
003cfc63 53extern long CALLBACK CDR__getStatus(struct CdrStat *stat);
47bf65ab 54
55struct CdrStat
56{
c668f248 57 unsigned int Type;
58 unsigned int Status;
47bf65ab 59 unsigned char Time[3]; // current playing time
60};
61
62struct 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
70static 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
79static 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
92static 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
2dd6a826 101static int uncompress2_pcsx(void *out, unsigned long *out_size, void *in, unsigned long in_size)
33716956 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
47bf65ab 132// read track
133// time: byte 0 - minute; byte 1 - second; byte 2 - frame
134// uses bcd format
135static long CDRreadTrack(unsigned char *time)
136{
137 unsigned int start_byte, size;
138 unsigned long cdbuffer_size;
33716956 139 int ret, sector, block;
47bf65ab 140
141 if (cd_file == NULL)
142 return -1;
143
144 sector = MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2]));
33716956 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) {
47bf65ab 166 // it's already there, nothing to do
167 //printf("hit sect %d\n", sector);
168 return 0;
169 }
170
33716956 171 if (sector >= cd_index_len * cd_sectors_per_blk) {
172 err("sector %d is past track end\n", sector);
47bf65ab 173 return -1;
174 }
175
33716956 176 start_byte = cd_index_table[block];
47bf65ab 177 if (fseek(cd_file, start_byte, SEEK_SET) != 0) {
33716956 178 err("seek error for block %d at %x: ",
179 block, start_byte);
47bf65ab 180 perror(NULL);
181 return -1;
182 }
183
33716956 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);
47bf65ab 187 return -1;
188 }
189
33716956 190 if (fread(cdbuffer->compressed, 1, size, cd_file) != size) {
191 err("read error for block %d at %x: ", block, start_byte);
47bf65ab 192 perror(NULL);
193 return -1;
194 }
195
33716956 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:
2dd6a826 202 ret = uncompress2_pcsx(cdbuffer->raw[0], &cdbuffer_size, cdbuffer->compressed, size);
33716956 203 break;
204 case CDRC_BZ:
bb5cf0fc 205 ret = pBZ2_bzBuffToBuffDecompress((char *)cdbuffer->raw, (unsigned int *)&cdbuffer_size,
33716956 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
47bf65ab 213 if (ret != 0) {
33716956 214 err("uncompress failed with %d for block %d, sector %d\n",
215 ret, block, sector);
47bf65ab 216 return -1;
217 }
33716956 218 if (cdbuffer_size != sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk)
219 err("cdbuffer_size: %lu != %d, sector %d\n", cdbuffer_size,
c979c3ee 220 (int)sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk, sector);
47bf65ab 221
222 // done at last!
33716956 223 current_block = block;
47bf65ab 224 return 0;
225}
226
227// return read track
228static unsigned char *CDRgetBuffer(void)
229{
33716956 230 return cdbuffer->raw[current_sect_in_blk] + 12;
47bf65ab 231}
232
233// plays cdda audio
234// sector: byte 0 - minute; byte 1 - second; byte 2 - frame
235// does NOT uses bcd format
236static long CDRplay(unsigned char *time)
237{
238 return 0;
239}
240
241// stops cdda audio
242static long CDRstop(void)
243{
244 return 0;
245}
246
247// gets subchannel data
248static unsigned char* CDRgetBufferSub(void)
249{
250 return NULL;
251}
252
253static long CDRgetStatus(struct CdrStat *stat) {
254 CDR__getStatus(stat);
255
256 stat->Type = 0x01;
257
258 return 0;
259}
260
261static 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
274static long CDRshutdown(void)
275{
276 return CDRclose();
277}
278
279static long CDRinit(void)
280{
33716956 281 if (cdbuffer == NULL) {
282 cdbuffer = malloc(sizeof(*cdbuffer));
283 if (cdbuffer == NULL) {
284 err("OOM\n");
285 return -1;
286 }
287 }
003cfc63 288#ifndef _WIN32
bb5cf0fc 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 }
003cfc63 301#endif
33716956 302 return 0;
303}
304
305static 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
387fail_index:
388 free(cd_index_table);
389 cd_index_table = NULL;
390fail_io:
391 fclose(f);
392 return -1;
47bf65ab 393}
394
395// This function is invoked by the front-end when opening an ISO
396// file for playback
397static long CDRopen(void)
398{
33716956 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;
47bf65ab 412 char table_fname[256];
413 long table_size;
414 int i, ret;
33716956 415 char *ext;
416 FILE *f = NULL;
47bf65ab 417
418 if (cd_file != NULL)
419 return 0; // it's already open
420
421 numtracks = 0;
33716956 422 current_block = -1;
423 current_sect_in_blk = 0;
47bf65ab 424
425 if (cd_fname == NULL)
426 return -1;
427
33716956 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) {
bb5cf0fc 448 if (pBZ2_bzBuffToBuffDecompress == NULL) {
449 err("libbz2 unavailable for .bz2 handling\n");
450 return -1;
451 }
33716956 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
47bf65ab 461 f = fopen(table_fname, "rb");
462 if (f == NULL) {
33716956 463 err("missing file: %s: ", table_fname);
47bf65ab 464 perror(NULL);
465 return -1;
466 }
467
468 ret = fseek(f, 0, SEEK_END);
469 if (ret != 0) {
33716956 470 err("failed to seek\n");
47bf65ab 471 goto fail_table_io;
472 }
473 table_size = ftell(f);
474 fseek(f, 0, SEEK_SET);
475
33716956 476 if (table_size > 4 * 1024 * 1024) {
477 err(".table too large\n");
47bf65ab 478 goto fail_table_io;
479 }
480
33716956 481 cd_index_len = table_size / tabentry_size;
482
47bf65ab 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
33716956 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;
47bf65ab 521 }
33716956 522 cd_sectors_per_blk = 10;
523 break;
47bf65ab 524 }
47bf65ab 525
526 cd_file = fopen(cd_fname, "rb");
527 if (cd_file == NULL) {
33716956 528 err("failed to open: %s: ", table_fname);
47bf65ab 529 perror(NULL);
530 goto fail_img;
531 }
532 fclose(f);
533
33716956 534 printf(PFX "Loaded compressed CD Image: %s.\n", cd_fname);
47bf65ab 535
536 return 0;
537
538fail_img:
539fail_table_io_read:
540 free(cd_index_table);
541 cd_index_table = NULL;
542fail_table_io:
543 fclose(f);
544 return -1;
545}
546
547#define FUNC(n) { #n, n }
548
549static 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
568void cdrcimg_set_fname(const char *fname)
569{
570 cd_fname = fname;
571}
572
573void *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