Search
lxdream.org :: lxdream/src/gdrom/gdimage.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/gdrom/gdimage.c
changeset 1030:864417a57662
prev1023:264e2fd90be8
next1071:182cfe43c09e
author nkeynes
date Mon Jun 22 01:13:16 2009 +0000 (13 years ago)
permissions -rw-r--r--
last change Fix disc type breakage introduced in last refactor
file annotate diff log raw
nkeynes@342
     1
/**
nkeynes@561
     2
 * $Id$
nkeynes@342
     3
 *
nkeynes@342
     4
 * GD-Rom image-file common functions. 
nkeynes@342
     5
 *
nkeynes@342
     6
 * Copyright (c) 2005 Nathan Keynes.
nkeynes@342
     7
 *
nkeynes@342
     8
 * This program is free software; you can redistribute it and/or modify
nkeynes@342
     9
 * it under the terms of the GNU General Public License as published by
nkeynes@342
    10
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@342
    11
 * (at your option) any later version.
nkeynes@342
    12
 *
nkeynes@342
    13
 * This program is distributed in the hope that it will be useful,
nkeynes@342
    14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@342
    15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@342
    16
 * GNU General Public License for more details.
nkeynes@342
    17
 */
nkeynes@342
    18
nkeynes@644
    19
#include <assert.h>
nkeynes@759
    20
#include <stdlib.h>
nkeynes@736
    21
#include <string.h>
nkeynes@840
    22
#include <ctype.h>
nkeynes@796
    23
#include <sys/types.h>
nkeynes@1023
    24
#include <sys/stat.h>
nkeynes@1023
    25
#include <fcntl.h>
nkeynes@422
    26
#include <netinet/in.h>
nkeynes@422
    27
nkeynes@678
    28
#include "gdrom/gddriver.h"
nkeynes@342
    29
#include "gdrom/packet.h"
nkeynes@644
    30
#include "ecc.h"
nkeynes@342
    31
nkeynes@1023
    32
static gboolean gdrom_null_check_status( gdrom_disc_t disc );
nkeynes@342
    33
static gdrom_error_t gdrom_image_read_sector( gdrom_disc_t disc, uint32_t lba, int mode, 
nkeynes@736
    34
                                              unsigned char *buf, uint32_t *readlength );
nkeynes@342
    35
nkeynes@644
    36
static uint8_t gdrom_default_sync[12] = { 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0 };
nkeynes@644
    37
nkeynes@644
    38
#define SECTOR_HEADER_SIZE 16
nkeynes@644
    39
#define SECTOR_SUBHEADER_SIZE 8
nkeynes@644
    40
nkeynes@644
    41
/* Data offset (from start of raw sector) by sector mode */
nkeynes@644
    42
static int gdrom_data_offset[] = { 16, 16, 16, 24, 24, 0, 8, 0, 0 };
nkeynes@644
    43
nkeynes@1023
    44
gdrom_image_class_t gdrom_image_classes[] = { &cdrom_device_class, 
nkeynes@1023
    45
        &nrg_image_class, 
nkeynes@1023
    46
        &cdi_image_class, 
nkeynes@1023
    47
        &gdi_image_class, 
nkeynes@1023
    48
        NULL };
nkeynes@644
    49
nkeynes@498
    50
struct cdrom_sector_header {
nkeynes@498
    51
    uint8_t sync[12];
nkeynes@498
    52
    uint8_t msf[3];
nkeynes@498
    53
    uint8_t mode;
nkeynes@644
    54
    uint8_t subhead[8]; // Mode-2 XA sectors only
nkeynes@498
    55
};
nkeynes@342
    56
nkeynes@1023
    57
gdrom_disc_t gdrom_disc_new( const gchar *filename, FILE *f )
nkeynes@342
    58
{
nkeynes@1023
    59
    gdrom_disc_t disc = (gdrom_disc_t)g_malloc0(sizeof(struct gdrom_disc));
nkeynes@1023
    60
    if( disc == NULL ) {
nkeynes@736
    61
        return NULL;
nkeynes@342
    62
    }
nkeynes@1023
    63
    disc->disc_type = IDE_DISC_NONE;
nkeynes@1023
    64
    disc->file = f;
nkeynes@464
    65
    if( filename == NULL ) {
nkeynes@736
    66
        disc->name = NULL;
nkeynes@464
    67
    } else {
nkeynes@736
    68
        disc->name = g_strdup(filename);
nkeynes@464
    69
    }
nkeynes@464
    70
nkeynes@1023
    71
	disc->check_status = gdrom_null_check_status;
nkeynes@1023
    72
	disc->destroy = gdrom_disc_destroy;
nkeynes@342
    73
    return disc;
nkeynes@342
    74
}
nkeynes@342
    75
