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.
19 #define PFX "cdrcimg: "
20 #define err(f, ...) fprintf(stderr, PFX f, ##__VA_ARGS__)
22 #define CD_FRAMESIZE_RAW 2352
30 static const char *cd_fname;
31 static unsigned int *cd_index_table;
32 static unsigned int cd_index_len;
33 static unsigned int cd_sectors_per_blk;
34 static int cd_compression;
38 unsigned char raw[16][CD_FRAMESIZE_RAW];
39 unsigned char compressed[CD_FRAMESIZE_RAW * 16 + 100];
41 static int current_block, current_sect_in_blk;
44 extern long CDR__getStatus(struct CdrStat *stat);
50 unsigned char Time[3]; // current playing time
54 enum {DATA, CDDA} type;
55 char start[3]; // MSF-format
56 char length[3]; // MSF-format
59 #define MAXTRACKS 100 /* How many tracks can a CD hold? */
61 static int numtracks = 0;
63 #define btoi(b) ((b) / 16 * 10 + (b) % 16) /* BCD to u_char */
64 #define MSF2SECT(m, s, f) (((m) * 60 + (s) - 2) * 75 + (f))
66 // return Starting and Ending Track
68 // byte 0 - start track
70 static long CDRgetTN(unsigned char *buffer)
73 buffer[1] = numtracks > 0 ? numtracks : 1;
83 static long CDRgetTD(unsigned char track, unsigned char *buffer)
92 int uncompress2(void *out, unsigned long *out_size, void *in, unsigned long in_size)
97 if (z.zalloc == NULL) {
98 // XXX: one-time leak here..
104 ret = inflateInit2(&z, -15);
107 ret = inflateReset(&z);
112 z.avail_in = in_size;
114 z.avail_out = *out_size;
116 ret = inflate(&z, Z_NO_FLUSH);
119 *out_size -= z.avail_out;
120 return ret == 1 ? 0 : ret;
124 // time: byte 0 - minute; byte 1 - second; byte 2 - frame
126 static long CDRreadTrack(unsigned char *time)
128 unsigned int start_byte, size;
129 unsigned long cdbuffer_size;
130 int ret, sector, block;
135 sector = MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2]));
137 // avoid division if possible
138 switch (cd_sectors_per_blk) {
141 current_sect_in_blk = 0;
145 current_sect_in_blk = sector % 10;
149 current_sect_in_blk = sector & 15;
152 err("unhandled cd_sectors_per_blk: %d\n", cd_sectors_per_blk);
156 if (block == current_block) {
157 // it's already there, nothing to do
158 //printf("hit sect %d\n", sector);
162 if (sector >= cd_index_len * cd_sectors_per_blk) {
163 err("sector %d is past track end\n", sector);
167 start_byte = cd_index_table[block];
168 if (fseek(cd_file, start_byte, SEEK_SET) != 0) {
169 err("seek error for block %d at %x: ",
175 size = cd_index_table[block + 1] - start_byte;
176 if (size > sizeof(cdbuffer->compressed)) {
177 err("block %d is too large: %u\n", block, size);
181 if (fread(cdbuffer->compressed, 1, size, cd_file) != size) {
182 err("read error for block %d at %x: ", block, start_byte);
187 cdbuffer_size = sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk;
188 switch (cd_compression) {
190 ret = uncompress(cdbuffer->raw[0], &cdbuffer_size, cdbuffer->compressed, size);
193 ret = uncompress2(cdbuffer->raw[0], &cdbuffer_size, cdbuffer->compressed, size);
196 ret = BZ2_bzBuffToBuffDecompress((char *)cdbuffer->raw, (unsigned int *)&cdbuffer_size,
197 (char *)cdbuffer->compressed, size, 0, 0);
200 err("bad cd_compression: %d\n", cd_compression);
205 err("uncompress failed with %d for block %d, sector %d\n",
209 if (cdbuffer_size != sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk)
210 err("cdbuffer_size: %lu != %d, sector %d\n", cdbuffer_size,
211 sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk, sector);
214 current_block = block;
219 static unsigned char *CDRgetBuffer(void)
221 return cdbuffer->raw[current_sect_in_blk] + 12;
225 // sector: byte 0 - minute; byte 1 - second; byte 2 - frame
226 // does NOT uses bcd format
227 static long CDRplay(unsigned char *time)
233 static long CDRstop(void)
238 // gets subchannel data
239 static unsigned char* CDRgetBufferSub(void)
244 static long CDRgetStatus(struct CdrStat *stat) {
245 CDR__getStatus(stat);
252 static long CDRclose(void)
254 if (cd_file != NULL) {
258 if (cd_index_table != NULL) {
259 free(cd_index_table);
260 cd_index_table = NULL;
265 static long CDRshutdown(void)
270 static long CDRinit(void)
272 if (cdbuffer == NULL) {
273 cdbuffer = malloc(sizeof(*cdbuffer));
274 if (cdbuffer == NULL) {
282 static long handle_eboot(void)
286 unsigned int dontcare[8];
287 unsigned int psar_offs;
292 unsigned int dontcare[6];
295 unsigned int cdimg_base;
299 f = fopen(cd_fname, "rb");
301 err("missing file: %s: ", cd_fname);
306 ret = fread(&pbp_hdr, 1, sizeof(pbp_hdr), f);
307 if (ret != sizeof(pbp_hdr)) {
308 err("failed to read pbp\n");
312 ret = fseek(f, pbp_hdr.psar_offs, SEEK_SET);
314 err("failed to seek to %x\n", pbp_hdr.psar_offs);
318 ret = fread(psar_sig, 1, sizeof(psar_sig), f);
319 if (ret != sizeof(psar_sig)) {
320 err("failed to read psar_sig\n");
325 if (strcmp(psar_sig, "PSISOIMG") != 0) {
326 err("bad psar_sig: %s\n", psar_sig);
331 ret = fseek(f, 0x4000 - sizeof(psar_sig), SEEK_CUR);
333 err("failed to seek to ISO index\n");
337 cd_index_len = (0x100000 - 0x4000) / sizeof(index_entry);
338 cd_index_table = malloc((cd_index_len + 1) * sizeof(cd_index_table[0]));
339 if (cd_index_table == NULL)
342 cdimg_base = pbp_hdr.psar_offs + 0x100000;
343 for (i = 0; i < cd_index_len; i++) {
344 ret = fread(&index_entry, 1, sizeof(index_entry), f);
345 if (ret != sizeof(index_entry)) {
346 err("failed to read index_entry #%d\n", i);
350 if (index_entry.size == 0)
353 cd_index_table[i] = cdimg_base + index_entry.offset;
355 cd_index_table[i] = cdimg_base + index_entry.offset + index_entry.size;
357 cd_compression = CDRC_ZLIB2;
358 cd_sectors_per_blk = 16;
361 printf(PFX "Loaded EBOOT CD Image: %s.\n", cd_fname);
365 free(cd_index_table);
366 cd_index_table = NULL;
372 // This function is invoked by the front-end when opening an ISO
374 static long CDRopen(void)
380 } __attribute__((packed)) ztab_entry;
384 unsigned int dontcare;
385 } __attribute__((packed)) znxtab_entry;
386 unsigned int bztab_entry;
389 char table_fname[256];
396 return 0; // it's already open
400 current_sect_in_blk = 0;
402 if (cd_fname == NULL)
405 ext = strrchr(cd_fname, '.');
409 if (strcasecmp(ext, ".pbp") == 0) {
410 return handle_eboot();
414 else if (strcasecmp(ext, ".z") == 0) {
415 cd_compression = CDRC_ZLIB;
416 tabentry_size = sizeof(u.ztab_entry);
417 snprintf(table_fname, sizeof(table_fname), "%s.table", cd_fname);
419 else if (strcasecmp(ext, ".znx") == 0) {
420 cd_compression = CDRC_ZLIB;
421 tabentry_size = sizeof(u.znxtab_entry);
422 snprintf(table_fname, sizeof(table_fname), "%s.table", cd_fname);
424 else if (strcasecmp(ext, ".bz") == 0) {
425 cd_compression = CDRC_BZ;
426 tabentry_size = sizeof(u.bztab_entry);
427 snprintf(table_fname, sizeof(table_fname), "%s.index", cd_fname);
430 err("unhandled extension: %s\n", ext);
434 f = fopen(table_fname, "rb");
436 err("missing file: %s: ", table_fname);
441 ret = fseek(f, 0, SEEK_END);
443 err("failed to seek\n");
446 table_size = ftell(f);
447 fseek(f, 0, SEEK_SET);
449 if (table_size > 4 * 1024 * 1024) {
450 err(".table too large\n");
454 cd_index_len = table_size / tabentry_size;
456 cd_index_table = malloc((cd_index_len + 1) * sizeof(cd_index_table[0]));
457 if (cd_index_table == NULL)
460 switch (cd_compression) {
462 // a Z.table file is binary where each element represents
463 // one compressed frame.
464 // 4 bytes: the offset of the frame in the .Z file
465 // 2 bytes: the length of the compressed frame
466 // .znx.table has 4 additional bytes (xa header??)
467 u.znxtab_entry.dontcare = 0;
468 for (i = 0; i < cd_index_len; i++) {
469 ret = fread(&u, 1, tabentry_size, f);
470 if (ret != tabentry_size) {
471 err(".table read failed on entry %d/%d\n", i, cd_index_len);
472 goto fail_table_io_read;
474 cd_index_table[i] = u.ztab_entry.offset;
475 //if (u.znxtab_entry.dontcare != 0)
476 // printf("znx %08x!\n", u.znxtab_entry.dontcare);
478 // fake entry, so that we know last compressed block size
479 cd_index_table[i] = u.ztab_entry.offset + u.ztab_entry.size;
480 cd_sectors_per_blk = 1;
483 // the .BZ.table file is arranged so that one entry represents
484 // 10 compressed frames. Each element is a 4 byte unsigned integer
485 // representing the offset in the .BZ file. Last entry is the size
486 // of the compressed file.
487 for (i = 0; i < cd_index_len; i++) {
488 ret = fread(&u.bztab_entry, 1, sizeof(u.bztab_entry), f);
489 if (ret != sizeof(u.bztab_entry)) {
490 err(".table read failed on entry %d/%d\n", i, cd_index_len);
491 goto fail_table_io_read;
493 cd_index_table[i] = u.bztab_entry;
495 cd_sectors_per_blk = 10;
499 cd_file = fopen(cd_fname, "rb");
500 if (cd_file == NULL) {
501 err("failed to open: %s: ", table_fname);
507 printf(PFX "Loaded compressed CD Image: %s.\n", cd_fname);
513 free(cd_index_table);
514 cd_index_table = NULL;
520 #define FUNC(n) { #n, n }
522 static const struct {
535 FUNC(CDRgetBufferSub),
541 void cdrcimg_set_fname(const char *fname)
546 void *cdrcimg_get_sym(const char *sym)
549 for (i = 0; i < sizeof(plugin_funcs) / sizeof(plugin_funcs[0]); i++)
550 if (strcmp(plugin_funcs[i].name, sym) == 0)
551 return plugin_funcs[i].func;