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
file annotate diff log raw
nkeynes@1097
     1
/**
nkeynes@1097
     2
 * $Id$
nkeynes@1097
     3
 *
nkeynes@1097
     4
 * Copyright (c) 2009 Nathan Keynes.
nkeynes@1097
     5
 *
nkeynes@1097
     6
 * This program is free software; you can redistribute it and/or modify
nkeynes@1097
     7
 * it under the terms of the GNU General Public License as published by
nkeynes@1097
     8
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@1097
     9
 * (at your option) any later version.
nkeynes@1097
    10
 *
nkeynes@1097
    11
 * This program is distributed in the hope that it will be useful,
nkeynes@1097
    12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@1097
    13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@1097
    14
 * GNU General Public License for more details.
nkeynes@1097
    15
 */
nkeynes@1097
    16
nkeynes@1097
    17
#include <assert.h>
nkeynes@1097
    18
#include <errno.h>
nkeynes@1097
    19
#include <fcntl.h>
nkeynes@1097
    20
#include <stdio.h>
nkeynes@1097
    21
#include <string.h>
nkeynes@1097
    22
#include <glib/gmem.h>
nkeynes@1097
    23
#include <glib/gstrfuncs.h>
nkeynes@1097
    24
#include "lxdream.h"
nkeynes@1097
    25
#include "drivers/cdrom/cdrom.h"
nkeynes@1097
    26
#include "drivers/cdrom/cdimpl.h"
nkeynes@1097
    27
nkeynes@1097
    28
extern struct cdrom_disc_factory linux_cdrom_drive_factory;
nkeynes@1097
    29
extern struct cdrom_disc_factory nrg_disc_factory;
nkeynes@1097
    30
extern struct cdrom_disc_factory cdi_disc_factory;
nkeynes@1097
    31
extern struct cdrom_disc_factory gdi_disc_factory;
nkeynes@1097
    32
nkeynes@1097
    33
cdrom_disc_factory_t cdrom_disc_factories[] = {
nkeynes@1097
    34
#ifdef HAVE_LINUX_CDROM
nkeynes@1097
    35
        &linux_cdrom_drive_factory,
nkeynes@1097
    36
#endif
nkeynes@1097
    37
        &nrg_disc_factory,
nkeynes@1097
    38
        &cdi_disc_factory,
nkeynes@1097
    39
        &gdi_disc_factory,
nkeynes@1097
    40
        NULL };
nkeynes@1097
    41
nkeynes@1097
    42
/********************* Implementation Support functions ************************/
nkeynes@1097
    43
nkeynes@1097
    44
cdrom_error_t default_image_read_blocks( sector_source_t source, cdrom_lba_t lba, cdrom_count_t count,
nkeynes@1097
    45
                                         unsigned char *buf )
nkeynes@1097
    46
{
nkeynes@1097
    47
    assert( 0 && "read_blocks called on a cdrom disc" );
nkeynes@1097
    48
    return CDROM_ERROR_BADREAD;
nkeynes@1097
    49
}
nkeynes@1097
    50
nkeynes@1097
    51
cdrom_error_t default_image_read_sectors( sector_source_t source, cdrom_lba_t lba, cdrom_count_t count,
nkeynes@1097
    52
                                          cdrom_read_mode_t mode, unsigned char *buf, size_t *length )