nkeynes@1023
    76
void gdrom_disc_destroy( gdrom_disc_t disc, gboolean close_fh )
nkeynes@342
    77
{
nkeynes@492
    78
    int i;
nkeynes@492
    79
    FILE *lastfile = NULL;
nkeynes@1023
    80
    if( disc->file != NULL ) {
nkeynes@1023
    81
    	if( close_fh ) {
nkeynes@1023
    82
        	fclose(disc->file);
nkeynes@1023
    83
    	}
nkeynes@1023
    84
        disc->file = NULL;
nkeynes@342
    85
    }
nkeynes@1023
    86
    for( i=0; i<disc->track_count; i++ ) {
nkeynes@1023
    87
        if( disc->track[i].file != NULL && disc->track[i].file != lastfile ) {
nkeynes@1023
    88
            lastfile = disc->track[i].file;
nkeynes@1023
    89
            /* Track files (if any) are closed regardless of the value of close_fh */
nkeynes@736
    90
            fclose(lastfile);
nkeynes@1023
    91
            disc->track[i].file = NULL;
nkeynes@736
    92
        }
nkeynes@492
    93
    }
nkeynes@464
    94
    if( disc->name != NULL ) {
nkeynes@736
    95
        g_free( (gpointer)disc->name );
nkeynes@736
    96
        disc->name = NULL;
nkeynes@464
    97
    }
nkeynes@1023
    98
    if( disc->display_name != NULL ) {
nkeynes@1023
    99
    	g_free( (gpointer)disc->name );
nkeynes@1023
   100
    	disc->display_name = NULL;
nkeynes@468
   101
    }
nkeynes@468
   102
    free( disc );
nkeynes@468
   103
}
nkeynes@468
   104
nkeynes@1023
   105
/**
nkeynes@1023
   106
 * Construct a new gdrom_disc_t and initalize the vtable to the gdrom image
nkeynes@1023
   107
 * default functions.
nkeynes@1023
   108
 */
nkeynes@1023
   109
gdrom_disc_t gdrom_image_new( const gchar *filename, FILE *f )
nkeynes@342
   110
{
nkeynes@1023
   111
	gdrom_disc_t disc = gdrom_disc_new( filename, f );
nkeynes@1023
   112
	if( disc != NULL ) {
nkeynes@1023
   113
	    disc->read_sector = gdrom_image_read_sector;
nkeynes@1023
   114
	    disc->play_audio = NULL; /* not supported yet */
nkeynes@1023
   115
	    disc->run_time_slice = NULL; /* not needed */
nkeynes@1023
   116
	}
nkeynes@1023
   117
}
nkeynes@1023
   118
nkeynes@1023
   119
nkeynes@1023
   120
