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