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 1298:d0eb2307b847
prev1296:30ecee61f811
author nkeynes
date Wed Feb 04 08:38:23 2015 +1000 (5 years ago)
permissions -rw-r--r--
last change Fix assorted compile warnings reported by Clang
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.h>
    23 #include "lxdream.h"
    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,
    36 #endif
    37         &nrg_disc_factory,
    38         &cdi_disc_factory,
    39         &gdi_disc_factory,
    40         NULL };
    42 /********************* Implementation Support functions ************************/
    44 cdrom_error_t default_image_read_blocks( sector_source_t source, cdrom_lba_t lba, cdrom_count_t count,
    45                                          unsigned char *buf )
    46 {
    47     assert( 0 && "read_blocks called on a cdrom disc" );
    48     return CDROM_ERROR_BADREAD;
    49 }
    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 )
    53 {
    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 );
    61         if( track == NULL )
    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
    69              */
    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 )
    73             return err;
    74         len += tmplen;
    75         current += sub_count;
    76     }
    77     if( length != NULL )
    78         *length = len;
    79     return CDROM_ERROR_OK;
    80 }
    82 void default_cdrom_disc_destroy( sector_source_t source )
    83 {
    84     assert( IS_SECTOR_SOURCE_TYPE(source,DISC_SECTOR_SOURCE) );
    85     cdrom_disc_t disc = (cdrom_disc_t)source;
    86     int i;
    88     for( i=0; i<disc->track_count; i++ ) {
    89         sector_source_unref( disc->track[i].source );
    90     }
    91     sector_source_unref( disc->base_source );
    92     g_free( (char *)disc->name );
    94     default_sector_source_destroy( source );
    95 }
    97 cdrom_disc_t cdrom_disc_init( cdrom_disc_t disc, const char *filename )
    98 {
    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;
   106     }
   107     if( filename != NULL )
   108         disc->name = g_strdup(filename);
   109     return disc;
   110 }
   112 cdrom_disc_t cdrom_disc_new( const char *name, ERROR *err )
   113 {
   114     cdrom_disc_t disc = g_malloc0( sizeof(struct cdrom_disc) );
   115     if( disc != NULL ) {
   116         cdrom_disc_init( disc, name );
   117     } else {
   118         SET_ERROR(err, LX_ERR_NOMEM, "Unable to allocate memory for cdrom disc");
   119     }
   120     return disc;
   121 }
   123 /**
   124  * Construct a new image-based disc using the given filename as the base source.
   125  * TOC is initialized to the empty values.
   126  */
   127 static cdrom_disc_t cdrom_disc_image_new( const char *filename, ERROR *err )
   128 {
   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);
   135             disc = NULL;
   136         } else {
   137             sector_source_ref(disc->base_source);
   138         }
   140     }
   141     return disc;
   142 }
   144 cdrom_lba_t cdrom_disc_compute_leadout( cdrom_disc_t disc )
   145 {
   146     if( disc->track_count == 0 ) {
   147         disc->leadout = 0;
   148     } else {
   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;
   154         }
   155     }
   156     return disc->leadout;
   157 }
   160 void cdrom_disc_set_default_disc_type( cdrom_disc_t disc )
   161 {
   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;
   172         } else {
   173             type = CDROM_DISC_XA;
   174             break;
   175         }
   176     }
   177     disc->disc_type = type;
   178 }
   180 void cdrom_disc_clear_toc( cdrom_disc_t disc )
   181 {
   182     disc->disc_type = CDROM_DISC_NONE;
   183     disc->leadout = 0;
   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;
   190         }
   191     }
   192 }
   194 gboolean cdrom_disc_read_toc( cdrom_disc_t disc, ERROR *err )
   195 {
   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);
   205             return TRUE;
   206         } else {
   207             /* Reset to an empty disc in case the reader left things in an
   208              * inconsistent state */
   209             cdrom_disc_clear_toc(disc);
   210             return FALSE;
   211         }
   212     } else {
   213         return TRUE;
   214     }
   215 }
   217 FILE *cdrom_disc_get_base_file( cdrom_disc_t disc )
   218 {
   219     return file_sector_source_get_file(disc->base_source);
   220 }
   222 /*************************** Public functions ***************************/
   224 cdrom_disc_t cdrom_disc_open( const char *inFilename, ERROR *err )
   225 {
   226     const gchar *filename = inFilename;
   227     const gchar *ext = strrchr(filename, '.');
   228     int i;
   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);
   235     }
   237     cdrom_disc_t disc = cdrom_disc_image_new( filename, err );
   238     if( disc == NULL )
   239         return NULL;
   241     /* check file extensions first */
   242     FILE *f = file_sector_source_get_file(disc->base_source);
   243     if( ext != NULL ) {
   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;
   251                 }
   252                 break;
   253             }
   254         }
   255     }
   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;
   263                 break;
   264             }
   265         }
   266     }
   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 );
   272         return NULL;
   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 */
   276         return NULL;
   277     } else {
   278         /* All good */
   279         return disc;
   280     }
   281 }
   283 /**
   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.
   289  */
   290 cdrom_disc_t cdrom_disc_new_from_track( cdrom_disc_type_t type, sector_source_t track, cdrom_lba_t lba, ERROR *err )
   291 {
   292     cdrom_disc_t disc = cdrom_disc_new( NULL, NULL );
   293     if( disc != NULL ) {
   294         disc->disc_type = type;
   295         int trackno = 0;
   296         if( lba != 0 ) {
   297             cdrom_count_t size = lba - 150;
   298             if( lba < 150 )
   299                 size = lba;
   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 );
   306             trackno++;
   307         }
   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);
   318     } else {
   319         SET_ERROR(err, LX_ERR_NOMEM, "Unable to allocate memory for cdrom disc");
   320     }
   321     return disc;
   322 }
   324 /**
   325  * Construct a disc around an IsoImage track (convenience function)
   326  */
   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 )
   329 {
   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);
   334         if( disc == NULL ) {
   335             sector_source_unref( source );
   336         } else {
   337             return disc;
   338         }
   339     }
   340     return NULL;
   341 }
   343 /**
   344  * Get the track information for the given track. If there is no such track,
   345  * return NULL;
   346  */
   347 cdrom_track_t cdrom_disc_get_track( cdrom_disc_t disc, cdrom_trackno_t track )
   348 {
   349     if( track < 1 || track >= disc->track_count )
   350         return NULL;
   351     return &disc->track[track-1];
   352 }
   354 /**
   355  * Get the track information for the first track of the given session. If there
   356  * is no such session, return NULL;
   357  */
   358 cdrom_track_t cdrom_disc_get_session( cdrom_disc_t disc, cdrom_sessionno_t session )
   359 {
   360     for( unsigned i=0; i< disc->track_count; i++ ) {
   361         if( disc->track[i].sessionno == session )
   362             return &disc->track[i];
   363     }
   364     return NULL;
   365 }
   367 cdrom_count_t cdrom_disc_get_track_size( cdrom_disc_t disc, cdrom_track_t track )
   368 {
   369     if( track->trackno == disc->track_count )
   370         return disc->leadout - track->lba;
   371     else
   372         return disc->track[track->trackno].lba - track->lba;
   373 }
   375 cdrom_track_t cdrom_disc_get_last_track( cdrom_disc_t disc )
   376 {
   377     if( disc->track_count == 0 )
   378         return NULL;
   379     return &disc->track[disc->track_count-1];
   380 }
   382 cdrom_track_t cdrom_disc_get_last_data_track( cdrom_disc_t disc )
   383 {
   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];
   387         }
   388     }
   389     return NULL;
   390 }
   391 cdrom_track_t cdrom_disc_prev_track( cdrom_disc_t disc, cdrom_track_t track )
   392 {
   393     if( track->trackno <= 1 )
   394         return NULL;
   395     return cdrom_disc_get_track( disc, track->trackno-1 );
   396 }
   398 cdrom_track_t cdrom_disc_next_track( cdrom_disc_t disc, cdrom_track_t track )
   399 {
   400     if( track->trackno >= disc->track_count )
   401         return NULL;
   402     return cdrom_disc_get_track( disc, track->trackno+1 );
   403 }
   405 /**
   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.
   409  */
   410 cdrom_track_t cdrom_disc_get_track_by_lba( cdrom_disc_t disc, cdrom_lba_t lba )
   411 {
   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];
   418     }
   419     return &disc->track[disc->track_count-1];
   420 }
   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 )
   424 {
   425     return disc->source.read_sectors( &disc->source, lba, count, mode, buf, length );
   426 }
   428 /**
   429  * Check if the disc contains valid media.
   430  * @return CDROM_ERROR_OK if disc is present, otherwise CDROM_ERROR_NODISC
   431  */
   432 cdrom_error_t cdrom_disc_check_media( cdrom_disc_t disc )
   433 {
   434     if( disc == NULL )
   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;
   439 }
   441 void cdrom_disc_print_toc( FILE *f, cdrom_disc_t disc )
   442 {
   443     int i;
   444     int session = 0;
   446     if( disc == NULL || disc->track_count == 0 ) {
   447         fprintf( f, "No disc\n" );
   448         return;
   449     }
   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 );
   455         }
   456         fprintf( f, "  %02d. %6d %02x\n", track->trackno, track->lba, track->flags );
   457     }
   458 }
   460 void cdrom_disc_dump_toc( cdrom_disc_t disc )
   461 {
   462     cdrom_disc_print_toc( stderr, disc );
   463 }
.