gdrom_disc_t gdrom_image_open( const gchar *inFilename )
nkeynes@1023
   121
{
nkeynes@1023
   122
    const gchar *filename = inFilename;
nkeynes@1023
   123
    const gchar *ext = strrchr(filename, '.');
nkeynes@1023
   124
    gdrom_disc_t disc = NULL;
nkeynes@1023
   125
    int fd;
nkeynes@1023
   126
    FILE *f;
nkeynes@342
   127
    int i;
nkeynes@1023
   128
    gdrom_image_class_t extclz = NULL;
nkeynes@1023
   129
nkeynes@1023
   130
    // Check for a url-style filename.
nkeynes@1023
   131
    char *lizard_lips = strstr( filename, "://" );
nkeynes@1023
   132
    if( lizard_lips != NULL ) {
nkeynes@1023
   133
        gchar *path = lizard_lips + 3;
nkeynes@1023
   134
        int method_len = (lizard_lips-filename);
nkeynes@1023
   135
        gchar method[method_len + 1];
nkeynes@1023
   136
        memcpy( method, filename, method_len );
nkeynes@1023
   137
        method[method_len] = '\0';
nkeynes@1023
   138
nkeynes@1023
   139
        if( strcasecmp( method, "file" ) == 0 ) {
nkeynes@1023
   140
            filename = path;
nkeynes@1023
   141
        } else if( strcasecmp( method, "dvd" ) == 0 ||
nkeynes@1023
   142
                strcasecmp( method, "cd" ) == 0 ||
nkeynes@1023
   143
                strcasecmp( method, "cdrom" ) ) {
nkeynes@1023
   144
            return cdrom_open_device( method, path );
nkeynes@1023
   145
        } else {
nkeynes@1023
   146
            ERROR( "Unrecognized URL method '%s' in filename '%s'", method, filename );
nkeynes@1023
   147
            return NULL;
nkeynes@736
   148
        }
nkeynes@342
   149
    }
nkeynes@1023
   150
nkeynes@1023
   151
    fd = open( filename, O_RDONLY | O_NONBLOCK );
nkeynes@1023
   152
    if( fd == -1 ) {
nkeynes@1023
   153
        return NULL;
nkeynes@1023
   154
    }
nkeynes@1023
   155
nkeynes@1023
   156
    f = fdopen(fd, "ro");
nkeynes@1023
   157
nkeynes@1023
   158
nkeynes@1023
   159
    /* try extensions */
nkeynes@1023
   160
    if( ext != NULL ) {
nkeynes@1023
   161
        ext++; /* Skip the '.' */
nkeynes@1023
   162
        for( i=0; gdrom_image_classes[i] != NULL; i++ ) {
nkeynes@1023
   163
            if( gdrom_image_classes[i]->extension != NULL &&
nkeynes@1023
   164
                    strcasecmp( gdrom_image_classes[i]->extension, ext ) == 0 ) {
nkeynes@1023
   165
                extclz = gdrom_image_classes[i];
nkeynes@1023
   166
                if( extclz->is_valid_file(f) ) {
nkeynes@1023
   167
                    disc = extclz->open_image_file(filename, f);
nkeynes@1023
   168
                    if( disc != NULL )
nkeynes@1023
   169
                        return disc;
nkeynes@1023
   170
                }
nkeynes@1023
   171
                break;
nkeynes@1023
   172
            }
nkeynes@1023
   173
        }
nkeynes@1023
   174
    }
nkeynes@1023
   175
nkeynes@1023
   176
    /* Okay, fall back to magic */
nkeynes@1023
   177
    gboolean recognized = FALSE;
nkeynes@1023
   178
    for( i=0; gdrom_image_classes[i] != NULL; i++ ) {
nkeynes@1023
   179
        if( gdrom_image_classes[i] != extclz &&
nkeynes@1023
   180
                gdrom_image_classes[i]->is_valid_file(f) ) {
nkeynes@1023
   181
            recognized = TRUE;
nkeynes@1023
   182
            disc = gdrom_image_classes[i]->open_image_file(filename, f);
nkeynes@1023
   183
            if( disc != NULL )
nkeynes@1023
   184
                return disc;
nkeynes@1023
   185
        }
nkeynes@1023
   186
    }
nkeynes@1023
   187
nkeynes@1023
   188
    fclose(f);
nkeynes@1023
   189
    return NULL;
nkeynes@342
   190
}
nkeynes@342
   191
nkeynes@492
   192
/**
nkeynes@492
   193
 * Read a block from an image file, handling negative file offsets
nkeynes@492
   194
 * with 0-fill.
nkeynes@492
   195
 */
nkeynes@644
   196
static gboolean gdrom_read_block( unsigned char *buf, int file_offset, int length, FILE *f )
nkeynes@492
   197
{
nkeynes@492
   198
    if( file_offset < 0 ) {
nkeynes@736
   199
        int size = -file_offset;
nkeynes@736
   200
        if( size >= length ) {
nkeynes@736
   201
            memset( buf, 0, length );
nkeynes@736
   202
            return TRUE;
nkeynes@736
   203
        } else {
nkeynes@736
   204
            memset( buf, 0, size );
nkeynes@736
   205
            file_offset = 0;
nkeynes@736
   206
            length -= size;
nkeynes@736
   207
        }
nkeynes@492
   208
    }
nkeynes@492
   209
    fseek( f, file_offset, SEEK_SET );
nkeynes@644
   210
    return fread( buf, length, 1, f ) == 1;
nkeynes@492
   211
}
nkeynes@492
   212