nkeynes@1097
    53
{
nkeynes@1097
    54
    assert( IS_SECTOR_SOURCE_TYPE(source,DISC_SECTOR_SOURCE) );
nkeynes@1097
    55
    cdrom_disc_t disc = (cdrom_disc_t)source;
nkeynes@1097
    56
    size_t len = 0, tmplen;
nkeynes@1097
    57
    cdrom_count_t current = 0;
nkeynes@1097
    58
nkeynes@1097
    59
    while( current < count ) {
nkeynes@1097
    60
        cdrom_track_t track = cdrom_disc_get_track_by_lba( disc, lba + current );
nkeynes@1097
    61
        if( track == NULL )
nkeynes@1097
    62
            return CDROM_ERROR_BADREAD;
nkeynes@1097
    63
        uint32_t track_size = cdrom_disc_get_track_size( disc, track );
nkeynes@1097
    64
        cdrom_lba_t track_offset = lba + current - track->lba;
nkeynes@1097
    65
        cdrom_count_t sub_count = count - current;
nkeynes@1097
    66
        if( track_size - track_offset < sub_count )
nkeynes@1097
    67
            /* Read breaks across track boundaries. This will probably fail (due
nkeynes@1097
    68
             * to inter-track gaps), but try it just in case
nkeynes@1097
    69
             */
nkeynes@1097
    70
            sub_count = track_size - track_offset;
nkeynes@1097
    71
        cdrom_error_t err = track->source->read_sectors( track->source, track_offset, sub_count, mode, &buf[len], &tmplen );
nkeynes@1097
    72
        if( err != CDROM_ERROR_OK )
nkeynes@1097
    73
            return err;
nkeynes@1097
    74
        len += tmplen;
nkeynes@1097
    75
        current += sub_count;
nkeynes@1097
    76
    }
nkeynes@1099
    77
    if( length != NULL )
nkeynes@1099
    78
        *length = len;
nkeynes@1097
    79
    return CDROM_ERROR_OK;
nkeynes@1097
    80
}
nkeynes@1097
    81
nkeynes@1097
    82
void default_cdrom_disc_destroy( sector_source_t source )
nkeynes@1097
    83
{
nkeynes@1097
    84
    assert( IS_SECTOR_SOURCE_TYPE(source,DISC_SECTOR_SOURCE) );
nkeynes@1097
    85
    cdrom_disc_t disc = (cdrom_disc_t)source;
nkeynes@1097
    86
    int i;
nkeynes@1097
    87
nkeynes@1097
    88
    for( i=0; i<disc->track_count; i++ ) {
nkeynes@1097
    89
        sector_source_unref( disc->track[i].source );
nkeynes@1097
    90
    }
nkeynes@1097
    91
    sector_source_unref( disc->base_source );
nkeynes@1097
    92
    g_free( (char *)disc->name );
nkeynes@1097
    93
nkeynes@1097
    94
    default_sector_source_destroy( source );
nkeynes@1097
    95
}
nkeynes@1097
    96
nkeynes@1097
    97
cdrom_disc_t cdrom_disc_init( cdrom_disc_t disc, const char *filename )
nkeynes@1097
    98
{
nkeynes@1097
    99
    sector_source_init( &disc->source, DISC_SECTOR_SOURCE, SECTOR_UNKNOWN, 0, default_image_read_blocks,
nkeynes@1097
   100
            default_cdrom_disc_destroy );
nkeynes@1097
   101
    disc->source.read_sectors = default_image_read_sectors;
nkeynes@1097
   102
    disc->disc_type = CDROM_DISC_NONE;
nkeynes@1097
   103
    disc->track_count = disc->session_count = 0;
nkeynes@1097
   104
    for( int i=0; i<99; i++ ) {
nkeynes@1097
   105
        disc->track[i].trackno = i+1;
nkeynes@1097
   106
    }
nkeynes@1097
   107
    if( filename != NULL )
nkeynes@1097
   108
        disc->name = g_strdup(filename);
nkeynes@1097
   109
    return disc;
nkeynes@1097
   110
}
nkeynes@1097
   111
nkeynes@1097
   112
cdrom_disc_t cdrom_disc_new( const char *name, ERROR *err )
nkeynes@1097
   113
{
nkeynes@1097
   114
    cdrom_disc_t disc = g_malloc0( sizeof(struct cdrom_disc) );
nkeynes@1097
   115
    if( disc != NULL ) {
nkeynes@1097
   116
        cdrom_disc_init( disc, name );
nkeynes@1097
   117
    } else {
nkeynes@1097
   118
        SET_ERROR(err, ENOMEM, "Unable to allocate memory for cdrom disc");
nkeynes@1097
   119
    }
nkeynes@1097
   120
    return disc;
nkeynes@1097
   121
}
nkeynes@1097
   122
nkeynes@1097
   123
/**
nkeynes@1097
   124
 * Construct a new image-based disc using the given filename as the base source.
nkeynes@1097
   125
 * TOC is initialized to the empty values.
nkeynes@1097
   126
 */
nkeynes@1097
   127
