2 * (C) GraÅžvydas "notaz" Ignotas, 2010
4 * This work is licensed under the terms of any of these licenses
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.
21 #define WIN32_LEAN_AND_MEAN
27 #define PFX "cdrcimg: "
28 #define err(f, ...) fprintf(stderr, PFX f, ##__VA_ARGS__)
30 #define CD_FRAMESIZE_RAW 2352
38 static const char *cd_fname;
39 static unsigned int *cd_index_table;
40 static unsigned int cd_index_len;
41 static unsigned int cd_sectors_per_blk;
42 static int cd_compression;
45 static int (*pBZ2_bzBuffToBuffDecompress)(char *dest, unsigned int *destLen, char *source,
46 unsigned int sourceLen, int small, int verbosity);
49 unsigned char raw[16][CD_FRAMESIZE_RAW];
50 unsigned char compressed[CD_FRAMESIZE_RAW * 16 + 100];
52 static int current_block, current_sect_in_blk;
55 extern long CALLBACK CDR__getStatus(struct CdrStat *stat);
61 unsigned char Time[3]; // current playing time
65 enum {DATA, CDDA} type;
66 char start[3]; // MSF-format
67 char length[3]; // MSF-format
70 #define MAXTRACKS 100 /* How many tracks can a CD hold? */
72 static int numtracks = 0;
74 #define btoi(b) ((b) / 16 * 10 + (b) % 16) /* BCD to u_char */
75 #define MSF2SECT(m, s, f) (((m) * 60 + (s) - 2) * 75 + (f))
77 // return Starting and Ending Track
79 // byte 0 - start track
81 static long CDRgetTN(unsigned char *buffer)
84 buffer[1] = numtracks > 0 ? numtracks : 1;
94 static long CDRgetTD(unsigned char track, unsigned char *buffer)
103 static int uncompress2_pcsx(void *out, unsigned long *out_size, void *in, unsigned long in_size)
108 if (z.zalloc == NULL) {
109 // XXX: one-time leak here..
115 ret = inflateInit2(&z, -15);
118 ret = inflateReset(&z);
123 z.avail_in = in_size;
125 z.avail_out = *out_size;
127 ret = inflate(&z, Z_NO_FLUSH);
130 *out_size -= z.avail_out;
131 return ret == 1 ? 0 : ret;
135 // time: byte 0 - minute; byte 1 - second; byte 2 - frame
137 static long CDRreadTrack(unsigned char *time)
139 unsigned int start_byte, size;
140 unsigned long cdbuffer_size;
141 int ret, sector, block;
146 sector = MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2]));
148 // avoid division if possible
149 switch (cd_sectors_per_blk) {
152 current_sect_in_blk = 0;
156 current_sect_in_blk = sector % 10;
160 current_sect_in_blk = sector & 15;
163 err("unhandled cd_sectors_per_blk: %d\n", cd_sectors_per_blk);
167 if (block == current_block) {
168 // it's already there, nothing to do
169 //printf("hit sect %d\n", sector);
173 if (sector >= cd_index_len * cd_sectors_per_blk) {
174 err("sector %d is past track end\n", sector);
178 start_byte = cd_index_table[block];
179 if (fseek(cd_file, start_byte, SEEK_SET) != 0) {
180 err("seek error for block %d at %x: ",
186 size = cd_index_table[block + 1] - start_byte;
187 if (size > sizeof(cdbuffer->compressed)) {
188 err("block %d is too large: %u\n", block, size);
192 if (fread(cdbuffer->compressed, 1, size, cd_file) != size) {
193 err("read error for block %d at %x: ", block, start_byte);
198 cdbuffer_size = sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk;
199 switch (cd_compression) {
201 ret = uncompress(cdbuffer->raw[0], &cdbuffer_size, cdbuffer->compressed, size);
204 ret = uncompress2_pcsx(cdbuffer->raw[0], &cdbuffer_size, cdbuffer->compressed, size);
207 ret = pBZ2_bzBuffToBuffDecompress((char *)cdbuffer->raw, (unsigned int *)&cdbuffer_size,
208 (char *)cdbuffer->compressed, size, 0, 0);
211 err("bad cd_compression: %d\n", cd_compression);
216 err("uncompress failed with %d for block %d, sector %d\n",
220 if (cdbuffer_size != sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk)
221 err("cdbuffer_size: %lu != %d, sector %d\n", cdbuffer_size,
222 (int)sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk, sector);
225 current_block = block;
230 static unsigned char *CDRgetBuffer(void)
232 return cdbuffer->raw[current_sect_in_blk] + 12;
236 // sector: byte 0 - minute; byte 1 - second; byte 2 - frame
237 // does NOT uses bcd format
238 static long CDRplay(unsigned char *time)
244 static long CDRstop(void)
249 // gets subchannel data
250 static unsigned char* CDRgetBufferSub(void)
255 static long CDRgetStatus(struct CdrStat *stat) {
256 CDR__getStatus(stat);
263 static long CDRclose(void)
265 if (cd_file != NULL) {
269 if (cd_index_table != NULL) {
270 free(cd_index_table);
271 cd_index_table = NULL;
276 static long CDRshutdown(void)
281 static long CDRinit(void)
283 if (cdbuffer == NULL) {
284 cdbuffer = malloc(sizeof(*cdbuffer));
285 if (cdbuffer == NULL) {
290 #if !defined(_WIN32) && !defined(NO_DYLIB)
291 if (pBZ2_bzBuffToBuffDecompress == NULL) {
292 void *h = dlopen("/usr/lib/libbz2.so.1", RTLD_LAZY);
294 h = dlopen("./lib/libbz2.so.1", RTLD_LAZY);
296 pBZ2_bzBuffToBuffDecompress = dlsym(h, "BZ2_bzBuffToBuffDecompress");
297 if (pBZ2_bzBuffToBuffDecompress == NULL) {
298 err("dlsym bz2: %s", dlerror());
307 static long handle_eboot(void)
311 unsigned int dontcare[8];
312 unsigned int psar_offs;
317 unsigned int dontcare[6];
320 unsigned int cdimg_base;
324 f = fopen(cd_fname, "rb");
326 err("missing file: %s: ", cd_fname);
331 ret = fread(&pbp_hdr, 1, sizeof(pbp_hdr), f);
332 if (ret != sizeof(pbp_hdr)) {
333 err("failed to read pbp\n");
337 ret = fseek(f, pbp_hdr.psar_offs, SEEK_SET);
339 err("failed to seek to %x\n", pbp_hdr.psar_offs);
343 ret = fread(psar_sig, 1, sizeof(psar_sig), f);
344 if (ret != sizeof(psar_sig)) {
345 err("failed to read psar_sig\n");
350 if (strcmp(psar_sig, "PSISOIMG") != 0) {
351 err("bad psar_sig: %s\n", psar_sig);
356 ret = fseek(f, 0x4000 - sizeof(psar_sig), SEEK_CUR);
358 err("failed to seek to ISO index\n");
362 cd_index_len = (0x100000 - 0x4000) / sizeof(index_entry);
363 cd_index_table = malloc((cd_index_len + 1) * sizeof(cd_index_table[0]));
364 if (cd_index_table == NULL)
367 cdimg_base = pbp_hdr.psar_offs + 0x100000;
368 for (i = 0; i < cd_index_len; i++) {
369 ret = fread(&index_entry, 1, sizeof(index_entry), f);
370 if (ret != sizeof(index_entry)) {
371 err("failed to read index_entry #%d\n", i);
375 if (index_entry.size == 0)
378 cd_index_table[i] = cdimg_base + index_entry.offset;
380 cd_index_table[i] = cdimg_base + index_entry.offset + index_entry.size;
382 cd_compression = CDRC_ZLIB2;
383 cd_sectors_per_blk = 16;
386 printf(PFX "Loaded EBOOT CD Image: %s.\n", cd_fname);
390 free(cd_index_table);
391 cd_index_table = NULL;
397 // This function is invoked by the front-end when opening an ISO
399 static long CDRopen(void)
405 } __attribute__((packed)) ztab_entry;
409 unsigned int dontcare;
410 } __attribute__((packed)) znxtab_entry;
411 unsigned int bztab_entry;
414 char table_fname[256];
420 printf("%s cd_file=%d\n", __func__, cd_file == NULL ? 0 : 1);
423 return 0; // it's already open
427 current_sect_in_blk = 0;
429 if (cd_fname == NULL)
432 ext = strrchr(cd_fname, '.');
436 if (strcasecmp(ext, ".pbp") == 0) {
437 return handle_eboot();
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);
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);
451 else if (strcasecmp(ext, ".bz") == 0) {
452 if (pBZ2_bzBuffToBuffDecompress == NULL) {
453 err("libbz2 unavailable for .bz2 handling\n");
456 cd_compression = CDRC_BZ;
457 tabentry_size = sizeof(u.bztab_entry);
458 snprintf(table_fname, sizeof(table_fname), "%s.index", cd_fname);
461 err("unhandled extension: %s\n", ext);
465 f = fopen(table_fname, "rb");
467 err("missing file: %s: ", table_fname);
472 ret = fseek(f, 0, SEEK_END);
474 err("failed to seek\n");
477 table_size = ftell(f);
478 fseek(f, 0, SEEK_SET);
480 if (table_size > 4 * 1024 * 1024) {
481 err(".table too large\n");
485 cd_index_len = table_size / tabentry_size;
487 cd_index_table = malloc((cd_index_len + 1) * sizeof(cd_index_table[0]));
488 if (cd_index_table == NULL)
491 switch (cd_compression) {
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;
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);
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;
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;
524 cd_index_table[i] = u.bztab_entry;
526 cd_sectors_per_blk = 10;
530 cd_file = fopen(cd_fname, "rb");
531 if (cd_file == NULL) {
532 err("failed to open: %s: ", table_fname);
538 printf(PFX "Loaded compressed CD Image: %s.\n", cd_fname);
544 free(cd_index_table);
545 cd_index_table = NULL;
551 #define FUNC(n) { #n, n }
553 static const struct {
566 FUNC(CDRgetBufferSub),
572 void cdrcimg_set_fname(const char *fname)
577 void *cdrcimg_get_sym(const char *sym)
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;