nkeynes@644
   213
static void gdrom_build_sector_header( unsigned char *buf, uint32_t lba, 
nkeynes@736
   214
                                       gdrom_track_mode_t sector_mode )
nkeynes@644
   215
{
nkeynes@644
   216
    memcpy( buf, gdrom_default_sync, 12 );
nkeynes@644
   217
    cd_build_address( buf, sector_mode, lba );
nkeynes@644
   218
}
nkeynes@644
   219
nkeynes@644
   220
/**
nkeynes@644
   221
 * Return TRUE if the given read mode + track modes are compatible,
nkeynes@644
   222
 * otherwise FALSE.
nkeynes@644
   223
 * @param track_mode one of the GDROM_MODE* constants
nkeynes@644
   224
 * @param read_mode the READ_CD_MODE from the read request
nkeynes@644
   225
 */
nkeynes@644
   226
static gboolean gdrom_is_compatible_read_mode( int track_mode, int read_mode )
nkeynes@644
   227
{
nkeynes@644
   228
    switch( read_mode ) {
nkeynes@644
   229
    case READ_CD_MODE_ANY:
nkeynes@736
   230
        return TRUE;
nkeynes@644
   231
    case READ_CD_MODE_CDDA:
nkeynes@736
   232
        return track_mode == GDROM_CDDA;
nkeynes@644
   233
    case READ_CD_MODE_1:
nkeynes@736
   234
        return track_mode == GDROM_MODE1 || track_mode == GDROM_MODE2_FORM1;
nkeynes@644
   235
    case READ_CD_MODE_2_FORM_1:
nkeynes@736
   236
        return track_mode == GDROM_MODE1 || track_mode == GDROM_MODE2_FORM1;
nkeynes@644
   237
    case READ_CD_MODE_2_FORM_2:
nkeynes@736
   238
        return track_mode == GDROM_MODE2_FORM2;
nkeynes@644
   239
    case READ_CD_MODE_2:
nkeynes@736
   240
        return track_mode == GDROM_MODE2_FORMLESS;
nkeynes@644
   241
    default:
nkeynes@736
   242
        return FALSE;
nkeynes@644
   243
    }
nkeynes@644
   244
}
nkeynes@644
   245
nkeynes@1030
   246
void gdrom_set_disc_type( gdrom_disc_t disc ) 
nkeynes@1030
   247
{
nkeynes@1030
   248
    int type = IDE_DISC_NONE, i;
nkeynes@1030
   249
    for( i=0; i<disc->track_count; i++ ) {
nkeynes@1030
   250
        if( disc->track[i].mode == GDROM_CDDA ) {
nkeynes@1030
   251
            if( type == IDE_DISC_NONE )
nkeynes@1030
   252
                type = IDE_DISC_AUDIO;
nkeynes@1030
   253
        } else if( disc->track[i].mode == GDROM_MODE1 || disc->track[i].mode == GDROM_RAW_NONXA ) {
nkeynes@1030
   254
            if( type != IDE_DISC_CDROMXA )
nkeynes@1030
   255
                type = IDE_DISC_CDROM;
nkeynes@1030
   256
        } else {
nkeynes@1030
   257
            type = IDE_DISC_CDROMXA;
nkeynes@1030
   258
            break;
nkeynes@1030
   259
        }
nkeynes@1030
   260
    }
nkeynes@1030
   261
    disc->disc_type = type;
nkeynes@1030
   262
}
nkeynes@1030
   263
nkeynes@644
   264
/**
nkeynes@644
   265
 * Determine the start position in a raw sector, and the amount of data to read
nkeynes@644
   266
 * in bytes, for a given combination of sector mode and read mode.
nkeynes@644
   267
 */ 
nkeynes@644
   268
