#include #include #include #include #include #include #include #include "../../file_io.h" #include "../../cd.h" #include "mister_chd.h" void lba_to_hunkinfo(chd_file *chd_f, int lba, int *hunknumber, int *hunkoffset) { const chd_header *chd_header = chd_get_header(chd_f); int sectors_per_hunk = chd_header->hunkbytes / chd_header->unitbytes; *hunknumber = lba / sectors_per_hunk; *hunkoffset = lba % sectors_per_hunk; return; } int mister_chd_log(const char *format, ...) { char logline[1024]; va_list args; va_start(args, format); vsprintf(logline, format, args); va_end(args); return printf("\x1b[32m%s\x1b[0m", logline); } chd_error mister_load_chd(const char *filename, toc_t *cd_toc) { cd_toc->last = -1; chd_error err = chd_open(getFullPath(filename), CHD_OPEN_READ, NULL, &cd_toc->chd_f); if (err != CHDERR_NONE) { cd_toc->chd_f = NULL; return err; } //TODO: deal with non v5 chd versions const chd_header *chd_header = chd_get_header(cd_toc->chd_f); if (!chd_header) { chd_close(cd_toc->chd_f); return CHDERR_NO_INTERFACE; //I'm not sure this error condition is possible, so just use whatever } mister_chd_log("hunkbytes %d unitbytes %d logical length %llu\n", chd_header->hunkbytes, chd_header->unitbytes, chd_header->logicalbytes); cd_toc->chd_hunksize = chd_header->hunkbytes; //Set CLOEXEC on underlying FD int chd_fd = fileno((FILE *)chd_core_file(cd_toc->chd_f)->argp); if (chd_fd) fcntl(chd_fd, F_SETFD, FD_CLOEXEC); //Load track info int sector_cnt = 0; for (cd_toc->last = 0; cd_toc->last < 99; cd_toc->last++) { char tmp[512]; int track_id = 0, frames = 0, pregap = 0, postgap = 0; char track_type[64], subtype[32], pgtype[32], pgsub[32]; if (chd_get_metadata(cd_toc->chd_f, CDROM_TRACK_METADATA2_TAG, cd_toc->last, tmp, sizeof(tmp), NULL, NULL, NULL) == CHDERR_NONE) { if (sscanf(tmp, CDROM_TRACK_METADATA2_FORMAT, &track_id, track_type, subtype, &frames, &pregap, pgtype, pgsub, &postgap) != 8) break; } else if (chd_get_metadata(cd_toc->chd_f, CDROM_TRACK_METADATA_TAG, cd_toc->last, tmp, sizeof(tmp), NULL, NULL, NULL) == CHDERR_NONE) { if (sscanf(tmp, CDROM_TRACK_METADATA_FORMAT, &track_id, track_type, subtype, &frames) != 4) break; } else { //No more tracks break; } bool pregap_valid = true; if (pgtype[0] != 'V') { pregap_valid = false; } if (cd_toc->last) { if (!pregap_valid) { cd_toc->tracks[cd_toc->last - 1].end += pregap; } cd_toc->end = cd_toc->tracks[cd_toc->last - 1].end; cd_toc->tracks[cd_toc->last].start = cd_toc->end; if (pregap_valid) { cd_toc->tracks[cd_toc->last].start += pregap; } cd_toc->tracks[cd_toc->last].indexes[1] = pregap; } else { if (pregap_valid) { cd_toc->tracks[cd_toc->last].start = pregap; } else { cd_toc->tracks[cd_toc->last].start = 0; } cd_toc->tracks[cd_toc->last].indexes[1] = pregap; } cd_toc->tracks[cd_toc->last].index_num = 2; if (!pregap_valid) { //Pregap sectors are NOT included in the CHD for this track pregap = 0; } if (!strcmp(track_type, "MODE1_RAW")) { cd_toc->tracks[cd_toc->last].sector_size = 2352; cd_toc->tracks[cd_toc->last].type = TT_MODE1; } else if (!strcmp(track_type, "MODE2_RAW")) { cd_toc->tracks[cd_toc->last].sector_size = 2352; cd_toc->tracks[cd_toc->last].type = TT_MODE2; } else if (!strcmp(track_type, "MODE1")) { cd_toc->tracks[cd_toc->last].sector_size = 2048; cd_toc->tracks[cd_toc->last].type = TT_MODE1; } else if (!strcmp(track_type, "MODE2")) { cd_toc->tracks[cd_toc->last].sector_size = 2336; cd_toc->tracks[cd_toc->last].type = TT_MODE2; } else if (!strcmp(track_type, "AUDIO")) { cd_toc->tracks[cd_toc->last].sector_size = 2352; cd_toc->tracks[cd_toc->last].type = TT_CDDA; } else { cd_toc->tracks[cd_toc->last].sector_size = 0; cd_toc->tracks[cd_toc->last].type = TT_CDDA; } cd_toc->tracks[cd_toc->last].sbc_type = SUBCODE_NONE; if (!strcmp(subtype, "RW")) { cd_toc->tracks[cd_toc->last].sbc_type = SUBCODE_RW; } else if (!strcmp(subtype, "RW_RAW")) { cd_toc->tracks[cd_toc->last].sbc_type = SUBCODE_RW_RAW; } //CHD pads tracks to a multiple of 4 sectors, keep track of the overall sector count and calculate the difference between the cdrom lba and the effective chd lba cd_toc->tracks[cd_toc->last].offset = (sector_cnt + pregap - cd_toc->tracks[cd_toc->last].start); cd_toc->tracks[cd_toc->last].end = cd_toc->tracks[cd_toc->last].start + frames - pregap; cd_toc->end = cd_toc->tracks[cd_toc->last].end + postgap; sector_cnt += ((frames + CD_TRACK_PADDING - 1) / CD_TRACK_PADDING) * CD_TRACK_PADDING; mister_chd_log("Track %d: Type: %s PreGap: %d PreGapType: %s Frames: %d start: %d end %d\n", cd_toc->last, track_type, pregap, pgtype, frames, cd_toc->tracks[cd_toc->last].start, cd_toc->tracks[cd_toc->last].end); } return CHDERR_NONE; } chd_error mister_chd_read_sector(chd_file *chd_f, int lba, uint32_t d_offset, uint32_t s_offset, int length, uint8_t *destbuf, uint8_t *hunkbuf, int *hunknum) { int tmphnum = 0; int hunkofs = 0; lba_to_hunkinfo(chd_f, lba, &tmphnum, &hunkofs); //mister_chd_log("READ LBA: %d, dest_offset: %d sector offset: %d length %d chd_f %p\n", lba, d_offset, s_offset, length, chd_f); if (tmphnum != *hunknum) { chd_error err = chd_read(chd_f, tmphnum, hunkbuf); if (err != CHDERR_NONE) { mister_chd_log("ERROR %s\n", chd_error_string(err)); return err; } *hunknum = tmphnum; } int sector_offset = hunkofs * CD_FRAME_SIZE; memcpy(destbuf + d_offset, hunkbuf + sector_offset + s_offset, length); return CHDERR_NONE; }