4 * GD-Rom image-file common functions.
6 * Copyright (c) 2005 Nathan Keynes.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
23 #include <sys/types.h>
26 #include <netinet/in.h>
28 #include "gdrom/gddriver.h"
29 #include "gdrom/packet.h"
32 static gboolean gdrom_null_check_status( gdrom_disc_t disc );
33 static gdrom_error_t gdrom_image_read_sector( gdrom_disc_t disc, uint32_t lba, int mode,
34 unsigned char *buf, uint32_t *readlength );
36 static uint8_t gdrom_default_sync[12] = { 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0 };
38 #define SECTOR_HEADER_SIZE 16
39 #define SECTOR_SUBHEADER_SIZE 8
41 /* Data offset (from start of raw sector) by sector mode */
42 static int gdrom_data_offset[] = { 16, 16, 16, 24, 24, 0, 8, 0, 0 };
44 gdrom_image_class_t gdrom_image_classes[] = { &cdrom_device_class,
50 struct cdrom_sector_header {
54 uint8_t subhead[8]; // Mode-2 XA sectors only
57 gdrom_disc_t gdrom_disc_new( const gchar *filename, FILE *f )
59 gdrom_disc_t disc = (gdrom_disc_t)g_malloc0(sizeof(struct gdrom_disc));
63 disc->disc_type = IDE_DISC_NONE;
65 if( filename == NULL ) {
68 disc->name = g_strdup(filename);
71 disc->check_status = gdrom_null_check_status;
72 disc->destroy = gdrom_disc_destroy;
76 void gdrom_disc_destroy( gdrom_disc_t disc, gboolean close_fh )
79 FILE *lastfile = NULL;
80 if( disc->file != NULL ) {
86 for( i=0; i<disc->track_count; i++ ) {
87 if( disc->track[i].file != NULL && disc->track[i].file != lastfile ) {
88 lastfile = disc->track[i].file;
89 /* Track files (if any) are closed regardless of the value of close_fh */
91 disc->track[i].file = NULL;
94 if( disc->name != NULL ) {
95 g_free( (gpointer)disc->name );
98 if( disc->display_name != NULL ) {
99 g_free( (gpointer)disc->name );
100 disc->display_name = NULL;
106 * Construct a new gdrom_disc_t and initalize the vtable to the gdrom image
109 gdrom_disc_t gdrom_image_new( const gchar *filename, FILE *f )
111 gdrom_disc_t disc = gdrom_disc_new( filename, f );
113 disc->read_sector = gdrom_image_read_sector;
114 disc->play_audio = NULL; /* not supported yet */
115 disc->run_time_slice = NULL; /* not needed */
120 gdrom_disc_t gdrom_image_open( const gchar *inFilename )
122 const gchar *filename = inFilename;
123 const gchar *ext = strrchr(filename, '.');
124 gdrom_disc_t disc = NULL;
128 gdrom_image_class_t extclz = NULL;
130 // Check for a url-style filename.
131 char *lizard_lips = strstr( filename, "://" );
132 if( lizard_lips != NULL ) {
133 gchar *path = lizard_lips + 3;
134 int method_len = (lizard_lips-filename);
135 gchar method[method_len + 1];
136 memcpy( method, filename, method_len );
137 method[method_len] = '\0';
139 if( strcasecmp( method, "file" ) == 0 ) {
141 } else if( strcasecmp( method, "dvd" ) == 0 ||
142 strcasecmp( method, "cd" ) == 0 ||
143 strcasecmp( method, "cdrom" ) ) {
144 return cdrom_open_device( method, path );
146 ERROR( "Unrecognized URL method '%s' in filename '%s'", method, filename );
151 fd = open( filename, O_RDONLY | O_NONBLOCK );
156 f = fdopen(fd, "ro");
161 ext++; /* Skip the '.' */
162 for( i=0; gdrom_image_classes[i] != NULL; i++ ) {
163 if( gdrom_image_classes[i]->extension != NULL &&
164 strcasecmp( gdrom_image_classes[i]->extension, ext ) == 0 ) {
165 extclz = gdrom_image_classes[i];
166 if( extclz->is_valid_file(f) ) {
167 disc = extclz->open_image_file(filename, f);
176 /* Okay, fall back to magic */
177 gboolean recognized = FALSE;
178 for( i=0; gdrom_image_classes[i] != NULL; i++ ) {
179 if( gdrom_image_classes[i] != extclz &&
180 gdrom_image_classes[i]->is_valid_file(f) ) {
182 disc = gdrom_image_classes[i]->open_image_file(filename, f);
193 * Read a block from an image file, handling negative file offsets
196 static gboolean gdrom_read_block( unsigned char *buf, int file_offset, int length, FILE *f )
198 if( file_offset < 0 ) {
199 int size = -file_offset;
200 if( size >= length ) {
201 memset( buf, 0, length );
204 memset( buf, 0, size );
209 fseek( f, file_offset, SEEK_SET );
210 return fread( buf, length, 1, f ) == 1;
213 static void gdrom_build_sector_header( unsigned char *buf, uint32_t lba,
214 gdrom_track_mode_t sector_mode )
216 memcpy( buf, gdrom_default_sync, 12 );
217 cd_build_address( buf, sector_mode, lba );
221 * Return TRUE if the given read mode + track modes are compatible,
223 * @param track_mode one of the GDROM_MODE* constants
224 * @param read_mode the READ_CD_MODE from the read request
226 static gboolean gdrom_is_compatible_read_mode( int track_mode, int read_mode )
228 switch( read_mode ) {
229 case READ_CD_MODE_ANY:
231 case READ_CD_MODE_CDDA:
232 return track_mode == GDROM_CDDA;
234 return track_mode == GDROM_MODE1 || track_mode == GDROM_MODE2_FORM1;
235 case READ_CD_MODE_2_FORM_1:
236 return track_mode == GDROM_MODE1 || track_mode == GDROM_MODE2_FORM1;
237 case READ_CD_MODE_2_FORM_2:
238 return track_mode == GDROM_MODE2_FORM2;
240 return track_mode == GDROM_MODE2_FORMLESS;
246 void gdrom_set_disc_type( gdrom_disc_t disc )
248 int type = IDE_DISC_NONE, i;
249 for( i=0; i<disc->track_count; i++ ) {
250 if( disc->track[i].mode == GDROM_CDDA ) {
251 if( type == IDE_DISC_NONE )
252 type = IDE_DISC_AUDIO;
253 } else if( disc->track[i].mode == GDROM_MODE1 || disc->track[i].mode == GDROM_RAW_NONXA ) {
254 if( type != IDE_DISC_CDROMXA )
255 type = IDE_DISC_CDROM;
257 type = IDE_DISC_CDROMXA;
261 disc->disc_type = type;
265 * Determine the start position in a raw sector, and the amount of data to read
266 * in bytes, for a given combination of sector mode and read mode.
268 static void gdrom_get_read_bounds( int sector_mode, int read_mode, int *start, int *size )
270 if( READ_CD_RAW(read_mode) ) {
276 if( READ_CD_DATA(read_mode) ) {
277 *start = gdrom_data_offset[sector_mode];
278 *size = gdrom_sector_size[sector_mode];
281 if( READ_CD_SUBHEAD(read_mode) &&
282 (sector_mode == GDROM_MODE2_FORM1 || sector_mode == GDROM_MODE2_FORM2) ) {
283 *start = SECTOR_HEADER_SIZE;
284 *size += SECTOR_SUBHEADER_SIZE;
287 if( READ_CD_HEADER(read_mode) ) {
288 *size += SECTOR_HEADER_SIZE;
295 void gdrom_extract_raw_data_sector( char *sector_data, int channels, unsigned char *buf, uint32_t *length )
299 struct cdrom_sector_header *secthead = (struct cdrom_sector_header *)sector_data;
300 if( secthead->mode == 1 ) {
301 sector_mode = GDROM_MODE1;
303 sector_mode = ((secthead->subhead[2] & 0x20) == 0 ) ? GDROM_MODE2_FORM1 : GDROM_MODE2_FORM2;
305 gdrom_get_read_bounds( sector_mode, channels, &start, &size );
307 memcpy( buf, sector_data+start, size );
312 * Default check media status that does nothing and always returns
315 static gboolean gdrom_null_check_status( gdrom_disc_t disc )
321 * Read a single sector from a disc image. If you thought this would be simple,
322 * I have just one thing to say to you: Bwahahahahahahahah.
324 * Once we've decided that there's a real sector at the requested lba, there's
325 * really two things we need to care about:
326 * 1. Is the sector mode compatible with the requested read mode
327 * 2. Which parts of the sector do we need to return?
328 * (header/subhead/data/raw sector)
330 * Also note that the disc image may supply us with just the data (most common
331 * case), or may have the full raw sector. In the former case we may need to
332 * generate the missing data on the fly, for which we use libedc to compute the
333 * data correction codes.
335 static gdrom_error_t gdrom_image_read_sector( gdrom_disc_t disc, uint32_t lba,
336 int mode, unsigned char *buf, uint32_t *length )
338 struct cdrom_sector_header secthead;
339 int file_offset, read_len, track_no;
343 track_no = gdrom_disc_get_track_by_lba( disc, lba );
344 if( track_no == -1 ) {
345 return PKT_ERR_BADREAD;
347 struct gdrom_track *track = &disc->track[track_no-1];
348 file_offset = track->offset + track->sector_size * (lba - track->lba);
349 read_len = track->sector_size;
350 if( track->file != NULL ) {
356 /* First figure out what the real sector mode is for raw/semiraw sectors */
358 switch( track->mode ) {
359 case GDROM_RAW_NONXA:
360 gdrom_read_block( (unsigned char *)(§head), file_offset, sizeof(secthead), f );
361 sector_mode = (secthead.mode == 1) ? GDROM_MODE1 : GDROM_MODE2_FORMLESS;
364 gdrom_read_block( (unsigned char *)(§head), file_offset, sizeof(secthead), f );
365 if( secthead.mode == 1 ) {
366 sector_mode = GDROM_MODE1;
368 sector_mode = ((secthead.subhead[2] & 0x20) == 0 ) ? GDROM_MODE2_FORM1 : GDROM_MODE2_FORM2;
371 case GDROM_SEMIRAW_MODE2:
372 gdrom_read_block( secthead.subhead, file_offset, 8, f );
373 sector_mode = ((secthead.subhead[2] & 0x20) == 0 ) ? GDROM_MODE2_FORM1 : GDROM_MODE2_FORM2;
376 /* In the other cases, the track mode completely defines the sector mode */
377 sector_mode = track->mode;
381 if( !gdrom_is_compatible_read_mode(sector_mode, READ_CD_MODE(mode)) ) {
382 return PKT_ERR_BADREADMODE;
385 /* Ok, we've got a valid sector, check what parts of the sector we need to
386 * return - header | subhead | data | everything
388 int channels = READ_CD_CHANNELS(mode);
390 if( channels == 0 ) {
394 } else if( channels == 0xA0 &&
395 (sector_mode == GDROM_MODE2_FORM1 || sector_mode == GDROM_MODE2_FORM2 )) {
396 // caller requested a non-contiguous region
397 return PKT_ERR_BADFIELD;
398 } else if( READ_CD_RAW(channels) ) {
399 channels = 0xF0; // implies everything
404 switch( track->mode ) {
406 // audio is nice and simple (assume perfect reads for now)
408 gdrom_read_block( buf, file_offset, track->sector_size, f );
411 case GDROM_RAW_NONXA:
412 gdrom_get_read_bounds( sector_mode, channels, &start, &size );
413 gdrom_read_block( buf, file_offset+start, size, f );
416 case GDROM_SEMIRAW_MODE2:
417 gdrom_get_read_bounds( sector_mode, channels, &start, &size );
418 if( READ_CD_HEADER(channels) ) {
419 gdrom_build_sector_header( buf, lba, sector_mode );
420 read_len += SECTOR_HEADER_SIZE;
421 size -= SECTOR_HEADER_SIZE;
423 start -= SECTOR_HEADER_SIZE;
425 gdrom_read_block( buf + read_len, file_offset+start, size, f );
428 default: // Data track w/ data only in file
429 if( READ_CD_RAW(channels) ) {
430 gdrom_read_block( buf + gdrom_data_offset[track->mode], file_offset,
431 track->sector_size, f );
432 do_encode_L2( buf, sector_mode, lba );
435 if( READ_CD_HEADER(channels) ) {
436 gdrom_build_sector_header( buf, lba, sector_mode );
437 read_len += SECTOR_HEADER_SIZE;
439 if( READ_CD_SUBHEAD(channels) &&
440 (sector_mode == GDROM_MODE2_FORM1 || sector_mode == GDROM_MODE2_FORM2) ) {
441 if( sector_mode == GDROM_MODE2_FORM1 ) {
442 *((uint32_t *)(buf+read_len)) = 0;
443 *((uint32_t *)(buf+read_len+4)) = 0;
445 *((uint32_t *)(buf+read_len)) = 0x00200000;
446 *((uint32_t *)(buf+read_len+4)) = 0x00200000;
450 if( READ_CD_DATA(channels) ) {
451 gdrom_read_block( buf+read_len, file_offset, track->sector_size, f );
452 read_len += track->sector_size;
.