nkeynes@31: /** nkeynes@561: * $Id$ nkeynes@31: * nkeynes@31: * CDI CD-image file support nkeynes@31: * nkeynes@31: * Copyright (c) 2005 Nathan Keynes. nkeynes@31: * nkeynes@31: * This program is free software; you can redistribute it and/or modify nkeynes@31: * it under the terms of the GNU General Public License as published by nkeynes@31: * the Free Software Foundation; either version 2 of the License, or nkeynes@31: * (at your option) any later version. nkeynes@31: * nkeynes@31: * This program is distributed in the hope that it will be useful, nkeynes@31: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@31: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@31: * GNU General Public License for more details. nkeynes@31: */ nkeynes@1: nkeynes@1: #include nkeynes@1: #include nkeynes@1: #include nkeynes@759: #include nkeynes@1: #include nkeynes@1: #include nkeynes@1: #include nkeynes@1097: #include "drivers/cdrom/cdimpl.h" nkeynes@1: nkeynes@168: #define CDI_V2_ID 0x80000004 nkeynes@168: #define CDI_V3_ID 0x80000005 nkeynes@168: #define CDI_V35_ID 0x80000006 nkeynes@168: nkeynes@168: nkeynes@168: static gboolean cdi_image_is_valid( FILE *f ); nkeynes@1097: static gboolean cdi_image_read_toc( cdrom_disc_t disc, ERROR *err ); nkeynes@168: nkeynes@1097: struct cdrom_disc_factory cdi_disc_factory = { "DiscJuggler", "cdi", nkeynes@1097: cdi_image_is_valid, NULL, cdi_image_read_toc }; nkeynes@1: nkeynes@347: static const char TRACK_START_MARKER[20] = { 0,0,1,0,0,0,255,255,255,255, nkeynes@736: 0,0,1,0,0,0,255,255,255,255 }; nkeynes@347: static const char EXT_MARKER[9] = {0,255,255,255,255,255,255,255,255 }; nkeynes@1: nkeynes@1: struct cdi_trailer { nkeynes@1: uint32_t cdi_version; nkeynes@1: uint32_t header_offset; nkeynes@1: }; nkeynes@1: nkeynes@1: struct cdi_track_data { nkeynes@1: uint32_t pregap_length; nkeynes@1: uint32_t length; nkeynes@1: char unknown2[6]; nkeynes@1: uint32_t mode; nkeynes@1: char unknown3[0x0c]; nkeynes@1: uint32_t start_lba; nkeynes@1: uint32_t total_length; nkeynes@1: char unknown4[0x10]; nkeynes@1: uint32_t sector_size; nkeynes@1: char unknown5[0x1D]; nkeynes@1: } __attribute__((packed)); nkeynes@1: nkeynes@168: gboolean cdi_image_is_valid( FILE *f ) nkeynes@1: { nkeynes@168: int len; nkeynes@168: struct cdi_trailer trail; nkeynes@1: nkeynes@168: fseek( f, -8, SEEK_END ); nkeynes@168: len = ftell(f)+8; nkeynes@168: fread( &trail, sizeof(trail), 1, f ); nkeynes@168: if( trail.header_offset >= len || nkeynes@736: trail.header_offset == 0 ) nkeynes@736: return FALSE; nkeynes@453: return trail.cdi_version == CDI_V2_ID || trail.cdi_version == CDI_V3_ID || nkeynes@736: trail.cdi_version == CDI_V35_ID; nkeynes@168: } nkeynes@168: nkeynes@1109: #define RETURN_PARSE_ERROR( ... ) do { SET_ERROR(err, LX_ERR_FILE_INVALID, __VA_ARGS__); return FALSE; } while(0) nkeynes@1097: nkeynes@1097: static gboolean cdi_image_read_toc( cdrom_disc_t disc, ERROR *err ) nkeynes@168: { nkeynes@422: int i,j; nkeynes@168: uint16_t session_count; nkeynes@168: uint16_t track_count; nkeynes@168: int total_tracks = 0; nkeynes@422: int posn = 0; nkeynes@1: long len; nkeynes@1: struct cdi_trailer trail; nkeynes@1: char marker[20]; nkeynes@1: nkeynes@1097: FILE *f = cdrom_disc_get_base_file(disc); nkeynes@168: fseek( f, -8, SEEK_END ); nkeynes@168: len = ftell(f)+8; nkeynes@168: fread( &trail, sizeof(trail), 1, f ); nkeynes@168: if( trail.header_offset >= len || nkeynes@1097: trail.header_offset == 0 ) { nkeynes@1097: RETURN_PARSE_ERROR( "Invalid CDI image" ); nkeynes@1097: } nkeynes@736: nkeynes@453: if( trail.cdi_version != CDI_V2_ID && trail.cdi_version != CDI_V3_ID && nkeynes@736: trail.cdi_version != CDI_V35_ID ) { nkeynes@1097: RETURN_PARSE_ERROR( "Invalid CDI image" ); nkeynes@453: } nkeynes@1: nkeynes@453: if( trail.cdi_version == CDI_V35_ID ) { nkeynes@999: fseek( f, -(long)trail.header_offset, SEEK_END ); nkeynes@453: } else { nkeynes@736: fseek( f, trail.header_offset, SEEK_SET ); nkeynes@453: } nkeynes@168: fread( &session_count, sizeof(session_count), 1, f ); nkeynes@736: nkeynes@168: for( i=0; i< session_count; i++ ) { nkeynes@736: fread( &track_count, sizeof(track_count), 1, f ); nkeynes@736: if( track_count + total_tracks > 99 ) { nkeynes@1097: RETURN_PARSE_ERROR("Invalid number of tracks, bad cdi image" ); nkeynes@736: } nkeynes@168: for( j=0; jtrack[total_tracks].sessionno = i+1; nkeynes@1097: disc->track[total_tracks].lba = trk.start_lba; nkeynes@1097: cdrom_count_t sector_count = trk.length; nkeynes@1097: sector_mode_t mode; nkeynes@736: switch( trk.mode ) { nkeynes@736: case 0: nkeynes@1097: mode = SECTOR_CDDA; nkeynes@1023: disc->track[total_tracks].flags = 0x01; nkeynes@736: if( trk.sector_size != 2 ) { nkeynes@1097: RETURN_PARSE_ERROR( "Invalid combination of mode %d with size %d", trk.mode, trk.sector_size ); nkeynes@736: } nkeynes@736: break; nkeynes@736: case 1: nkeynes@1097: mode = SECTOR_MODE1; nkeynes@1023: disc->track[total_tracks].flags = 0x41; nkeynes@736: if( trk.sector_size != 0 ) { nkeynes@1097: RETURN_PARSE_ERROR( "Invalid combination of mode %d with size %d", trk.mode, trk.sector_size ); nkeynes@736: } nkeynes@736: break; nkeynes@736: case 2: nkeynes@1023: disc->track[total_tracks].flags = 0x41; nkeynes@736: switch( trk.sector_size ) { nkeynes@736: case 0: nkeynes@1097: mode = SECTOR_MODE2_FORM1; nkeynes@736: break; nkeynes@736: case 1: nkeynes@1097: mode = SECTOR_SEMIRAW_MODE2; nkeynes@736: break; nkeynes@736: case 2: nkeynes@736: default: nkeynes@1097: RETURN_PARSE_ERROR( "Invalid combination of mode %d with size %d", trk.mode, trk.sector_size ); nkeynes@736: } nkeynes@736: break; nkeynes@1097: default: nkeynes@1097: RETURN_PARSE_ERROR( "Unsupported track mode %d", trk.mode ); nkeynes@736: } nkeynes@1097: uint32_t offset = posn + nkeynes@1097: trk.pregap_length * CDROM_SECTOR_SIZE(mode); nkeynes@1097: disc->track[total_tracks].source = file_sector_source_new_source( disc->base_source, mode, offset, sector_count ); nkeynes@1097: posn += trk.total_length * CDROM_SECTOR_SIZE(mode); nkeynes@736: total_tracks++; nkeynes@736: fread( marker, 1, 9, f ); nkeynes@736: if( memcmp( marker, EXT_MARKER, 9 ) == 0 ) { nkeynes@736: fseek( f, 79, SEEK_CUR ); nkeynes@736: } else { nkeynes@736: fseek( f, -9, SEEK_CUR ); nkeynes@736: } nkeynes@736: } nkeynes@736: fseek( f, 12, SEEK_CUR ); nkeynes@1: } nkeynes@1097: nkeynes@1023: disc->track_count = total_tracks; nkeynes@1097: disc->session_count = session_count; nkeynes@1097: return TRUE; nkeynes@1: }