static cdrom_disc_t cdrom_disc_image_new( const char *filename, ERROR *err )
nkeynes@1097
   128
{
nkeynes@1097
   129
    cdrom_disc_t disc = cdrom_disc_new( filename, err );
nkeynes@1097
   130
    if( disc != NULL && filename != NULL ) {
nkeynes@1097
   131
        disc->base_source = file_sector_source_new_filename( filename, SECTOR_UNKNOWN, 0, FILE_SECTOR_FULL_FILE );
nkeynes@1097
   132
        if( disc->base_source == NULL ) {
nkeynes@1097
   133
            SET_ERROR( err, errno, "Unable to open cdrom file '%s': %s", filename, strerror(errno) );
nkeynes@1097
   134
            cdrom_disc_unref(disc);
nkeynes@1097
   135
            disc = NULL;
nkeynes@1097
   136
        } else {
nkeynes@1097
   137
            sector_source_ref(disc->base_source);
nkeynes@1097
   138
        }
nkeynes@1097
   139
nkeynes@1097
   140
    }
nkeynes@1097
   141
    return disc;
nkeynes@1097
   142
}
nkeynes@1097
   143
nkeynes@1097
   144
cdrom_lba_t cdrom_disc_compute_leadout( cdrom_disc_t disc )
nkeynes@1097
   145
{
nkeynes@1097
   146
    if( disc->track_count == 0 ) {
nkeynes@1097
   147
        disc->leadout = 0;
nkeynes@1097
   148
    } else {
nkeynes@1097
   149
        cdrom_track_t last_track = &disc->track[disc->track_count-1];
nkeynes@1097
   150
        if( last_track->source != NULL ) {
nkeynes@1097
   151
            cdrom_lba_t leadout = last_track->lba + last_track->source->size;
nkeynes@1097
   152
            if( leadout > disc->leadout )
nkeynes@1097
   153
                disc->leadout = leadout;
nkeynes@1097
   154
        }
nkeynes@1097
   155
    }
nkeynes@1097
   156
    return disc->leadout;
nkeynes@1097
   157
}
nkeynes@1097
   158
nkeynes@1097
   159
nkeynes@1097
   160
void cdrom_disc_set_default_disc_type( cdrom_disc_t disc )
nkeynes@1097
   161
{
nkeynes@1097
   162
    int type = CDROM_DISC_NONE, i;
nkeynes@1097
   163
    for( i=0; i<disc->track_count; i++ ) {
nkeynes@1097
   164
        if( (disc->track[i].flags & TRACK_FLAG_DATA == 0) ) {
nkeynes@1097
   165
            if( type == CDROM_DISC_NONE )
nkeynes@1097
   166
                type = CDROM_DISC_AUDIO;
nkeynes@1097
   167
        } else if( disc->track[i].source != NULL &&
nkeynes@1097
   168
                   (disc->track[i].source->mode == SECTOR_MODE1 ||
nkeynes@1097
   169
                    disc->track[i].source->mode == SECTOR_RAW_NONXA) ) {
nkeynes@1097
   170
            if( type != CDROM_DISC_XA )
nkeynes@1097
   171
                type = CDROM_DISC_NONXA;
nkeynes@1097
   172
        } else {
nkeynes@1097
   173
            type = CDROM_DISC_XA;
nkeynes@1097
   174
            break;
nkeynes@1097
   175
        }
nkeynes@1097
   176
    }
nkeynes@1097
   177
    disc->disc_type = type;
nkeynes@1097
   178
}
nkeynes@1097
   179
nkeynes@1097
   180
void cdrom_disc_clear_toc( cdrom_disc_t disc )
nkeynes@1097
   181
{
nkeynes@1097
   182
    disc->disc_type = CDROM_DISC_NONE;
nkeynes@1097
   183
    disc->leadout = 0;
nkeynes@1097
   184
    disc->track_count = 0;
nkeynes@1097
   185
    disc->session_count = 0;
nkeynes@1097
   186
    for( unsigned i=0; i< CDROM_MAX_TRACKS; i++ ) {
nkeynes@1097
   187
        if( disc->track[i].source != NULL ) {
nkeynes@1097
   188
            sector_source_unref( disc->track[i].source );
nkeynes@1097
   189
            disc->track[i].source = NULL;
nkeynes@1097
   190
        }
nkeynes@1097
   191
    }
nkeynes@1097
   192
}
nkeynes@1097
   193
nkeynes@1097
   194
