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