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@1: #include nkeynes@1: #include nkeynes@1: #include nkeynes@168: #include "gdrom/gdrom.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@168: static gdrom_disc_t cdi_image_open( const gchar *filename, FILE *f ); nkeynes@168: nkeynes@168: struct gdrom_image_class cdi_image_class = { "DiscJuggler", "cdi", nkeynes@168: cdi_image_is_valid, cdi_image_open }; nkeynes@1: nkeynes@347: static const char TRACK_START_MARKER[20] = { 0,0,1,0,0,0,255,255,255,255, nkeynes@1: 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@168: trail.header_offset == 0 ) nkeynes@168: return FALSE; nkeynes@453: return trail.cdi_version == CDI_V2_ID || trail.cdi_version == CDI_V3_ID || nkeynes@453: trail.cdi_version == CDI_V35_ID; nkeynes@168: } nkeynes@168: nkeynes@168: gdrom_disc_t cdi_image_open( const gchar *filename, FILE *f ) nkeynes@168: { nkeynes@168: gdrom_disc_t disc = NULL; nkeynes@342: gdrom_image_t image; 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@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@1: trail.header_offset == 0 ) nkeynes@168: return NULL; nkeynes@453: nkeynes@453: if( trail.cdi_version != CDI_V2_ID && trail.cdi_version != CDI_V3_ID && nkeynes@453: trail.cdi_version != CDI_V35_ID ) { nkeynes@453: return NULL; nkeynes@453: } nkeynes@1: nkeynes@453: if( trail.cdi_version == CDI_V35_ID ) { nkeynes@453: fseek( f, -trail.header_offset, SEEK_END ); nkeynes@453: } else { nkeynes@453: fseek( f, trail.header_offset, SEEK_SET ); nkeynes@453: } nkeynes@168: fread( &session_count, sizeof(session_count), 1, f ); nkeynes@1: nkeynes@464: disc = gdrom_image_new(filename, f); nkeynes@342: if( disc == NULL ) { nkeynes@342: ERROR("Unable to allocate memory!"); nkeynes@342: return NULL; nkeynes@342: } nkeynes@342: image = (gdrom_image_t)disc; nkeynes@1: nkeynes@168: for( i=0; i< session_count; i++ ) { nkeynes@168: fread( &track_count, sizeof(track_count), 1, f ); nkeynes@347: if( track_count + total_tracks > 99 ) { nkeynes@347: ERROR( "Invalid number of tracks, bad cdi image\n" ); nkeynes@468: gdrom_image_destroy_no_close(disc); nkeynes@347: return NULL; nkeynes@347: } nkeynes@168: for( j=0; jtrack[total_tracks].session = i; nkeynes@342: image->track[total_tracks].lba = trk.start_lba + 150; nkeynes@342: image->track[total_tracks].sector_count = trk.length; nkeynes@168: switch( trk.mode ) { nkeynes@168: case 0: nkeynes@342: image->track[total_tracks].mode = GDROM_CDDA; nkeynes@342: image->track[total_tracks].sector_size = 2352; nkeynes@342: image->track[total_tracks].flags = 0x01; nkeynes@168: if( trk.sector_size != 2 ) { nkeynes@168: ERROR( "Invalid combination of mode %d with size %d", trk.mode, trk.sector_size ); nkeynes@468: gdrom_image_destroy_no_close(disc); nkeynes@168: return NULL; nkeynes@168: } nkeynes@168: break; nkeynes@168: case 1: nkeynes@342: image->track[total_tracks].mode = GDROM_MODE1; nkeynes@342: image->track[total_tracks].sector_size = 2048; nkeynes@342: image->track[total_tracks].flags = 0x41; nkeynes@168: if( trk.sector_size != 0 ) { nkeynes@168: ERROR( "Invalid combination of mode %d with size %d", trk.mode, trk.sector_size ); nkeynes@468: gdrom_image_destroy_no_close(disc); nkeynes@168: return NULL; nkeynes@168: } nkeynes@168: break; nkeynes@168: case 2: nkeynes@342: image->track[total_tracks].flags = 0x41; nkeynes@168: switch( trk.sector_size ) { nkeynes@168: case 0: nkeynes@342: image->track[total_tracks].mode = GDROM_MODE2_XA1; nkeynes@342: image->track[total_tracks].sector_size = 2048; nkeynes@168: break; nkeynes@168: case 1: nkeynes@342: image->track[total_tracks].mode = GDROM_MODE2; nkeynes@342: image->track[total_tracks].sector_size = 2336; nkeynes@168: break; nkeynes@168: case 2: nkeynes@168: default: nkeynes@168: ERROR( "Invalid combination of mode %d with size %d", trk.mode, trk.sector_size ); nkeynes@468: gdrom_image_destroy_no_close(disc); nkeynes@168: return NULL; nkeynes@168: } nkeynes@168: break; nkeynes@168: default: nkeynes@168: ERROR( "Unsupported track mode %d", trk.mode ); nkeynes@468: gdrom_image_destroy_no_close(disc); nkeynes@168: return NULL; nkeynes@168: } nkeynes@342: image->track[total_tracks].offset = posn + nkeynes@342: trk.pregap_length * image->track[total_tracks].sector_size ; nkeynes@342: posn += trk.total_length * image->track[total_tracks].sector_size; nkeynes@168: total_tracks++; nkeynes@347: fread( marker, 1, 9, f ); nkeynes@347: if( memcmp( marker, EXT_MARKER, 9 ) == 0 ) { nkeynes@468: fseek( f, 79, SEEK_CUR ); nkeynes@168: } else { nkeynes@468: fseek( f, -9, SEEK_CUR ); nkeynes@168: } nkeynes@168: } nkeynes@468: fseek( f, 12, SEEK_CUR ); nkeynes@1: } nkeynes@342: image->track_count = total_tracks; nkeynes@168: return disc; nkeynes@1: }