gboolean cdrom_disc_read_toc( cdrom_disc_t disc, ERROR *err )
nkeynes@1097
   195
{
nkeynes@1097
   196
    /* First set the defaults for an empty disc */
nkeynes@1097
   197
    cdrom_disc_clear_toc(disc);
nkeynes@1097
   198
nkeynes@1097
   199
    if( disc->read_toc(disc, err ) ) {
nkeynes@1097
   200
        /* Success - update disc type and leadout if the TOC read didn't set them */
nkeynes@1097
   201
        if( disc->disc_type == CDROM_DISC_NONE )
nkeynes@1097
   202
            cdrom_disc_set_default_disc_type(disc);
nkeynes@1097
   203
        cdrom_disc_compute_leadout(disc);
nkeynes@1097
   204
        return TRUE;
nkeynes@1097
   205
    } else {
nkeynes@1097
   206
        /* Reset to an empty disc in case the reader left things in an
nkeynes@1097
   207
         * inconsistent state */
nkeynes@1097
   208
        cdrom_disc_clear_toc(disc);
nkeynes@1097
   209
        return FALSE;
nkeynes@1097
   210
    }
nkeynes@1097
   211
}
nkeynes@1097
   212
nkeynes@1097
   213
FILE *cdrom_disc_get_base_file( cdrom_disc_t disc )
nkeynes@1097
   214
{
nkeynes@1097
   215
    return file_sector_source_get_file(disc->base_source);
nkeynes@1097
   216
}
nkeynes@1097
   217
nkeynes@1097
   218
/*************************** Public functions ***************************/
nkeynes@1097
   219
nkeynes@1097
   220
cdrom_disc_t cdrom_disc_open( const char *inFilename, ERROR *err )
nkeynes@1097
   221
{
nkeynes@1097
   222
    const gchar *filename = inFilename;
nkeynes@1097
   223
    const gchar *ext = strrchr(filename, '.');
nkeynes@1097
   224
    int i;
nkeynes@1097
   225
    cdrom_disc_factory_t extclz = NULL;
nkeynes@1097
   226
nkeynes@1097
   227
    /* Ask the drive list if it recognizes the name first */
nkeynes@1097
   228
    cdrom_drive_t drive = cdrom_drive_find(inFilename);
nkeynes@1097
   229
    if( drive != NULL ) {
nkeynes@1097
   230
        return cdrom_drive_open(drive, err);
nkeynes@1097
   231
    }
nkeynes@1097
   232
nkeynes@1097
   233
    cdrom_disc_t disc = cdrom_disc_image_new( filename, err );
nkeynes@1097
   234
    if( disc == NULL )
nkeynes@1097
   235
        return NULL;
nkeynes@1097
   236
nkeynes@1097
   237
    /* check file extensions first */
nkeynes@1097
   238
    FILE *f = file_sector_source_get_file(disc->base_source);
nkeynes@1097
   239
    if( ext != NULL ) {
nkeynes@1097
   240
        ext++; /* Skip the '.' */
nkeynes@1097
   241
        for( i=0; cdrom_disc_factories[i] != NULL; i++ ) {
nkeynes@1097
   242
            if( cdrom_disc_factories[i]->extension != NULL &&
nkeynes@1097
   243
                    strcasecmp( cdrom_disc_factories[i]->extension, ext ) == 0 ) {
nkeynes@1097
   244
                extclz = cdrom_disc_factories[i];
nkeynes@1097
   245
                if( extclz->is_valid_file(f) ) {
nkeynes@1097
   246
                    disc->read_toc = extclz->read_toc;
nkeynes@1097
   247
                }
nkeynes@1097
   248
                break;
nkeynes@1097
   249
            }
nkeynes@1097
   250
        }
nkeynes@1097
   251
    }
nkeynes@1097
   252
nkeynes@1097
   253
    if( disc->read_toc == NULL ) {
nkeynes@1097
   254
        /* Okay, fall back to magic */
nkeynes@1097
   255
        for( i=0; cdrom_disc_factories[i] != NULL; i++ ) {
nkeynes@1097
   256
            if( cdrom_disc_factories[i] != extclz &&
nkeynes@1097
   257
                cdrom_disc_factories[i]->is_valid_file(f) ) {
nkeynes@1097
   258
                disc->read_toc = cdrom_disc_factories[i]->read_toc;
nkeynes@1097
   259
                break;
nkeynes@1097
   260
            }
nkeynes@1097
   261
        }
nkeynes@1097
   262
    }
nkeynes@1097
   263
nkeynes@1097
   264
    if( disc->read_toc != NULL && cdrom_disc_read_toc( disc, err ) ) {
nkeynes@1097
   265
        /* All good */
nkeynes@1097
   266
        return disc;
nkeynes@1097
   267
    } else {
nkeynes@1097
   268
        /* No handler found for file */
nkeynes@1097
   269
        cdrom_disc_unref( disc );
nkeynes@1097
   270
        SET_ERROR( err, EINVAL, "File '%s' could not be recognized as any known image file or device type" );
nkeynes@1097
   271
        return NULL;
nkeynes@1097
   272
    }
nkeynes@1097
   273
}
nkeynes@1097
   274
