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 1099:566cdeb157ec
prev1097:d4807997e450
next1108:305ef2082079
author nkeynes
date Wed Feb 10 18:16:19 2010 +1000 (10 years ago)
permissions -rw-r--r--
last change First draft of basic ISO9660 filesystem reader
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"
    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, ENOMEM, "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, errno, "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     /* First set the defaults for an empty disc */
   197     cdrom_disc_clear_toc(disc);
   199     if( disc->read_toc(disc, err ) ) {
   200         /* Success - update disc type and leadout if the TOC read didn't set them */
   201         if( disc->disc_type == CDROM_DISC_NONE )
   202             cdrom_disc_set_default_disc_type(disc);
   203         cdrom_disc_compute_leadout(disc);
   204         return TRUE;
   205     } else {
   206         /* Reset to an empty disc in case the reader left things in an
   207          * inconsistent state */
   208         cdrom_disc_clear_toc(disc);
   209         return FALSE;
   210     }
   211 }
   213 FILE *cdrom_disc_get_base_file( cdrom_disc_t disc )
   214 {
   215     return file_sector_source_get_file(disc->base_source);
   216 }
   218 /*************************** Public functions ***************************/
   220 cdrom_disc_t cdrom_disc_open( const char *inFilename, ERROR *err )
   221 {
   222     const gchar *filename = inFilename;
   223     const gchar *ext = strrchr(filename, '.');
   224     int i;
   225     cdrom_disc_factory_t extclz = NULL;
   227     /* Ask the drive list if it recognizes the name first */
   228     cdrom_drive_t drive = cdrom_drive_find(inFilename);
   229     if( drive != NULL ) {
   230         return cdrom_drive_open(drive, err);
   231     }
   233     cdrom_disc_t disc = cdrom_disc_image_new( filename, err );
   234     if( disc == NULL )
   235         return NULL;
   237     /* check file extensions first */
   238     FILE *f = file_sector_source_get_file(disc->base_source);
   239     if( ext != NULL ) {
   240         ext++; /* Skip the '.' */
   241         for( i=0; cdrom_disc_factories[i] != NULL; i++ ) {
   242             if( cdrom_disc_factories[i]->extension != NULL &&
   243                     strcasecmp( cdrom_disc_factories[i]->extension, ext ) == 0 ) {
   244                 extclz = cdrom_disc_factories[i];
   245                 if( extclz->is_valid_file(f) ) {
   246                     disc->read_toc = extclz->read_toc;
   247                 }
   248                 break;
   249             }
   250         }
   251     }
   253     if( disc->read_toc == NULL ) {
   254         /* Okay, fall back to magic */
   255         for( i=0; cdrom_disc_factories[i] != NULL; i++ ) {
   256             if( cdrom_disc_factories[i] != extclz &&
   257                 cdrom_disc_factories[i]->is_valid_file(f) ) {
   258                 disc->read_toc = cdrom_disc_factories[i]->read_toc;
   259                 break;
   260             }
   261         }
   262     }
   264     if( disc->read_toc != NULL && cdrom_disc_read_toc( disc, err ) ) {
   265         /* All good */
   266         return disc;
   267     } else {
   268         /* No handler found for file */
   269         cdrom_disc_unref( disc );
   270         SET_ERROR( err, EINVAL, "File '%s' could not be recognized as any known image file or device type" );
   271         return NULL;
   272     }
   273 }
   275 /**
   276  * Get the track information for the given track. If there is no such track,
   277  * return NULL;
   278  */
   279 cdrom_track_t cdrom_disc_get_track( cdrom_disc_t disc, cdrom_trackno_t track )
   280 {
   281     if( track < 1 || track >= disc->track_count )
   282         return NULL;
   283     return &disc->track[track-1];
   284 }
   286 /**
   287  * Get the track information for the first track of the given session. If there
   288  * is no such session, return NULL;
   289  */
   290 cdrom_track_t cdrom_disc_get_session( cdrom_disc_t disc, cdrom_sessionno_t session )
   291 {
   292     for( unsigned i=0; i< disc->track_count; i++ ) {
   293         if( disc->track[i].sessionno == session )
   294             return &disc->track[i];
   295     }
   296     return NULL;
   297 }
   299 cdrom_count_t cdrom_disc_get_track_size( cdrom_disc_t disc, cdrom_track_t track )
   300 {
   301     if( track->trackno == disc->track_count )
   302         return disc->leadout - track->lba;
   303     else
   304         return disc->track[track->trackno].lba - track->lba;
   305 }
   307 cdrom_track_t cdrom_disc_get_last_track( cdrom_disc_t disc )
   308 {
   309     if( disc->track_count == 0 )
   310         return NULL;
   311     return &disc->track[disc->track_count-1];
   312 }
   314 cdrom_track_t cdrom_disc_get_last_data_track( cdrom_disc_t disc )
   315 {
   316     for( unsigned i=disc->track_count; i>0; i-- ) {
   317         if( disc->track[i-1].flags & TRACK_FLAG_DATA ) {
   318             return &disc->track[i-1];
   319         }
   320     }
   321     return NULL;
   322 }
   323 cdrom_track_t cdrom_disc_prev_track( cdrom_disc_t disc, cdrom_track_t track )
   324 {
   325     if( track->trackno <= 1 )
   326         return NULL;
   327     return cdrom_disc_get_track( disc, track->trackno-1 );
   328 }
   330 cdrom_track_t cdrom_disc_next_track( cdrom_disc_t disc, cdrom_track_t track )
   331 {
   332     if( track->trackno >= disc->track_count )
   333         return NULL;
   334     return cdrom_disc_get_track( disc, track->trackno+1 );
   335 }
   337 /**
   338  * Find the track containing the sector specified by LBA.
   339  * Note: this function does not check for media change.
   340  * @return The track, or NULL if no track contains the sector.
   341  */
   342 cdrom_track_t cdrom_disc_get_track_by_lba( cdrom_disc_t disc, cdrom_lba_t lba )
   343 {
   344     if( disc->track_count == 0 || disc->track[0].lba > lba || lba >= disc->leadout )
   345         return NULL; /* LBA outside disc bounds */
   347     for( unsigned i=1; i< disc->track_count; i++ ) {
   348         if( lba < disc->track[i].lba )
   349             return &disc->track[i-1];
   350     }
   351     return &disc->track[disc->track_count-1];
   352 }
   354 cdrom_error_t cdrom_disc_read_sectors( cdrom_disc_t disc, cdrom_lba_t lba, cdrom_count_t count,
   355                                        cdrom_read_mode_t mode, unsigned char *buf, size_t *length )
   356 {
   357     return disc->source.read_sectors( &disc->source, lba, count, mode, buf, length );
   358 }
   360 /**
   361  * Check if the disc contains valid media.
   362  * @return CDROM_ERROR_OK if disc is present, otherwise CDROM_ERROR_NODISC
   363  */
   364 cdrom_error_t cdrom_disc_check_media( cdrom_disc_t disc )
   365 {
   366     if( disc == NULL )
   367         return CDROM_ERROR_NODISC;
   368     if( disc->check_media != NULL )
   369         disc->check_media(disc);
   370     return disc->disc_type == CDROM_DISC_NONE ? CDROM_ERROR_NODISC : CDROM_ERROR_OK;
   371 }
   373 void cdrom_disc_print_toc( FILE *f, cdrom_disc_t disc )
   374 {
   375     int i;
   376     int session = 0;
   378     if( disc == NULL || disc->track_count == 0 ) {
   379         fprintf( f, "No disc\n" );
   380         return;
   381     }
   382     for( i=0; i<disc->track_count; i++ ) {
   383         cdrom_track_t track = &disc->track[i];
   384         if( track->sessionno != session ) {
   385             session = disc->track[i].sessionno;
   386             fprintf( f, "Session %d:\n", session );
   387         }
   388         fprintf( f, "  %02d. %6d %02x\n", track->trackno, track->lba, track->flags );
   389     }
   390 }
   392 void cdrom_disc_dump_toc( cdrom_disc_t disc )
   393 {
   394     cdrom_disc_print_toc( stderr, disc );
   395 }
.