static void gdrom_get_read_bounds( int sector_mode, int read_mode, int *start, int *size )
nkeynes@644
   269
{
nkeynes@644
   270
    if( READ_CD_RAW(read_mode) ) {
nkeynes@736
   271
        // whole sector
nkeynes@736
   272
        *start = 0;
nkeynes@736
   273
        *size = 2352;
nkeynes@644
   274
    } else {
nkeynes@736
   275
        *size = 0;
nkeynes@736
   276
        if( READ_CD_DATA(read_mode) ) {
nkeynes@736
   277
            *start = gdrom_data_offset[sector_mode];
nkeynes@736
   278
            *size = gdrom_sector_size[sector_mode];
nkeynes@736
   279
        }
nkeynes@644
   280
nkeynes@736
   281
        if( READ_CD_SUBHEAD(read_mode) && 
nkeynes@736
   282
                (sector_mode == GDROM_MODE2_FORM1 || sector_mode == GDROM_MODE2_FORM2) ) {
nkeynes@736
   283
            *start = SECTOR_HEADER_SIZE;
nkeynes@736
   284
            *size += SECTOR_SUBHEADER_SIZE;
nkeynes@736
   285
        }
nkeynes@644
   286
nkeynes@736
   287
        if( READ_CD_HEADER(read_mode) ) {
nkeynes@736
   288
            *size += SECTOR_HEADER_SIZE;
nkeynes@736
   289
            *start = 0;
nkeynes@736
   290
        }
nkeynes@644
   291
nkeynes@644
   292
    }
nkeynes@644
   293
}
nkeynes@644
   294
nkeynes@782
   295
void gdrom_extract_raw_data_sector( char *sector_data, int channels, unsigned char *buf, uint32_t *length )
nkeynes@782
   296
{
nkeynes@782
   297
    int sector_mode;
nkeynes@782
   298
    int start, size;
nkeynes@782
   299
    struct cdrom_sector_header *secthead = (struct cdrom_sector_header *)sector_data;
nkeynes@782
   300
    if( secthead->mode == 1 ) {
nkeynes@782
   301
        sector_mode = GDROM_MODE1;
nkeynes@782
   302
    } else {
nkeynes@782
   303
        sector_mode = ((secthead->subhead[2] & 0x20) == 0 ) ? GDROM_MODE2_FORM1 : GDROM_MODE2_FORM2;
nkeynes@782
   304
    }
nkeynes@782
   305
    gdrom_get_read_bounds( sector_mode, channels, &start, &size );
nkeynes@782
   306
    
nkeynes@782
   307
    memcpy( buf, sector_data+start, size );
nkeynes@782
   308
    *length = size;
nkeynes@782
   309
}
nkeynes@782
   310
nkeynes@644
   311
/**
nkeynes@1023
   312
 * Default check media status that does nothing and always returns
nkeynes@1023
   313
 * false (unchanged).
nkeynes@1023
   314
 */
nkeynes@1023
   315
static gboolean gdrom_null_check_status( gdrom_disc_t disc )
nkeynes@1023
   316
{
nkeynes@1023
   317
	return FALSE;
nkeynes@1023
   318
}
nkeynes@1023
   319
nkeynes@1023
   320
/**
nkeynes@644
   321
 * Read a single sector from a disc image. If you thought this would be simple, 
nkeynes@644
   322
 * I have just one thing to say to you: Bwahahahahahahahah.
nkeynes@644
   323
 *
nkeynes@644
   324
 * Once we've decided that there's a real sector at the requested lba, there's 
nkeynes@644
   325
 * really two things we need to care about:
nkeynes@644
   326
 *   1. Is the sector mode compatible with the requested read mode
nkeynes@644
   327
 *   2. Which parts of the sector do we need to return? 
nkeynes@644
   328
 *      (header/subhead/data/raw sector)
nkeynes@644
   329
 *
nkeynes@644
   330
 * Also note that the disc image may supply us with just the data (most common 
nkeynes@644
   331
 * case), or may have the full raw sector. In the former case we may need to 
nkeynes@644
   332
 * generate the missing data on the fly, for which we use libedc to compute the
nkeynes@644
   333
 * data correction codes.
nkeynes@644
   334
 */
nkeynes@342
   335
static gdrom_error_t gdrom_image_read_sector( gdrom_disc_t disc, uint32_t lba,
nkeynes@736
   336
                                              int mode, unsigned char *buf, uint32_t *length )