nkeynes@1097
   275
/**
nkeynes@1097
   276
 * Get the track information for the given track. If there is no such track,
nkeynes@1097
   277
 * return NULL;
nkeynes@1097
   278
 */
nkeynes@1097
   279
cdrom_track_t cdrom_disc_get_track( cdrom_disc_t disc, cdrom_trackno_t track )
nkeynes@1097
   280
{
nkeynes@1097
   281
    if( track < 1 || track >= disc->track_count )
nkeynes@1097
   282
        return NULL;
nkeynes@1097
   283
    return &disc->track[track-1];
nkeynes@1097
   284
}
nkeynes@1097
   285
nkeynes@1097
   286
/**
nkeynes@1097
   287
 * Get the track information for the first track of the given session. If there
nkeynes@1097
   288
 * is no such session, return NULL;
nkeynes@1097
   289
 */
nkeynes@1097
   290
cdrom_track_t cdrom_disc_get_session( cdrom_disc_t disc, cdrom_sessionno_t session )
nkeynes@1097
   291
{
nkeynes@1097
   292
    for( unsigned i=0; i< disc->track_count; i++ ) {
nkeynes@1097
   293
        if( disc->track[i].sessionno == session )
nkeynes@1097
   294
            return &disc->track[i];
nkeynes@1097
   295
    }
nkeynes@1097
   296
    return NULL;
nkeynes@1097
   297
}
nkeynes@1097
   298
nkeynes@1097
   299
cdrom_count_t cdrom_disc_get_track_size( cdrom_disc_t disc, cdrom_track_t track )
nkeynes@1097
   300
{
nkeynes@1097
   301
    if( track->trackno == disc->track_count )
nkeynes@1097
   302
        return disc->leadout - track->lba;
nkeynes@1097
   303
    else
nkeynes@1097
   304
        return disc->track[track->trackno].lba - track->lba;
nkeynes@1097
   305
}
nkeynes@1097
   306
nkeynes@1097
   307
cdrom_track_t cdrom_disc_get_last_track( cdrom_disc_t disc )
nkeynes@1097
   308
{
nkeynes@1097
   309
    if( disc->track_count == 0 )
nkeynes@1097
   310
        return NULL;
nkeynes@1097
   311
    return &disc->track[disc->track_count-1];
nkeynes@1097
   312
}
nkeynes@1097
   313
nkeynes@1099
   314
cdrom_track_t cdrom_disc_get_last_data_track( cdrom_disc_t disc )
nkeynes@1099
   315
{
nkeynes@1099
   316
    for( unsigned i=disc->track_count; i>0; i-- ) {
nkeynes@1099
   317
        if( disc->track[i-1].flags & TRACK_FLAG_DATA ) {
nkeynes@1099
   318
            return &disc->track[i-1];
nkeynes@1099
   319
        }
nkeynes@1099
   320
    }
nkeynes@1099
   321
    return NULL;
nkeynes@1099
   322
}
nkeynes@1097
   323
cdrom_track_t cdrom_disc_prev_track( cdrom_disc_t disc, cdrom_track_t track )
nkeynes@1097
   324
{
nkeynes@1097
   325
    if( track->trackno <= 1 )
nkeynes@1097
   326
        return NULL;
nkeynes@1097
   327
    return cdrom_disc_get_track( disc, track->trackno-1 );
nkeynes@1097
   328
}
nkeynes@1097
   329
nkeynes@1097
   330
