4 * Copyright (c) 2009 Nathan Keynes.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
24 #include "drivers/cdrom/cdrom.h"
25 #include "drivers/cdrom/cdimpl.h"
26 #include "drivers/cdrom/isofs.h"
28 extern struct cdrom_disc_factory linux_cdrom_drive_factory;
29 extern struct cdrom_disc_factory nrg_disc_factory;
30 extern struct cdrom_disc_factory cdi_disc_factory;
31 extern struct cdrom_disc_factory gdi_disc_factory;
33 cdrom_disc_factory_t cdrom_disc_factories[] = {
34 #ifdef HAVE_LINUX_CDROM
35 &linux_cdrom_drive_factory,
42 /********************* Implementation Support functions ************************/
44 cdrom_error_t default_image_read_blocks( sector_source_t source, cdrom_lba_t lba, cdrom_count_t count,
47 assert( 0 && "read_blocks called on a cdrom disc" );
48 return CDROM_ERROR_BADREAD;
51 cdrom_error_t default_image_read_sectors( sector_source_t source, cdrom_lba_t lba, cdrom_count_t count,
52 cdrom_read_mode_t mode, unsigned char *buf, size_t *length )
54 assert( IS_SECTOR_SOURCE_TYPE(source,DISC_SECTOR_SOURCE) );
55 cdrom_disc_t disc = (cdrom_disc_t)source;
56 size_t len = 0, tmplen;
57 cdrom_count_t current = 0;
59 while( current < count ) {
60 cdrom_track_t track = cdrom_disc_get_track_by_lba( disc, lba + current );
62 return CDROM_ERROR_BADREAD;
63 uint32_t track_size = cdrom_disc_get_track_size( disc, track );
64 cdrom_lba_t track_offset = lba + current - track->lba;
65 cdrom_count_t sub_count = count - current;
66 if( track_size - track_offset < sub_count )
67 /* Read breaks across track boundaries. This will probably fail (due
68 * to inter-track gaps), but try it just in case
70 sub_count = track_size - track_offset;
71 cdrom_error_t err = track->source->read_sectors( track->source, track_offset, sub_count, mode, &buf[len], &tmplen );
72 if( err != CDROM_ERROR_OK )
79 return CDROM_ERROR_OK;
82 void default_cdrom_disc_destroy( sector_source_t source )
84 assert( IS_SECTOR_SOURCE_TYPE(source,DISC_SECTOR_SOURCE) );
85 cdrom_disc_t disc = (cdrom_disc_t)source;
88 for( i=0; i<disc->track_count; i++ ) {
89 sector_source_unref( disc->track[i].source );
91 sector_source_unref( disc->base_source );
92 g_free( (char *)disc->name );
94 default_sector_source_destroy( source );
97 cdrom_disc_t cdrom_disc_init( cdrom_disc_t disc, const char *filename )
99 sector_source_init( &disc->source, DISC_SECTOR_SOURCE, SECTOR_UNKNOWN, 0, default_image_read_blocks,
100 default_cdrom_disc_destroy );
101 disc->source.read_sectors = default_image_read_sectors;
102 disc->disc_type = CDROM_DISC_NONE;
103 disc->track_count = disc->session_count = 0;
104 for( int i=0; i<99; i++ ) {
105 disc->track[i].trackno = i+1;
107 if( filename != NULL )
108 disc->name = g_strdup(filename);
112 cdrom_disc_t cdrom_disc_new( const char *name, ERROR *err )
114 cdrom_disc_t disc = g_malloc0( sizeof(struct cdrom_disc) );
116 cdrom_disc_init( disc, name );
118 SET_ERROR(err, LX_ERR_NOMEM, "Unable to allocate memory for cdrom disc");
124 * Construct a new image-based disc using the given filename as the base source.
125 * TOC is initialized to the empty values.
127 static cdrom_disc_t cdrom_disc_image_new( const char *filename, ERROR *err )
129 cdrom_disc_t disc = cdrom_disc_new( filename, err );
130 if( disc != NULL && filename != NULL ) {
131 disc->base_source = file_sector_source_new_filename( filename, SECTOR_UNKNOWN, 0, FILE_SECTOR_FULL_FILE );
132 if( disc->base_source == NULL ) {
133 SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open cdrom file '%s': %s", filename, strerror(errno) );
134 cdrom_disc_unref(disc);
137 sector_source_ref(disc->base_source);
144 cdrom_lba_t cdrom_disc_compute_leadout( cdrom_disc_t disc )
146 if( disc->track_count == 0 ) {
149 cdrom_track_t last_track = &disc->track[disc->track_count-1];
150 if( last_track->source != NULL ) {
151 cdrom_lba_t leadout = last_track->lba + last_track->source->size;
152 if( leadout > disc->leadout )
153 disc->leadout = leadout;
156 return disc->leadout;
160 void cdrom_disc_set_default_disc_type( cdrom_disc_t disc )
162 int type = CDROM_DISC_NONE, i;
163 for( i=0; i<disc->track_count; i++ ) {
164 if( ((disc->track[i].flags & TRACK_FLAG_DATA) == 0) ) {
165 if( type == CDROM_DISC_NONE )
166 type = CDROM_DISC_AUDIO;
167 } else if( disc->track[i].source != NULL &&
168 (disc->track[i].source->mode == SECTOR_MODE1 ||
169 disc->track[i].source->mode == SECTOR_RAW_NONXA) ) {
170 if( type != CDROM_DISC_XA )
171 type = CDROM_DISC_NONXA;
173 type = CDROM_DISC_XA;
177 disc->disc_type = type;
180 void cdrom_disc_clear_toc( cdrom_disc_t disc )
182 disc->disc_type = CDROM_DISC_NONE;
184 disc->track_count = 0;
185 disc->session_count = 0;
186 for( unsigned i=0; i< CDROM_MAX_TRACKS; i++ ) {
187 if( disc->track[i].source != NULL ) {
188 sector_source_unref( disc->track[i].source );
189 disc->track[i].source = NULL;
194 gboolean cdrom_disc_read_toc( cdrom_disc_t disc, ERROR *err )
196 if( disc->read_toc != NULL ) {
197 /* First set the defaults for an empty disc */
198 cdrom_disc_clear_toc(disc);
200 if( disc->read_toc(disc, err ) ) {
201 /* Success - update disc type and leadout if the TOC read didn't set them */
202 if( disc->disc_type == CDROM_DISC_NONE )
203 cdrom_disc_set_default_disc_type(disc);
204 cdrom_disc_compute_leadout(disc);
207 /* Reset to an empty disc in case the reader left things in an
208 * inconsistent state */
209 cdrom_disc_clear_toc(disc);
217 FILE *cdrom_disc_get_base_file( cdrom_disc_t disc )
219 return file_sector_source_get_file(disc->base_source);
222 /*************************** Public functions ***************************/
224 cdrom_disc_t cdrom_disc_open( const char *inFilename, ERROR *err )
226 const gchar *filename = inFilename;
227 const gchar *ext = strrchr(filename, '.');
229 cdrom_disc_factory_t extclz = NULL;
231 /* Ask the drive list if it recognizes the name first */
232 cdrom_drive_t drive = cdrom_drive_find(inFilename);
233 if( drive != NULL ) {
234 return cdrom_drive_open(drive, err);
237 cdrom_disc_t disc = cdrom_disc_image_new( filename, err );
241 /* check file extensions first */
242 FILE *f = file_sector_source_get_file(disc->base_source);
244 ext++; /* Skip the '.' */
245 for( i=0; cdrom_disc_factories[i] != NULL; i++ ) {
246 if( cdrom_disc_factories[i]->extension != NULL &&
247 strcasecmp( cdrom_disc_factories[i]->extension, ext ) == 0 ) {
248 extclz = cdrom_disc_factories[i];
249 if( extclz->is_valid_file(f) ) {
250 disc->read_toc = extclz->read_toc;
257 if( disc->read_toc == NULL ) {
258 /* Okay, fall back to magic */
259 for( i=0; cdrom_disc_factories[i] != NULL; i++ ) {
260 if( cdrom_disc_factories[i] != extclz &&
261 cdrom_disc_factories[i]->is_valid_file(f) ) {
262 disc->read_toc = cdrom_disc_factories[i]->read_toc;
268 if( disc->read_toc == NULL ) {
269 /* No handler found for file */
270 cdrom_disc_unref( disc );
271 SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' could not be recognized as any known image file or device type", filename );
273 } else if( !cdrom_disc_read_toc( disc, err ) ) {
274 cdrom_disc_unref( disc );
275 assert( err == NULL || err->code != LX_ERR_NONE ); /* Read-toc should have set an error code in this case */
284 * Construct a disc around a source track.
285 * @param type Disc type, which must be compatible with the track mode
286 * @param track The source of data for the main track
287 * @param lba The position on disc of the main track. If non-zero,
288 * a filler track is added before it, in 2 separate sessions.
290 cdrom_disc_t cdrom_disc_new_from_track( cdrom_disc_type_t type, sector_source_t track, cdrom_lba_t lba, ERROR *err )
292 cdrom_disc_t disc = cdrom_disc_new( NULL, NULL );
294 disc->disc_type = type;
297 cdrom_count_t size = lba - 150;
300 disc->track[0].trackno = 1;
301 disc->track[0].sessionno = 1;
302 disc->track[0].lba = 0;
303 disc->track[0].flags = 0;
304 disc->track[0].source = null_sector_source_new( SECTOR_CDDA, size );
305 sector_source_ref( disc->track[0].source );
308 disc->track[trackno].trackno = trackno+1;
309 disc->track[trackno].sessionno = trackno+1;
310 disc->track[trackno].lba = lba;
311 disc->track[trackno].flags = (track->mode == SECTOR_CDDA ? 0 : TRACK_FLAG_DATA);
312 disc->track[trackno].source = track;
313 sector_source_ref(track);
315 disc->track_count = trackno+1;
316 disc->session_count = trackno+1;
317 cdrom_disc_compute_leadout(disc);
319 SET_ERROR(err, LX_ERR_NOMEM, "Unable to allocate memory for cdrom disc");
325 * Construct a disc around an IsoImage track (convenience function)
327 cdrom_disc_t cdrom_disc_new_from_iso_image( cdrom_disc_type_t type, IsoImage *iso, cdrom_lba_t lba,
328 const char *bootstrap, ERROR *err )
330 sector_mode_t mode = (type == CDROM_DISC_NONXA ? SECTOR_MODE1 : SECTOR_MODE2_FORM1 );
331 sector_source_t source = iso_sector_source_new( iso, mode, lba, bootstrap, err );
332 if( source != NULL ) {
333 cdrom_disc_t disc = cdrom_disc_new_from_track(type, source, lba, err);
335 sector_source_unref( source );
344 * Get the track information for the given track. If there is no such track,
347 cdrom_track_t cdrom_disc_get_track( cdrom_disc_t disc, cdrom_trackno_t track )
349 if( track < 1 || track >= disc->track_count )
351 return &disc->track[track-1];
355 * Get the track information for the first track of the given session. If there
356 * is no such session, return NULL;
358 cdrom_track_t cdrom_disc_get_session( cdrom_disc_t disc, cdrom_sessionno_t session )
360 for( unsigned i=0; i< disc->track_count; i++ ) {
361 if( disc->track[i].sessionno == session )
362 return &disc->track[i];
367 cdrom_count_t cdrom_disc_get_track_size( cdrom_disc_t disc, cdrom_track_t track )
369 if( track->trackno == disc->track_count )
370 return disc->leadout - track->lba;
372 return disc->track[track->trackno].lba - track->lba;
375 cdrom_track_t cdrom_disc_get_last_track( cdrom_disc_t disc )
377 if( disc->track_count == 0 )
379 return &disc->track[disc->track_count-1];
382 cdrom_track_t cdrom_disc_get_last_data_track( cdrom_disc_t disc )
384 for( unsigned i=disc->track_count; i>0; i-- ) {
385 if( disc->track[i-1].flags & TRACK_FLAG_DATA ) {
386 return &disc->track[i-1];
391 cdrom_track_t cdrom_disc_prev_track( cdrom_disc_t disc, cdrom_track_t track )
393 if( track->trackno <= 1 )
395 return cdrom_disc_get_track( disc, track->trackno-1 );
398 cdrom_track_t cdrom_disc_next_track( cdrom_disc_t disc, cdrom_track_t track )
400 if( track->trackno >= disc->track_count )
402 return cdrom_disc_get_track( disc, track->trackno+1 );
406 * Find the track containing the sector specified by LBA.
407 * Note: this function does not check for media change.
408 * @return The track, or NULL if no track contains the sector.
410 cdrom_track_t cdrom_disc_get_track_by_lba( cdrom_disc_t disc, cdrom_lba_t lba )
412 if( disc->track_count == 0 || disc->track[0].lba > lba || lba >= disc->leadout )
413 return NULL; /* LBA outside disc bounds */
415 for( unsigned i=1; i< disc->track_count; i++ ) {
416 if( lba < disc->track[i].lba )
417 return &disc->track[i-1];
419 return &disc->track[disc->track_count-1];
422 cdrom_error_t cdrom_disc_read_sectors( cdrom_disc_t disc, cdrom_lba_t lba, cdrom_count_t count,
423 cdrom_read_mode_t mode, unsigned char *buf, size_t *length )
425 return disc->source.read_sectors( &disc->source, lba, count, mode, buf, length );
429 * Check if the disc contains valid media.
430 * @return CDROM_ERROR_OK if disc is present, otherwise CDROM_ERROR_NODISC
432 cdrom_error_t cdrom_disc_check_media( cdrom_disc_t disc )
435 return CDROM_ERROR_NODISC;
436 if( disc->check_media != NULL )
437 disc->check_media(disc);
438 return disc->disc_type == CDROM_DISC_NONE ? CDROM_ERROR_NODISC : CDROM_ERROR_OK;
441 void cdrom_disc_print_toc( FILE *f, cdrom_disc_t disc )
446 if( disc == NULL || disc->track_count == 0 ) {
447 fprintf( f, "No disc\n" );
450 for( i=0; i<disc->track_count; i++ ) {
451 cdrom_track_t track = &disc->track[i];
452 if( track->sessionno != session ) {
453 session = disc->track[i].sessionno;
454 fprintf( f, "Session %d:\n", session );
456 fprintf( f, " %02d. %6d %02x\n", track->trackno, track->lba, track->flags );
460 void cdrom_disc_dump_toc( cdrom_disc_t disc )
462 cdrom_disc_print_toc( stderr, disc );
.