Search
lxdream.org :: lxdream/src/drivers/cdrom/cdrom.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/drivers/cdrom/cdrom.c
changeset 1177:bd5893522efc
prev1109:700c5ab26a63
next1296:30ecee61f811
author Nathan Keynes <nkeynes@lxdream.org>
date Sat Sep 17 22:35:45 2011 +1000 (10 years ago)
permissions -rw-r--r--
last change When initial disc read-toc fails, return the actual failure error code rather than
overwriting it with LX_ERR_FILE_UNKNOWN - if the read-toc failed, we
identified the file, it just didn't work
view annotate diff log raw
     1 /**
     2  * $Id$
     3  *
     4  * Copyright (c) 2009 Nathan Keynes.
     5  *
     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.
    10  *
    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.
    15  */
    17 #include <assert.h>
    18 #include <errno.h>
    19 #include <fcntl.h>
    20 #include <stdio.h>
    21 #include <string.h>
    22 #include <glib/gmem.h>
    23 #include <glib/gstrfuncs.h>
    24 #include "lxdream.h"
    25 #include "drivers/cdrom/cdrom.h"
    26 #include "drivers/cdrom/cdimpl.h"
    27 #include "drivers/cdrom/isofs.h"
    29 extern struct cdrom_disc_factory linux_cdrom_drive_factory;
    30 extern struct cdrom_disc_factory nrg_disc_factory;
    31 extern struct cdrom_disc_factory cdi_disc_factory;
    32 extern struct cdrom_disc_factory gdi_disc_factory;
    34 cdrom_disc_factory_t cdrom_disc_factories[] = {
    35 #ifdef HAVE_LINUX_CDROM
    36         &linux_cdrom_drive_factory,
    37 #endif
    38         &nrg_disc_factory,
    39         &cdi_disc_factory,
    40         &gdi_disc_factory,
    41         NULL };
    43 /********************* Implementation Support functions ************************/
    45 cdrom_error_t default_image_read_blocks( sector_source_t source, cdrom_lba_t lba, cdrom_count_t count,
    46                                          unsigned char *buf )
    47 {
    48     assert( 0 && "read_blocks called on a cdrom disc" );
    49     return CDROM_ERROR_BADREAD;
    50 }
    52 cdrom_error_t default_image_read_sectors( sector_source_t source, cdrom_lba_t lba, cdrom_count_t count,
    53                                           cdrom_read_mode_t mode, unsigned char *buf, size_t *length )
    54 {
    55     assert( IS_SECTOR_SOURCE_TYPE(source,DISC_SECTOR_SOURCE) );
    56     cdrom_disc_t disc = (cdrom_disc_t)source;
    57     size_t len = 0, tmplen;
    58     cdrom_count_t current = 0;
    60     while( current < count ) {
    61         cdrom_track_t track = cdrom_disc_get_track_by_lba( disc, lba + current );
    62         if( track == NULL )
    63             return CDROM_ERROR_BADREAD;
    64         uint32_t track_size = cdrom_disc_get_track_size( disc, track );
    65         cdrom_lba_t track_offset = lba + current - track->lba;
    66         cdrom_count_t sub_count = count - current;
    67         if( track_size - track_offset < sub_count )
    68             /* Read breaks across track boundaries. This will probably fail (due
    69              * to inter-track gaps), but try it just in case
    70              */
    71             sub_count = track_size - track_offset;
    72         cdrom_error_t err = track->source->read_sectors( track->source, track_offset, sub_count, mode, &buf[len], &tmplen );
    73         if( err != CDROM_ERROR_OK )
    74             return err;
    75         len += tmplen;
    76         current += sub_count;
    77     }
    78     if( length != NULL )
    79         *length = len;
    80     return CDROM_ERROR_OK;
    81 }
    83 void default_cdrom_disc_destroy( sector_source_t source )
    84 {
    85     assert( IS_SECTOR_SOURCE_TYPE(source,DISC_SECTOR_SOURCE) );
    86     cdrom_disc_t disc = (cdrom_disc_t)source;
    87     int i;
    89     for( i=0; i<disc->track_count; i++ ) {
    90         sector_source_unref( disc->track[i].source );
    91     }
    92     sector_source_unref( disc->base_source );
    93     g_free( (char *)disc->name );
    95     default_sector_source_destroy( source );
    96 }
    98 cdrom_disc_t cdrom_disc_init( cdrom_disc_t disc, const char *filename )
    99 {
   100     sector_source_init( &disc->source, DISC_SECTOR_SOURCE, SECTOR_UNKNOWN, 0, default_image_read_blocks,
   101             default_cdrom_disc_destroy );
   102     disc->source.read_sectors = default_image_read_sectors;
   103     disc->disc_type = CDROM_DISC_NONE;
   104     disc->track_count = disc->session_count = 0;
   105     for( int i=0; i<99; i++ ) {
   106         disc->track[i].trackno = i+1;
   107     }
   108     if( filename != NULL )
   109         disc->name = g_strdup(filename);
   110     return disc;
   111 }
   113 cdrom_disc_t cdrom_disc_new( const char *name, ERROR *err )
   114 {
   115     cdrom_disc_t disc = g_malloc0( sizeof(struct cdrom_disc) );
   116     if( disc != NULL ) {
   117         cdrom_disc_init( disc, name );
   118     } else {
   119         SET_ERROR(err, LX_ERR_NOMEM, "Unable to allocate memory for cdrom disc");
   120     }
   121     return disc;
   122 }
   124 /**
   125  * Construct a new image-based disc using the given filename as the base source.
   126  * TOC is initialized to the empty values.
   127  */
   128 static cdrom_disc_t cdrom_disc_image_new( const char *filename, ERROR *err )
   129 {
   130     cdrom_disc_t disc = cdrom_disc_new( filename, err );
   131     if( disc != NULL && filename != NULL ) {
   132         disc->base_source = file_sector_source_new_filename( filename, SECTOR_UNKNOWN, 0, FILE_SECTOR_FULL_FILE );
   133         if( disc->base_source == NULL ) {
   134             SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open cdrom file '%s': %s", filename, strerror(errno) );
   135             cdrom_disc_unref(disc);
   136             disc = NULL;
   137         } else {
   138             sector_source_ref(disc->base_source);
   139         }
   141     }
   142     return disc;
   143 }
   145 cdrom_lba_t cdrom_disc_compute_leadout( cdrom_disc_t disc )
   146 {
   147     if( disc->track_count == 0 ) {
   148         disc->leadout = 0;
   149     } else {
   150         cdrom_track_t last_track = &disc->track[disc->track_count-1];
   151         if( last_track->source != NULL ) {
   152             cdrom_lba_t leadout = last_track->lba + last_track->source->size;
   153             if( leadout > disc->leadout )
   154                 disc->leadout = leadout;
   155         }
   156     }
   157     return disc->leadout;
   158 }
   161 void cdrom_disc_set_default_disc_type( cdrom_disc_t disc )
   162 {
   163     int type = CDROM_DISC_NONE, i;
   164     for( i=0; i<disc->track_count; i++ ) {
   165         if( (disc->track[i].flags & TRACK_FLAG_DATA == 0) ) {
   166             if( type == CDROM_DISC_NONE )
   167                 type = CDROM_DISC_AUDIO;
   168         } else if( disc->track[i].source != NULL &&
   169                    (disc->track[i].source->mode == SECTOR_MODE1 ||
   170                     disc->track[i].source->mode == SECTOR_RAW_NONXA) ) {
   171             if( type != CDROM_DISC_XA )
   172                 type = CDROM_DISC_NONXA;
   173         } else {
   174             type = CDROM_DISC_XA;
   175             break;
   176         }
   177     }
   178     disc->disc_type = type;
   179 }
   181 void cdrom_disc_clear_toc( cdrom_disc_t disc )
   182 {
   183     disc->disc_type = CDROM_DISC_NONE;
   184     disc->leadout = 0;
   185     disc->track_count = 0;
   186     disc->session_count = 0;
   187     for( unsigned i=0; i< CDROM_MAX_TRACKS; i++ ) {
   188         if( disc->track[i].source != NULL ) {
   189             sector_source_unref( disc->track[i].source );
   190             disc->track[i].source = NULL;
   191         }
   192     }
   193 }
   195 gboolean cdrom_disc_read_toc( cdrom_disc_t disc, ERROR *err )
   196 {
   197     if( disc->read_toc != NULL ) {
   198         /* First set the defaults for an empty disc */
   199         cdrom_disc_clear_toc(disc);
   201         if( disc->read_toc(disc, err ) ) {
   202             /* Success - update disc type and leadout if the TOC read didn't set them */
   203             if( disc->disc_type == CDROM_DISC_NONE )
   204                 cdrom_disc_set_default_disc_type(disc);
   205             cdrom_disc_compute_leadout(disc);
   206             return TRUE;
   207         } else {
   208             /* Reset to an empty disc in case the reader left things in an
   209              * inconsistent state */
   210             cdrom_disc_clear_toc(disc);
   211             return FALSE;
   212         }
   213     } else {
   214         return TRUE;
   215     }
   216 }
   218 FILE *cdrom_disc_get_base_file( cdrom_disc_t disc )
   219 {
   220     return file_sector_source_get_file(disc->base_source);
   221 }
   223 /*************************** Public functions ***************************/
   225 cdrom_disc_t cdrom_disc_open( const char *inFilename, ERROR *err )
   226 {
   227     const gchar *filename = inFilename;
   228     const gchar *ext = strrchr(filename, '.');
   229     int i;
   230     cdrom_disc_factory_t extclz = NULL;
   232     /* Ask the drive list if it recognizes the name first */
   233     cdrom_drive_t drive = cdrom_drive_find(inFilename);
   234     if( drive != NULL ) {
   235         return cdrom_drive_open(drive, err);
   236     }
   238     cdrom_disc_t disc = cdrom_disc_image_new( filename, err );
   239     if( disc == NULL )
   240         return NULL;
   242     /* check file extensions first */
   243     FILE *f = file_sector_source_get_file(disc->base_source);
   244     if( ext != NULL ) {
   245         ext++; /* Skip the '.' */
   246         for( i=0; cdrom_disc_factories[i] != NULL; i++ ) {
   247             if( cdrom_disc_factories[i]->extension != NULL &&
   248                     strcasecmp( cdrom_disc_factories[i]->extension, ext ) == 0 ) {
   249                 extclz = cdrom_disc_factories[i];
   250                 if( extclz->is_valid_file(f) ) {
   251                     disc->read_toc = extclz->read_toc;
   252                 }
   253                 break;
   254             }
   255         }
   256     }
   258     if( disc->read_toc == NULL ) {
   259         /* Okay, fall back to magic */
   260         for( i=0; cdrom_disc_factories[i] != NULL; i++ ) {
   261             if( cdrom_disc_factories[i] != extclz &&
   262                 cdrom_disc_factories[i]->is_valid_file(f) ) {
   263                 disc->read_toc = cdrom_disc_factories[i]->read_toc;
   264                 break;
   265             }
   266         }
   267     }
   269     if( disc->read_toc == NULL ) {
   270         /* No handler found for file */
   271         cdrom_disc_unref( disc );
   272         SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' could not be recognized as any known image file or device type", filename );
   273         return NULL;
   274     } else if( !cdrom_disc_read_toc( disc, err ) ) {
   275         cdrom_disc_unref( disc );
   276         assert( err == NULL || err->code != LX_ERR_NONE ); /* Read-toc should have set an error code in this case */
   277         return NULL;
   278     } else {
   279         /* All good */
   280         return disc;
   281     }
   282 }
   284 /**
   285  * Construct a disc around a source track.
   286  * @param type Disc type, which must be compatible with the track mode
   287  * @param track The source of data for the main track
   288  * @param lba The position on disc of the main track. If non-zero,
   289  * a filler track is added before it, in 2 separate sessions.
   290  */
   291 cdrom_disc_t cdrom_disc_new_from_track( cdrom_disc_type_t type, sector_source_t track, cdrom_lba_t lba, ERROR *err )
   292 {
   293     cdrom_disc_t disc = cdrom_disc_new( NULL, NULL );
   294     if( disc != NULL ) {
   295         disc->disc_type = type;
   296         int trackno = 0;
   297         if( lba != 0 ) {
   298             cdrom_count_t size = lba - 150;
   299             if( lba < 150 )
   300                 size = lba;
   301             disc->track[0].trackno = 1;
   302             disc->track[0].sessionno = 1;
   303             disc->track[0].lba = 0;
   304             disc->track[0].flags = 0;
   305             disc->track[0].source = null_sector_source_new( SECTOR_CDDA, size );
   306             sector_source_ref( disc->track[0].source );
   307             trackno++;
   308         }
   309         disc->track[trackno].trackno = trackno+1;
   310         disc->track[trackno].sessionno = trackno+1;
   311         disc->track[trackno].lba = lba;
   312         disc->track[trackno].flags = (track->mode == SECTOR_CDDA ? 0 : TRACK_FLAG_DATA);
   313         disc->track[trackno].source = track;
   314         sector_source_ref(track);
   316         disc->track_count = trackno+1;
   317         disc->session_count = trackno+1;
   318         cdrom_disc_compute_leadout(disc);
   319     } else {
   320         SET_ERROR(err, LX_ERR_NOMEM, "Unable to allocate memory for cdrom disc");
   321     }
   322     return disc;
   323 }
   325 /**
   326  * Construct a disc around an IsoImage track (convenience function)
   327  */
   328 cdrom_disc_t cdrom_disc_new_from_iso_image( cdrom_disc_type_t type, IsoImage *iso, cdrom_lba_t lba,
   329                                             const char *bootstrap, ERROR *err )
   330 {
   331     sector_mode_t mode = (type == CDROM_DISC_NONXA ? SECTOR_MODE1 : SECTOR_MODE2_FORM1 );
   332     sector_source_t source = iso_sector_source_new( iso, mode, lba, bootstrap, err );
   333     if( source != NULL ) {
   334         cdrom_disc_t disc = cdrom_disc_new_from_track(type, source, lba, err);
   335         if( disc == NULL ) {
   336             sector_source_unref( source );
   337         } else {
   338             return disc;
   339         }
   340     }
   341     return NULL;
   342 }
   344 /**
   345  * Get the track information for the given track. If there is no such track,
   346  * return NULL;
   347  */
   348 cdrom_track_t cdrom_disc_get_track( cdrom_disc_t disc, cdrom_trackno_t track )
   349 {
   350     if( track < 1 || track >= disc->track_count )
   351         return NULL;
   352     return &disc->track[track-1];
   353 }
   355 /**
   356  * Get the track information for the first track of the given session. If there
   357  * is no such session, return NULL;
   358  */
   359 cdrom_track_t cdrom_disc_get_session( cdrom_disc_t disc, cdrom_sessionno_t session )
   360 {
   361     for( unsigned i=0; i< disc->track_count; i++ ) {
   362         if( disc->track[i].sessionno == session )
   363             return &disc->track[i];
   364     }
   365     return NULL;
   366 }
   368 cdrom_count_t cdrom_disc_get_track_size( cdrom_disc_t disc, cdrom_track_t track )
   369 {
   370     if( track->trackno == disc->track_count )
   371         return disc->leadout - track->lba;
   372     else
   373         return disc->track[track->trackno].lba - track->lba;
   374 }
   376 cdrom_track_t cdrom_disc_get_last_track( cdrom_disc_t disc )
   377 {
   378     if( disc->track_count == 0 )
   379         return NULL;
   380     return &disc->track[disc->track_count-1];
   381 }
   383 cdrom_track_t cdrom_disc_get_last_data_track( cdrom_disc_t disc )
   384 {
   385     for( unsigned i=disc->track_count; i>0; i-- ) {
   386         if( disc->track[i-1].flags & TRACK_FLAG_DATA ) {
   387             return &disc->track[i-1];
   388         }
   389     }
   390     return NULL;
   391 }
   392 cdrom_track_t cdrom_disc_prev_track( cdrom_disc_t disc, cdrom_track_t track )
   393 {
   394     if( track->trackno <= 1 )
   395         return NULL;
   396     return cdrom_disc_get_track( disc, track->trackno-1 );
   397 }
   399 cdrom_track_t cdrom_disc_next_track( cdrom_disc_t disc, cdrom_track_t track )
   400 {
   401     if( track->trackno >= disc->track_count )
   402         return NULL;
   403     return cdrom_disc_get_track( disc, track->trackno+1 );
   404 }
   406 /**
   407  * Find the track containing the sector specified by LBA.
   408  * Note: this function does not check for media change.
   409  * @return The track, or NULL if no track contains the sector.
   410  */
   411 cdrom_track_t cdrom_disc_get_track_by_lba( cdrom_disc_t disc, cdrom_lba_t lba )
   412 {
   413     if( disc->track_count == 0 || disc->track[0].lba > lba || lba >= disc->leadout )
   414         return NULL; /* LBA outside disc bounds */
   416     for( unsigned i=1; i< disc->track_count; i++ ) {
   417         if( lba < disc->track[i].lba )
   418             return &disc->track[i-1];
   419     }
   420     return &disc->track[disc->track_count-1];
   421 }
   423 cdrom_error_t cdrom_disc_read_sectors( cdrom_disc_t disc, cdrom_lba_t lba, cdrom_count_t count,
   424                                        cdrom_read_mode_t mode, unsigned char *buf, size_t *length )
   425 {
   426     return disc->source.read_sectors( &disc->source, lba, count, mode, buf, length );
   427 }
   429 /**
   430  * Check if the disc contains valid media.
   431  * @return CDROM_ERROR_OK if disc is present, otherwise CDROM_ERROR_NODISC
   432  */
   433 cdrom_error_t cdrom_disc_check_media( cdrom_disc_t disc )
   434 {
   435     if( disc == NULL )
   436         return CDROM_ERROR_NODISC;
   437     if( disc->check_media != NULL )
   438         disc->check_media(disc);
   439     return disc->disc_type == CDROM_DISC_NONE ? CDROM_ERROR_NODISC : CDROM_ERROR_OK;
   440 }
   442 void cdrom_disc_print_toc( FILE *f, cdrom_disc_t disc )
   443 {
   444     int i;
   445     int session = 0;
   447     if( disc == NULL || disc->track_count == 0 ) {
   448         fprintf( f, "No disc\n" );
   449         return;
   450     }
   451     for( i=0; i<disc->track_count; i++ ) {
   452         cdrom_track_t track = &disc->track[i];
   453         if( track->sessionno != session ) {
   454             session = disc->track[i].sessionno;
   455             fprintf( f, "Session %d:\n", session );
   456         }
   457         fprintf( f, "  %02d. %6d %02x\n", track->trackno, track->lba, track->flags );
   458     }
   459 }
   461 void cdrom_disc_dump_toc( cdrom_disc_t disc )
   462 {
   463     cdrom_disc_print_toc( stderr, disc );
   464 }
.