improve ARM feature detection
[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>
bb5cf0fc 15#include <dlfcn.h>
47bf65ab 16
17#include "cdrcimg.h"
18
33716956 19#define PFX "cdrcimg: "
20#define err(f, ...) fprintf(stderr, PFX f, ##__VA_ARGS__)
21
47bf65ab 22#define CD_FRAMESIZE_RAW 2352
23
33716956 24enum {
25 CDRC_ZLIB,
26 CDRC_ZLIB2,
27 CDRC_BZ,
28};
29
47bf65ab 30static const char *cd_fname;
31static unsigned int *cd_index_table;
32static unsigned int cd_index_len;
33716956 33static unsigned int cd_sectors_per_blk;
34static int cd_compression;
47bf65ab 35static FILE *cd_file;
36
bb5cf0fc 37static int (*pBZ2_bzBuffToBuffDecompress)(char *dest, unsigned int *destLen, char *source,
38 unsigned int sourceLen, int small, int verbosity);
39
33716956 40static struct {
41 unsigned char raw[16][CD_FRAMESIZE_RAW];
42 unsigned char compressed[CD_FRAMESIZE_RAW * 16 + 100];
43} *cdbuffer;
44static int current_block, current_sect_in_blk;
47bf65ab 45
46struct CdrStat;
47extern long CDR__getStatus(struct CdrStat *stat);
48
49struct CdrStat
50{
c668f248 51 unsigned int Type;
52 unsigned int Status;
47bf65ab 53 unsigned char Time[3]; // current playing time
54};
55
56struct 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
64static 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
73static 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
86static 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
33716956 95int 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
47bf65ab 126// read track
127// time: byte 0 - minute; byte 1 - second; byte 2 - frame
128// uses bcd format
129static long CDRreadTrack(unsigned char *time)
130{
131 unsigned int start_byte, size;
132 unsigned long cdbuffer_size;
33716956 133 int ret, sector, block;
47bf65ab 134
135 if (cd_file == NULL)
136 return -1;
137
138 sector = MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2]));
33716956 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) {
47bf65ab 160 // it's already there, nothing to do
161 //printf("hit sect %d\n", sector);
162 return 0;
163 }
164
33716956 165 if (sector >= cd_index_len * cd_sectors_per_blk) {
166 err("sector %d is past track end\n", sector);
47bf65ab 167 return -1;
168 }
169
33716956 170 start_byte = cd_index_table[block];
47bf65ab 171 if (fseek(cd_file, start_byte, SEEK_SET) != 0) {
33716956 172 err("seek error for block %d at %x: ",
173 block, start_byte);
47bf65ab 174 perror(NULL);
175 return -1;
176 }
177
33716956 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);
47bf65ab 181 return -1;
182 }
183
33716956 184 if (fread(cdbuffer->compressed, 1, size, cd_file) != size) {
185 err("read error for block %d at %x: ", block, start_byte);
47bf65ab 186 perror(NULL);
187 return -1;
188 }
189
33716956 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:
bb5cf0fc 199 ret = pBZ2_bzBuffToBuffDecompress((char *)cdbuffer->raw, (unsigned int *)&cdbuffer_size,
33716956 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
47bf65ab 207 if (ret != 0) {
33716956 208 err("uncompress failed with %d for block %d, sector %d\n",
209 ret, block, sector);
47bf65ab 210 return -1;
211 }
33716956 212 if (cdbuffer_size != sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk)
213 err("cdbuffer_size: %lu != %d, sector %d\n", cdbuffer_size,
c979c3ee 214 (int)sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk, sector);
47bf65ab 215
216 // done at last!
33716956 217 current_block = block;
47bf65ab 218 return 0;
219}
220
221// return read track
222static unsigned char *CDRgetBuffer(void)
223{
33716956 224 return cdbuffer->raw[current_sect_in_blk] + 12;
47bf65ab 225}
226
227// plays cdda audio
228// sector: byte 0 - minute; byte 1 - second; byte 2 - frame
229// does NOT uses bcd format
230static long CDRplay(unsigned char *time)
231{
232 return 0;
233}
234
235// stops cdda audio
236static long CDRstop(void)
237{
238 return 0;
239}
240
241// gets subchannel data
242static unsigned char* CDRgetBufferSub(void)
243{
244 return NULL;
245}
246
247static long CDRgetStatus(struct CdrStat *stat) {
248 CDR__getStatus(stat);
249
250 stat->Type = 0x01;
251
252 return 0;
253}
254
255static 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
268static long CDRshutdown(void)
269{
270 return CDRclose();
271}
272
273static long CDRinit(void)
274{
33716956 275 if (cdbuffer == NULL) {
276 cdbuffer = malloc(sizeof(*cdbuffer));
277 if (cdbuffer == NULL) {
278 err("OOM\n");
279 return -1;
280 }
281 }
bb5cf0fc 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 }
33716956 294 return 0;
295}
296
297static 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
379fail_index:
380 free(cd_index_table);
381 cd_index_table = NULL;
382fail_io:
383 fclose(f);
384 return -1;
47bf65ab 385}
386
387// This function is invoked by the front-end when opening an ISO
388// file for playback
389static long CDRopen(void)
390{
33716956 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;
47bf65ab 404 char table_fname[256];
405 long table_size;
406 int i, ret;
33716956 407 char *ext;
408 FILE *f = NULL;
47bf65ab 409
410 if (cd_file != NULL)
411 return 0; // it's already open
412
413 numtracks = 0;
33716956 414 current_block = -1;
415 current_sect_in_blk = 0;
47bf65ab 416
417 if (cd_fname == NULL)
418 return -1;
419
33716956 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) {
bb5cf0fc 440 if (pBZ2_bzBuffToBuffDecompress == NULL) {
441 err("libbz2 unavailable for .bz2 handling\n");
442 return -1;
443 }
33716956 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
47bf65ab 453 f = fopen(table_fname, "rb");
454 if (f == NULL) {
33716956 455 err("missing file: %s: ", table_fname);
47bf65ab 456 perror(NULL);
457 return -1;
458 }
459
460 ret = fseek(f, 0, SEEK_END);
461 if (ret != 0) {
33716956 462 err("failed to seek\n");
47bf65ab 463 goto fail_table_io;
464 }
465 table_size = ftell(f);
466 fseek(f, 0, SEEK_SET);
467
33716956 468 if (table_size > 4 * 1024 * 1024) {
469 err(".table too large\n");
47bf65ab 470 goto fail_table_io;
471 }
472
33716956 473 cd_index_len = table_size / tabentry_size;
474
47bf65ab 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
33716956 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;
47bf65ab 513 }
33716956 514 cd_sectors_per_blk = 10;
515 break;
47bf65ab 516 }
47bf65ab 517
518 cd_file = fopen(cd_fname, "rb");
519 if (cd_file == NULL) {
33716956 520 err("failed to open: %s: ", table_fname);
47bf65ab 521 perror(NULL);
522 goto fail_img;
523 }
524 fclose(f);
525
33716956 526 printf(PFX "Loaded compressed CD Image: %s.\n", cd_fname);
47bf65ab 527
528 return 0;
529
530fail_img:
531fail_table_io_read:
532 free(cd_index_table);
533 cd_index_table = NULL;
534fail_table_io:
535 fclose(f);
536 return -1;
537}
538
539#define FUNC(n) { #n, n }
540
541static 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
560void cdrcimg_set_fname(const char *fname)
561{
562 cd_fname = fname;
563}
564
565void *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