cdrom_track_t cdrom_disc_next_track( cdrom_disc_t disc, cdrom_track_t track )
nkeynes@1097
   331
{
nkeynes@1097
   332
    if( track->trackno >= disc->track_count )
nkeynes@1097
   333
        return NULL;
nkeynes@1097
   334
    return cdrom_disc_get_track( disc, track->trackno+1 );
nkeynes@1097
   335
}
nkeynes@1097
   336
nkeynes@1097
   337
/**
nkeynes@1097
   338
 * Find the track containing the sector specified by LBA.
nkeynes@1097
   339
 * Note: this function does not check for media change.
nkeynes@1097
   340
 * @return The track, or NULL if no track contains the sector.
nkeynes@1097
   341
 */
nkeynes@1097
   342
cdrom_track_t cdrom_disc_get_track_by_lba( cdrom_disc_t disc, cdrom_lba_t lba )
nkeynes@1097
   343
{
nkeynes@1097
   344
    if( disc->track_count == 0 || disc->track[0].lba > lba || lba >= disc->leadout )
nkeynes@1097
   345
        return NULL; /* LBA outside disc bounds */
nkeynes@1097
   346
nkeynes@1097
   347
    for( unsigned i=1; i< disc->track_count; i++ ) {
nkeynes@1097
   348
        if( lba < disc->track[i].lba )
nkeynes@1097
   349
            return &disc->track[i-1];
nkeynes@1097
   350
    }
nkeynes@1097
   351
    return &disc->track[disc->track_count-1];
nkeynes@1097
   352
}
nkeynes@1097
   353
nkeynes@1097
   354
cdrom_error_t cdrom_disc_read_sectors( cdrom_disc_t disc, cdrom_lba_t lba, cdrom_count_t count,
nkeynes@1097
   355
                                       cdrom_read_mode_t mode, unsigned char *buf, size_t *length )
nkeynes@1097
   356
{
nkeynes@1097
   357
    return disc->source.read_sectors( &disc->source, lba, count, mode, buf, length );
nkeynes@1097
   358
}
nkeynes@1097
   359
nkeynes@1097
   360
/**
nkeynes@1097
   361
 * Check if the disc contains valid media.
nkeynes@1097
   362
 * @return CDROM_ERROR_OK if disc is present, otherwise CDROM_ERROR_NODISC
nkeynes@1097
   363
 */
nkeynes@1097
   364
cdrom_error_t cdrom_disc_check_media( cdrom_disc_t disc )
nkeynes@1097
   365
{
nkeynes@1097
   366
    if( disc == NULL )
nkeynes@1097
   367
        return CDROM_ERROR_NODISC;
nkeynes@1097
   368
    if( disc->check_media != NULL )
nkeynes@1097
   369
        disc->check_media(disc);
nkeynes@1097
   370
    return disc->disc_type == CDROM_DISC_NONE ? CDROM_ERROR_NODISC : CDROM_ERROR_OK;
nkeynes@1097
   371
}
nkeynes@1097
   372
nkeynes@1097
   373
void cdrom_disc_print_toc( FILE *f, cdrom_disc_t disc )
nkeynes@1097
   374
{
nkeynes@1097
   375
    int i;
nkeynes@1097
   376
    int session = 0;
nkeynes@1097
   377
nkeynes@1097
   378
    if( disc == NULL || disc->track_count == 0 ) {
nkeynes@1097
   379
        fprintf( f, "No disc\n" );
nkeynes@1097
   380
        return;
nkeynes@1097
   381
    }
nkeynes@1097
   382
    for( i=0; i<disc->track_count; i++ ) {
nkeynes@1097
   383
        cdrom_track_t track = &disc->track[i];
nkeynes@1097
   384
        if( track->sessionno != session ) {
nkeynes@1097
   385
            session = disc->track[i].sessionno;
nkeynes@1097
   386
            fprintf( f, "Session %d:\n", session );
nkeynes@1097
   387
        }
nkeynes@1097
   388
        fprintf( f, "  %02d. %6d %02x\n", track->trackno, track->lba, track->flags );
nkeynes@1097
   389
    }
nkeynes@1097
   390
}
nkeynes@1097
   391
nkeynes@1097
   392
void cdrom_disc_dump_toc( cdrom_disc_t disc )
nkeynes@1097
   393
{
nkeynes@1097
   394
    cdrom_disc_print_toc( stderr, disc );
nkeynes@1097
   395
}
.