nkeynes@342
   337
{
nkeynes@498
   338
    struct cdrom_sector_header secthead;
nkeynes@422
   339
    int file_offset, read_len, track_no;
nkeynes@498
   340
nkeynes@492
   341
    FILE *f;
nkeynes@342
   342
nkeynes@1023
   343
    track_no = gdrom_disc_get_track_by_lba( disc, lba );
nkeynes@342
   344
    if( track_no == -1 ) {
nkeynes@736
   345
        return PKT_ERR_BADREAD;
nkeynes@342
   346
    }
nkeynes@1023
   347
    struct gdrom_track *track = &disc->track[track_no-1];
nkeynes@342
   348
    file_offset = track->offset + track->sector_size * (lba - track->lba);
nkeynes@342
   349
    read_len = track->sector_size;
nkeynes@492
   350
    if( track->file != NULL ) {
nkeynes@736
   351
        f = track->file;
nkeynes@492
   352
    } else {
nkeynes@1023
   353
        f = disc->file;
nkeynes@492
   354
    }
nkeynes@492
   355
nkeynes@644
   356
    /* First figure out what the real sector mode is for raw/semiraw sectors */
nkeynes@644
   357
    int sector_mode;
nkeynes@644
   358
    switch( track->mode ) {
nkeynes@644
   359
    case GDROM_RAW_NONXA:
nkeynes@736
   360
        gdrom_read_block( (unsigned char *)(&secthead), file_offset, sizeof(secthead), f );
nkeynes@736
   361
        sector_mode = (secthead.mode == 1) ? GDROM_MODE1 : GDROM_MODE2_FORMLESS;
nkeynes@736
   362
        break;
nkeynes@644
   363
    case GDROM_RAW_XA:
nkeynes@736
   364
        gdrom_read_block( (unsigned char *)(&secthead), file_offset, sizeof(secthead), f );
nkeynes@736
   365
        if( secthead.mode == 1 ) {
nkeynes@736
   366
            sector_mode = GDROM_MODE1;
nkeynes@736
   367
        } else {
nkeynes@736
   368
            sector_mode = ((secthead.subhead[2] & 0x20) == 0 ) ? GDROM_MODE2_FORM1 : GDROM_MODE2_FORM2;
nkeynes@736
   369
        }
nkeynes@736
   370
        break;
nkeynes@644
   371
    case GDROM_SEMIRAW_MODE2:
nkeynes@736
   372
        gdrom_read_block( secthead.subhead, file_offset, 8, f );
nkeynes@736
   373
        sector_mode = ((secthead.subhead[2] & 0x20) == 0 ) ? GDROM_MODE2_FORM1 : GDROM_MODE2_FORM2;
nkeynes@736
   374
        break;
nkeynes@342
   375
    default:
nkeynes@736
   376
        /* In the other cases, the track mode completely defines the sector mode */
nkeynes@736
   377
        sector_mode = track->mode;
nkeynes@736
   378
        break;
nkeynes@644
   379
    }
nkeynes@644
   380
nkeynes@644
   381
    if( !gdrom_is_compatible_read_mode(sector_mode, READ_CD_MODE(mode)) ) {
nkeynes@736
   382
        return PKT_ERR_BADREADMODE;
nkeynes@342
   383
    }
nkeynes@644
   384
nkeynes@644
   385
    /* Ok, we've got a valid sector, check what parts of the sector we need to
nkeynes@644
   386
     * return - header | subhead | data | everything
nkeynes@644
   387
     */
nkeynes@644
   388
    int channels = READ_CD_CHANNELS(mode);
nkeynes@644
   389
nkeynes@644
   390
    if( channels == 0 ) {
nkeynes@736
   391
        // legal, if weird
nkeynes@736
   392
        *length = 0;
nkeynes@736
   393
        return PKT_ERR_OK;
nkeynes@644
   394
    } else if( channels == 0xA0 && 
nkeynes@736
   395
            (sector_mode == GDROM_MODE2_FORM1 || sector_mode == GDROM_MODE2_FORM2 )) {
nkeynes@736
   396
        // caller requested a non-contiguous region
nkeynes@736
   397
        return PKT_ERR_BADFIELD;
nkeynes@644
   398
    } else if( READ_CD_RAW(channels) ) {
nkeynes@736
   399
        channels = 0xF0; // implies everything
nkeynes@644
   400
    }
nkeynes@644
   401
nkeynes@644
   402
    read_len = 0;
nkeynes@644
   403
    int start, size;
nkeynes@644
   404
    switch( track->mode ) {
nkeynes@644
   405
    case GDROM_CDDA:
nkeynes@736
   406
        // audio is nice and simple (assume perfect reads for now)
nkeynes@736
   407
        *length = 2352;
nkeynes@736
   408
        gdrom_read_block( buf, file_offset, track->sector_size, f );
nkeynes@736
   409
        return PKT_ERR_OK;
nkeynes@644
   410
    case GDROM_RAW_XA:
nkeynes@644
   411
    case GDROM_RAW_NONXA:
nkeynes@736
   412
        gdrom_get_read_bounds( sector_mode, channels, &start, &size );
nkeynes@736
   413
        gdrom_read_block( buf, file_offset+start, size, f );
nkeynes@736
   414
        read_len = size;
nkeynes@736
   415
        break;
nkeynes@644
   416
    case GDROM_SEMIRAW_MODE2:
nkeynes@736
   417
        gdrom_get_read_bounds( sector_mode, channels, &start, &size );
nkeynes@736
   418
        if( READ_CD_HEADER(channels) ) {
nkeynes@736
   419
            gdrom_build_sector_header( buf, lba, sector_mode );
nkeynes@736
   420
            read_len += SECTOR_HEADER_SIZE;
nkeynes@736
   421
            size -= SECTOR_HEADER_SIZE;
nkeynes@736
   422
        } else {
nkeynes@736
   423
            start -= SECTOR_HEADER_SIZE;
nkeynes@736
   424
        }
nkeynes@736
   425
        gdrom_read_block( buf + read_len, file_offset+start, size, f );
nkeynes@736
   426
        read_len += size;
nkeynes@736
   427
        break;
nkeynes@644
   428
    default: // Data track w/ data only in file
nkeynes@736
   429
        if( READ_CD_RAW(channels) ) {
nkeynes@736
   430
            gdrom_read_block( buf + gdrom_data_offset[track->mode], file_offset, 
nkeynes@736
   431
                    track->sector_size, f );
nkeynes@736
   432
            do_encode_L2( buf, sector_mode, lba );
nkeynes@736
   433
            read_len = 2352;
nkeynes@736
   434
        } else {
nkeynes@736
   435
            if( READ_CD_HEADER(channels) ) {
nkeynes@736
   436
                gdrom_build_sector_header( buf, lba, sector_mode );
nkeynes@736
   437
                read_len += SECTOR_HEADER_SIZE;
nkeynes@736
   438
            }
nkeynes@736
   439
            if( READ_CD_SUBHEAD(channels) && 
nkeynes@736
   440
                    (sector_mode == GDROM_MODE2_FORM1 || sector_mode == GDROM_MODE2_FORM2) ) {
nkeynes@736
   441
                if( sector_mode == GDROM_MODE2_FORM1 ) {
nkeynes@736
   442
                    *((uint32_t *)(buf+read_len)) = 0;
nkeynes@736
   443
                    *((uint32_t *)(buf+read_len+4)) = 0;
nkeynes@736
   444
                } else {
nkeynes@736
   445
                    *((uint32_t *)(buf+read_len)) = 0x00200000;
nkeynes@736
   446
                    *((uint32_t *)(buf+read_len+4)) = 0x00200000;
nkeynes@736
   447
                }
nkeynes@736
   448
                read_len += 8;
nkeynes@736
   449
            }
nkeynes@736
   450
            if( READ_CD_DATA(channels) ) {
nkeynes@736
   451
                gdrom_read_block( buf+read_len, file_offset, track->sector_size, f );
nkeynes@736
   452
                read_len += track->sector_size;
nkeynes@736
   453
            }
nkeynes@736
   454
        }
nkeynes@644
   455
    }
nkeynes@342
   456
    *length = read_len;
nkeynes@342
   457
    return PKT_ERR_OK;
nkeynes@342
   458
}
nkeynes@342
   459
.