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 */
121 gdrom_disc_t gdrom_image_open( const gchar *inFilename )
123 const gchar *filename = inFilename;
124 const gchar *ext = strrchr(filename, '.');
125 gdrom_disc_t disc = NULL;
129 gdrom_image_class_t extclz = NULL;
131 // Check for a url-style filename.
132 char *lizard_lips = strstr( filename, "://" );
133 if( lizard_lips != NULL ) {
134 gchar *path = lizard_lips + 3;
135 int method_len = (lizard_lips-filename);
136 gchar method[method_len + 1];
137 memcpy( method, filename, method_len );
138 method[method_len] = '\0';
140 if( strcasecmp( method, "file" ) == 0 ) {
142 } else if( strcasecmp( method, "dvd" ) == 0 ||
143 strcasecmp( method, "cd" ) == 0 ||
144 strcasecmp( method, "cdrom" ) ) {
145 return cdrom_open_device( method, path );
147 ERROR( "Unrecognized URL method '%s' in filename '%s'", method, filename );
152 fd = open( filename, O_RDONLY | O_NONBLOCK );
157 f = fdopen(fd, "ro");
162 ext++; /* Skip the '.' */
163 for( i=0; gdrom_image_classes[i] != NULL; i++ ) {
164 if( gdrom_image_classes[i]->extension != NULL &&
165 strcasecmp( gdrom_image_classes[i]->extension, ext ) == 0 ) {
166 extclz = gdrom_image_classes[i];
167 if( extclz->is_valid_file(f) ) {
168 disc = extclz->open_image_file(filename, f);
177 /* Okay, fall back to magic */
178 gboolean recognized = FALSE;
179 for( i=0; gdrom_image_classes[i] != NULL; i++ ) {
180 if( gdrom_image_classes[i] != extclz &&
181 gdrom_image_classes[i]->is_valid_file(f) ) {
183 disc = gdrom_image_classes[i]->open_image_file(filename, f);
194 * Read a block from an image file, handling negative file offsets
197 static gboolean gdrom_read_block( unsigned char *buf, int file_offset, int length, FILE *f )
199 if( file_offset < 0 ) {
200 int size = -file_offset;
201 if( size >= length ) {
202 memset( buf, 0, length );
205 memset( buf, 0, size );
210 fseek( f, file_offset, SEEK_SET );
211 return fread( buf, length, 1, f ) == 1;
214 static void gdrom_build_sector_header( unsigned char *buf, uint32_t lba,
215 gdrom_track_mode_t sector_mode )
217 memcpy( buf, gdrom_default_sync, 12 );
218 cd_build_address( buf, sector_mode, lba );
222 * Return TRUE if the given read mode + track modes are compatible,
224 * @param track_mode one of the GDROM_MODE* constants
225 * @param read_mode the READ_CD_MODE from the read request
227 static gboolean gdrom_is_compatible_read_mode( int track_mode, int read_mode )
229 switch( read_mode ) {
230 case READ_CD_MODE_ANY:
232 case READ_CD_MODE_CDDA:
233 return track_mode == GDROM_CDDA;
235 return track_mode == GDROM_MODE1 || track_mode == GDROM_MODE2_FORM1;
236 case READ_CD_MODE_2_FORM_1:
237 return track_mode == GDROM_MODE1 || track_mode == GDROM_MODE2_FORM1;
238 case READ_CD_MODE_2_FORM_2:
239 return track_mode == GDROM_MODE2_FORM2;
241 return track_mode == GDROM_MODE2_FORMLESS;
247 void gdrom_set_disc_type( gdrom_disc_t disc )
249 int type = IDE_DISC_NONE, i;
250 for( i=0; i<disc->track_count; i++ ) {
251 if( disc->track[i].mode == GDROM_CDDA ) {
252 if( type == IDE_DISC_NONE )
253 type = IDE_DISC_AUDIO;
254 } else if( disc->track[i].mode == GDROM_MODE1 || disc->track[i].mode == GDROM_RAW_NONXA ) {
255 if( type != IDE_DISC_CDROMXA )
256 type = IDE_DISC_CDROM;
258 type = IDE_DISC_CDROMXA;
262 disc->disc_type = type;
266 * Determine the start position in a raw sector, and the amount of data to read
267 * in bytes, for a given combination of sector mode and read mode.
269 static void gdrom_get_read_bounds( int sector_mode, int read_mode, int *start, int *size )
271 if( READ_CD_RAW(read_mode) ) {
277 if( READ_CD_DATA(read_mode) ) {
278 *start = gdrom_data_offset[sector_mode];
279 *size = gdrom_sector_size[sector_mode];
282 if( READ_CD_SUBHEAD(read_mode) &&
283 (sector_mode == GDROM_MODE2_FORM1 || sector_mode == GDROM_MODE2_FORM2) ) {
284 *start = SECTOR_HEADER_SIZE;
285 *size += SECTOR_SUBHEADER_SIZE;
288 if( READ_CD_HEADER(read_mode) ) {
289 *size += SECTOR_HEADER_SIZE;
296 void gdrom_extract_raw_data_sector( char *sector_data, int channels, unsigned char *buf, uint32_t *length )
300 struct cdrom_sector_header *secthead = (struct cdrom_sector_header *)sector_data;
301 if( secthead->mode == 1 ) {
302 sector_mode = GDROM_MODE1;
304 sector_mode = ((secthead->subhead[2] & 0x20) == 0 ) ? GDROM_MODE2_FORM1 : GDROM_MODE2_FORM2;
306 gdrom_get_read_bounds( sector_mode, channels, &start, &size );
308 memcpy( buf, sector_data+start, size );
313 * Default check media status that does nothing and always returns
316 static gboolean gdrom_null_check_status( gdrom_disc_t disc )
322 * Read a single sector from a disc image. If you thought this would be simple,
323 * I have just one thing to say to you: Bwahahahahahahahah.
325 * Once we've decided that there's a real sector at the requested lba, there's
326 * really two things we need to care about:
327 * 1. Is the sector mode compatible with the requested read mode
328 * 2. Which parts of the sector do we need to return?
329 * (header/subhead/data/raw sector)
331 * Also note that the disc image may supply us with just the data (most common
332 * case), or may have the full raw sector. In the former case we may need to
333 * generate the missing data on the fly, for which we use libedc to compute the
334 * data correction codes.
336 static gdrom_error_t gdrom_image_read_sector( gdrom_disc_t disc, uint32_t lba,
337 int mode, unsigned char *buf, uint32_t *length )
339 struct cdrom_sector_header secthead;
340 int file_offset, read_len, track_no;
344 track_no = gdrom_disc_get_track_by_lba( disc, lba );
345 if( track_no == -1 ) {
346 return PKT_ERR_BADREAD;
348 struct gdrom_track *track = &disc->track[track_no-1];
349 file_offset = track->offset + track->sector_size * (lba - track->lba);
350 read_len = track->sector_size;
351 if( track->file != NULL ) {
357 /* First figure out what the real sector mode is for raw/semiraw sectors */
359 switch( track->mode ) {
360 case GDROM_RAW_NONXA:
361 gdrom_read_block( (unsigned char *)(§head), file_offset, sizeof(secthead), f );
362 sector_mode = (secthead.mode == 1) ? GDROM_MODE1 : GDROM_MODE2_FORMLESS;
365 gdrom_read_block( (unsigned char *)(§head), file_offset, sizeof(secthead), f );
366 if( secthead.mode == 1 ) {
367 sector_mode = GDROM_MODE1;
369 sector_mode = ((secthead.subhead[2] & 0x20) == 0 ) ? GDROM_MODE2_FORM1 : GDROM_MODE2_FORM2;
372 case GDROM_SEMIRAW_MODE2:
373 gdrom_read_block( secthead.subhead, file_offset, 8, f );
374 sector_mode = ((secthead.subhead[2] & 0x20) == 0 ) ? GDROM_MODE2_FORM1 : GDROM_MODE2_FORM2;
377 /* In the other cases, the track mode completely defines the sector mode */
378 sector_mode = track->mode;
382 if( !gdrom_is_compatible_read_mode(sector_mode, READ_CD_MODE(mode)) ) {
383 return PKT_ERR_BADREADMODE;
386 /* Ok, we've got a valid sector, check what parts of the sector we need to
387 * return - header | subhead | data | everything
389 int channels = READ_CD_CHANNELS(mode);
391 if( channels == 0 ) {
395 } else if( channels == 0xA0 &&
396 (sector_mode == GDROM_MODE2_FORM1 || sector_mode == GDROM_MODE2_FORM2 )) {
397 // caller requested a non-contiguous region
398 return PKT_ERR_BADFIELD;
399 } else if( READ_CD_RAW(channels) ) {
400 channels = 0xF0; // implies everything
405 switch( track->mode ) {
407 // audio is nice and simple (assume perfect reads for now)
409 gdrom_read_block( buf, file_offset, track->sector_size, f );
412 case GDROM_RAW_NONXA:
413 gdrom_get_read_bounds( sector_mode, channels, &start, &size );
414 gdrom_read_block( buf, file_offset+start, size, f );
417 case GDROM_SEMIRAW_MODE2:
418 gdrom_get_read_bounds( sector_mode, channels, &start, &size );
419 if( READ_CD_HEADER(channels) ) {
420 gdrom_build_sector_header( buf, lba, sector_mode );
421 read_len += SECTOR_HEADER_SIZE;
422 size -= SECTOR_HEADER_SIZE;
424 start -= SECTOR_HEADER_SIZE;
426 gdrom_read_block( buf + read_len, file_offset+start, size, f );
429 default: // Data track w/ data only in file
430 if( READ_CD_RAW(channels) ) {
431 gdrom_read_block( buf + gdrom_data_offset[track->mode], file_offset,
432 track->sector_size, f );
433 do_encode_L2( buf, sector_mode, lba );
436 if( READ_CD_HEADER(channels) ) {
437 gdrom_build_sector_header( buf, lba, sector_mode );
438 read_len += SECTOR_HEADER_SIZE;
440 if( READ_CD_SUBHEAD(channels) &&
441 (sector_mode == GDROM_MODE2_FORM1 || sector_mode == GDROM_MODE2_FORM2) ) {
442 if( sector_mode == GDROM_MODE2_FORM1 ) {
443 *((uint32_t *)(buf+read_len)) = 0;
444 *((uint32_t *)(buf+read_len+4)) = 0;
446 *((uint32_t *)(buf+read_len)) = 0x00200000;
447 *((uint32_t *)(buf+read_len+4)) = 0x00200000;
451 if( READ_CD_DATA(channels) ) {
452 gdrom_read_block( buf+read_len, file_offset, track->sector_size, f );
453 read_len += track->sector_size;
.