diff options
Diffstat (limited to 'plugins/cdrcimg/cdrcimg.c')
-rw-r--r-- | plugins/cdrcimg/cdrcimg.c | 342 |
1 files changed, 292 insertions, 50 deletions
diff --git a/plugins/cdrcimg/cdrcimg.c b/plugins/cdrcimg/cdrcimg.c index 608fdc4..32786f2 100644 --- a/plugins/cdrcimg/cdrcimg.c +++ b/plugins/cdrcimg/cdrcimg.c @@ -12,19 +12,33 @@ #include <string.h> #include <stdlib.h> #include <zlib.h> +#include <bzlib.h> #include "cdrcimg.h" +#define PFX "cdrcimg: " +#define err(f, ...) fprintf(stderr, PFX f, ##__VA_ARGS__) + #define CD_FRAMESIZE_RAW 2352 +enum { + CDRC_ZLIB, + CDRC_ZLIB2, + CDRC_BZ, +}; + static const char *cd_fname; static unsigned int *cd_index_table; static unsigned int cd_index_len; +static unsigned int cd_sectors_per_blk; +static int cd_compression; static FILE *cd_file; -static unsigned char cdbuffer[CD_FRAMESIZE_RAW]; -static unsigned char cdbuffer_compressed[CD_FRAMESIZE_RAW + 100]; -static int current_sector; +static struct { + unsigned char raw[16][CD_FRAMESIZE_RAW]; + unsigned char compressed[CD_FRAMESIZE_RAW * 16 + 100]; +} *cdbuffer; +static int current_block, current_sect_in_blk; struct CdrStat; extern long CDR__getStatus(struct CdrStat *stat); @@ -75,6 +89,37 @@ static long CDRgetTD(unsigned char track, unsigned char *buffer) return 0; } +int uncompress2(void *out, unsigned long *out_size, void *in, unsigned long in_size) +{ + static z_stream z; + int ret = 0; + + if (z.zalloc == NULL) { + // XXX: one-time leak here.. + z.next_in = Z_NULL; + z.avail_in = 0; + z.zalloc = Z_NULL; + z.zfree = Z_NULL; + z.opaque = Z_NULL; + ret = inflateInit2(&z, -15); + } + else + ret = inflateReset(&z); + if (ret != Z_OK) + return ret; + + z.next_in = in; + z.avail_in = in_size; + z.next_out = out; + z.avail_out = *out_size; + + ret = inflate(&z, Z_NO_FLUSH); + //inflateEnd(&z); + + *out_size -= z.avail_out; + return ret == 1 ? 0 : ret; +} + // read track // time: byte 0 - minute; byte 1 - second; byte 2 - frame // uses bcd format @@ -82,62 +127,98 @@ static long CDRreadTrack(unsigned char *time) { unsigned int start_byte, size; unsigned long cdbuffer_size; - int ret, sector; + int ret, sector, block; if (cd_file == NULL) return -1; sector = MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2])); - if (sector == current_sector) { + + // avoid division if possible + switch (cd_sectors_per_blk) { + case 1: + block = sector; + current_sect_in_blk = 0; + break; + case 10: + block = sector / 10; + current_sect_in_blk = sector % 10; + break; + case 16: + block = sector >> 4; + current_sect_in_blk = sector & 15; + break; + default: + err("unhandled cd_sectors_per_blk: %d\n", cd_sectors_per_blk); + return -1; + } + + if (block == current_block) { // it's already there, nothing to do //printf("hit sect %d\n", sector); return 0; } - if (sector >= cd_index_len) { - fprintf(stderr, "sector %d is past track end\n", sector); + if (sector >= cd_index_len * cd_sectors_per_blk) { + err("sector %d is past track end\n", sector); return -1; } - start_byte = cd_index_table[sector]; + start_byte = cd_index_table[block]; if (fseek(cd_file, start_byte, SEEK_SET) != 0) { - fprintf(stderr, "seek error for sector %d at %x: ", - sector, start_byte); + err("seek error for block %d at %x: ", + block, start_byte); perror(NULL); return -1; } - size = cd_index_table[sector + 1] - start_byte; - if (size > sizeof(cdbuffer_compressed)) { - fprintf(stderr, "sector %d is too large: %u\n", sector, size); + size = cd_index_table[block + 1] - start_byte; + if (size > sizeof(cdbuffer->compressed)) { + err("block %d is too large: %u\n", block, size); return -1; } - if (fread(cdbuffer_compressed, 1, size, cd_file) != size) { - fprintf(stderr, "read error for sector %d at %x: ", - sector, start_byte); + if (fread(cdbuffer->compressed, 1, size, cd_file) != size) { + err("read error for block %d at %x: ", block, start_byte); perror(NULL); return -1; } - cdbuffer_size = sizeof(cdbuffer); - ret = uncompress(cdbuffer, &cdbuffer_size, cdbuffer_compressed, size); + cdbuffer_size = sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk; + switch (cd_compression) { + case CDRC_ZLIB: + ret = uncompress(cdbuffer->raw[0], &cdbuffer_size, cdbuffer->compressed, size); + break; + case CDRC_ZLIB2: + ret = uncompress2(cdbuffer->raw[0], &cdbuffer_size, cdbuffer->compressed, size); + break; + case CDRC_BZ: + ret = BZ2_bzBuffToBuffDecompress((char *)cdbuffer->raw, (unsigned int *)&cdbuffer_size, + (char *)cdbuffer->compressed, size, 0, 0); + break; + default: + err("bad cd_compression: %d\n", cd_compression); + return -1; + } + if (ret != 0) { - fprintf(stderr, "uncompress failed with %d for sector %d\n", ret, sector); + err("uncompress failed with %d for block %d, sector %d\n", + ret, block, sector); return -1; } - if (cdbuffer_size != sizeof(cdbuffer)) - printf("%lu != %d\n", cdbuffer_size, sizeof(cdbuffer)); + if (cdbuffer_size != sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk) + err("cdbuffer_size: %lu != %d, sector %d\n", cdbuffer_size, + sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk, sector); // done at last! - current_sector = sector; + current_block = block; return 0; } // return read track static unsigned char *CDRgetBuffer(void) { - return cdbuffer + 12; + return cdbuffer->raw[current_sect_in_blk] + 12; } // plays cdda audio @@ -188,81 +269,242 @@ static long CDRshutdown(void) static long CDRinit(void) { - return 0; // do nothing + if (cdbuffer == NULL) { + cdbuffer = malloc(sizeof(*cdbuffer)); + if (cdbuffer == NULL) { + err("OOM\n"); + return -1; + } + } + return 0; +} + +static long handle_eboot(void) +{ + struct { + unsigned int sig; + unsigned int dontcare[8]; + unsigned int psar_offs; + } pbp_hdr; + struct { + unsigned int offset; + unsigned int size; + unsigned int dontcare[6]; + } index_entry; + char psar_sig[9]; + unsigned int cdimg_base; + int i, ret; + FILE *f; + + f = fopen(cd_fname, "rb"); + if (f == NULL) { + err("missing file: %s: ", cd_fname); + perror(NULL); + return -1; + } + + ret = fread(&pbp_hdr, 1, sizeof(pbp_hdr), f); + if (ret != sizeof(pbp_hdr)) { + err("failed to read pbp\n"); + goto fail_io; + } + + ret = fseek(f, pbp_hdr.psar_offs, SEEK_SET); + if (ret != 0) { + err("failed to seek to %x\n", pbp_hdr.psar_offs); + goto fail_io; + } + + ret = fread(psar_sig, 1, sizeof(psar_sig), f); + if (ret != sizeof(psar_sig)) { + err("failed to read psar_sig\n"); + goto fail_io; + } + + psar_sig[8] = 0; + if (strcmp(psar_sig, "PSISOIMG") != 0) { + err("bad psar_sig: %s\n", psar_sig); + goto fail_io; + } + + // seek to ISO index + ret = fseek(f, 0x4000 - sizeof(psar_sig), SEEK_CUR); + if (ret != 0) { + err("failed to seek to ISO index\n"); + goto fail_io; + } + + cd_index_len = (0x100000 - 0x4000) / sizeof(index_entry); + cd_index_table = malloc((cd_index_len + 1) * sizeof(cd_index_table[0])); + if (cd_index_table == NULL) + goto fail_io; + + cdimg_base = pbp_hdr.psar_offs + 0x100000; + for (i = 0; i < cd_index_len; i++) { + ret = fread(&index_entry, 1, sizeof(index_entry), f); + if (ret != sizeof(index_entry)) { + err("failed to read index_entry #%d\n", i); + goto fail_index; + } + + if (index_entry.size == 0) + break; + + cd_index_table[i] = cdimg_base + index_entry.offset; + } + cd_index_table[i] = cdimg_base + index_entry.offset + index_entry.size; + + cd_compression = CDRC_ZLIB2; + cd_sectors_per_blk = 16; + cd_file = f; + + printf(PFX "Loaded EBOOT CD Image: %s.\n", cd_fname); + return 0; + +fail_index: + free(cd_index_table); + cd_index_table = NULL; +fail_io: + fclose(f); + return -1; } // This function is invoked by the front-end when opening an ISO // file for playback static long CDRopen(void) { - // a Z.table file is binary where each element represents - // one compressed frame. - // 4 bytes: the offset of the frame in the .Z file - // 2 bytes: the length of the compressed frame - struct { - unsigned int offset; - unsigned short size; - } __attribute__((packed)) ztab_entry; + union { + struct { + unsigned int offset; + unsigned short size; + } __attribute__((packed)) ztab_entry; + struct { + unsigned int offset; + unsigned short size; + unsigned int dontcare; + } __attribute__((packed)) znxtab_entry; + unsigned int bztab_entry; + } u; + int tabentry_size; char table_fname[256]; long table_size; int i, ret; - FILE *f; + char *ext; + FILE *f = NULL; if (cd_file != NULL) return 0; // it's already open numtracks = 0; + current_block = -1; + current_sect_in_blk = 0; if (cd_fname == NULL) return -1; - snprintf(table_fname, sizeof(table_fname), "%s.table", cd_fname); + ext = strrchr(cd_fname, '.'); + if (ext == NULL) + return -1; + + if (strcasecmp(ext, ".pbp") == 0) { + return handle_eboot(); + } + + // pocketiso stuff + else if (strcasecmp(ext, ".z") == 0) { + cd_compression = CDRC_ZLIB; + tabentry_size = sizeof(u.ztab_entry); + snprintf(table_fname, sizeof(table_fname), "%s.table", cd_fname); + } + else if (strcasecmp(ext, ".znx") == 0) { + cd_compression = CDRC_ZLIB; + tabentry_size = sizeof(u.znxtab_entry); + snprintf(table_fname, sizeof(table_fname), "%s.table", cd_fname); + } + else if (strcasecmp(ext, ".bz") == 0) { + cd_compression = CDRC_BZ; + tabentry_size = sizeof(u.bztab_entry); + snprintf(table_fname, sizeof(table_fname), "%s.index", cd_fname); + } + else { + err("unhandled extension: %s\n", ext); + return -1; + } + f = fopen(table_fname, "rb"); if (f == NULL) { - fprintf(stderr, "missing file: %s: ", table_fname); + err("missing file: %s: ", table_fname); perror(NULL); return -1; } ret = fseek(f, 0, SEEK_END); if (ret != 0) { - fprintf(stderr, "failed to seek\n"); + err("failed to seek\n"); goto fail_table_io; } table_size = ftell(f); fseek(f, 0, SEEK_SET); - if (table_size > 2 * 1024 * 1024) { - fprintf(stderr, ".table too large\n"); + if (table_size > 4 * 1024 * 1024) { + err(".table too large\n"); goto fail_table_io; } - cd_index_len = table_size / 6; + cd_index_len = table_size / tabentry_size; + cd_index_table = malloc((cd_index_len + 1) * sizeof(cd_index_table[0])); if (cd_index_table == NULL) goto fail_table_io; - for (i = 0; i < cd_index_len; i++) { - ret = fread(&ztab_entry, 1, sizeof(ztab_entry), f); - if (ret != sizeof(ztab_entry)) { - fprintf(stderr, ".table read failed on entry %d/%d\n", i, cd_index_len); - goto fail_table_io_read; + switch (cd_compression) { + case CDRC_ZLIB: + // a Z.table file is binary where each element represents + // one compressed frame. + // 4 bytes: the offset of the frame in the .Z file + // 2 bytes: the length of the compressed frame + // .znx.table has 4 additional bytes (xa header??) + u.znxtab_entry.dontcare = 0; + for (i = 0; i < cd_index_len; i++) { + ret = fread(&u, 1, tabentry_size, f); + if (ret != tabentry_size) { + err(".table read failed on entry %d/%d\n", i, cd_index_len); + goto fail_table_io_read; + } + cd_index_table[i] = u.ztab_entry.offset; + //if (u.znxtab_entry.dontcare != 0) + // printf("znx %08x!\n", u.znxtab_entry.dontcare); + } + // fake entry, so that we know last compressed block size + cd_index_table[i] = u.ztab_entry.offset + u.ztab_entry.size; + cd_sectors_per_blk = 1; + break; + case CDRC_BZ: + // the .BZ.table file is arranged so that one entry represents + // 10 compressed frames. Each element is a 4 byte unsigned integer + // representing the offset in the .BZ file. Last entry is the size + // of the compressed file. + for (i = 0; i < cd_index_len; i++) { + ret = fread(&u.bztab_entry, 1, sizeof(u.bztab_entry), f); + if (ret != sizeof(u.bztab_entry)) { + err(".table read failed on entry %d/%d\n", i, cd_index_len); + goto fail_table_io_read; + } + cd_index_table[i] = u.bztab_entry; } - cd_index_table[i] = ztab_entry.offset; + cd_sectors_per_blk = 10; + break; } - // fake entry, so that we know last compressed block size - cd_index_table[i] = ztab_entry.offset + ztab_entry.size; cd_file = fopen(cd_fname, "rb"); if (cd_file == NULL) { - fprintf(stderr, "faied to open: %s: ", table_fname); + err("failed to open: %s: ", table_fname); perror(NULL); goto fail_img; } fclose(f); - printf("Loaded compressed CD Image: %s.\n", cd_fname); - current_sector = -1; + printf(PFX "Loaded compressed CD Image: %s.\n", cd_fname); return 0; |