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