filename | src/drivers/cdrom/cdrom.c |
changeset | 1296:30ecee61f811 |
prev | 1177:bd5893522efc |
next | 1298:d0eb2307b847 |
author | nkeynes |
date | Sat Jan 26 14:00:48 2013 +1000 (10 years ago) |
permissions | -rw-r--r-- |
last change | Change glib includes to #include <glib.h> rather than the individual headers, as recent glib versions are breaking on this |
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.h>
23 #include "lxdream.h"
24 #include "drivers/cdrom/cdrom.h"
25 #include "drivers/cdrom/cdimpl.h"
26 #include "drivers/cdrom/isofs.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, LX_ERR_NOMEM, "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, LX_ERR_FILE_NOOPEN, "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 if( disc->read_toc != NULL ) {
197 /* First set the defaults for an empty disc */
198 cdrom_disc_clear_toc(disc);
200 if( disc->read_toc(disc, err ) ) {
201 /* Success - update disc type and leadout if the TOC read didn't set them */
202 if( disc->disc_type == CDROM_DISC_NONE )
203 cdrom_disc_set_default_disc_type(disc);
204 cdrom_disc_compute_leadout(disc);
205 return TRUE;
206 } else {
207 /* Reset to an empty disc in case the reader left things in an
208 * inconsistent state */
209 cdrom_disc_clear_toc(disc);
210 return FALSE;
211 }
212 } else {
213 return TRUE;
214 }
215 }
217 FILE *cdrom_disc_get_base_file( cdrom_disc_t disc )
218 {
219 return file_sector_source_get_file(disc->base_source);
220 }
222 /*************************** Public functions ***************************/
224 cdrom_disc_t cdrom_disc_open( const char *inFilename, ERROR *err )
225 {
226 const gchar *filename = inFilename;
227 const gchar *ext = strrchr(filename, '.');
228 int i;
229 cdrom_disc_factory_t extclz = NULL;
231 /* Ask the drive list if it recognizes the name first */
232 cdrom_drive_t drive = cdrom_drive_find(inFilename);
233 if( drive != NULL ) {
234 return cdrom_drive_open(drive, err);
235 }
237 cdrom_disc_t disc = cdrom_disc_image_new( filename, err );
238 if( disc == NULL )
239 return NULL;
241 /* check file extensions first */
242 FILE *f = file_sector_source_get_file(disc->base_source);
243 if( ext != NULL ) {
244 ext++; /* Skip the '.' */
245 for( i=0; cdrom_disc_factories[i] != NULL; i++ ) {
246 if( cdrom_disc_factories[i]->extension != NULL &&
247 strcasecmp( cdrom_disc_factories[i]->extension, ext ) == 0 ) {
248 extclz = cdrom_disc_factories[i];
249 if( extclz->is_valid_file(f) ) {
250 disc->read_toc = extclz->read_toc;
251 }
252 break;
253 }
254 }
255 }
257 if( disc->read_toc == NULL ) {
258 /* Okay, fall back to magic */
259 for( i=0; cdrom_disc_factories[i] != NULL; i++ ) {
260 if( cdrom_disc_factories[i] != extclz &&
261 cdrom_disc_factories[i]->is_valid_file(f) ) {
262 disc->read_toc = cdrom_disc_factories[i]->read_toc;
263 break;
264 }
265 }
266 }
268 if( disc->read_toc == NULL ) {
269 /* No handler found for file */
270 cdrom_disc_unref( disc );
271 SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' could not be recognized as any known image file or device type", filename );
272 return NULL;
273 } else if( !cdrom_disc_read_toc( disc, err ) ) {
274 cdrom_disc_unref( disc );
275 assert( err == NULL || err->code != LX_ERR_NONE ); /* Read-toc should have set an error code in this case */
276 return NULL;
277 } else {
278 /* All good */
279 return disc;
280 }
281 }
283 /**
284 * Construct a disc around a source track.
285 * @param type Disc type, which must be compatible with the track mode
286 * @param track The source of data for the main track
287 * @param lba The position on disc of the main track. If non-zero,
288 * a filler track is added before it, in 2 separate sessions.
289 */
290 cdrom_disc_t cdrom_disc_new_from_track( cdrom_disc_type_t type, sector_source_t track, cdrom_lba_t lba, ERROR *err )
291 {
292 cdrom_disc_t disc = cdrom_disc_new( NULL, NULL );
293 if( disc != NULL ) {
294 disc->disc_type = type;
295 int trackno = 0;
296 if( lba != 0 ) {
297 cdrom_count_t size = lba - 150;
298 if( lba < 150 )
299 size = lba;
300 disc->track[0].trackno = 1;
301 disc->track[0].sessionno = 1;
302 disc->track[0].lba = 0;
303 disc->track[0].flags = 0;
304 disc->track[0].source = null_sector_source_new( SECTOR_CDDA, size );
305 sector_source_ref( disc->track[0].source );
306 trackno++;
307 }
308 disc->track[trackno].trackno = trackno+1;
309 disc->track[trackno].sessionno = trackno+1;
310 disc->track[trackno].lba = lba;
311 disc->track[trackno].flags = (track->mode == SECTOR_CDDA ? 0 : TRACK_FLAG_DATA);
312 disc->track[trackno].source = track;
313 sector_source_ref(track);
315 disc->track_count = trackno+1;
316 disc->session_count = trackno+1;
317 cdrom_disc_compute_leadout(disc);
318 } else {
319 SET_ERROR(err, LX_ERR_NOMEM, "Unable to allocate memory for cdrom disc");
320 }
321 return disc;
322 }
324 /**
325 * Construct a disc around an IsoImage track (convenience function)
326 */
327 cdrom_disc_t cdrom_disc_new_from_iso_image( cdrom_disc_type_t type, IsoImage *iso, cdrom_lba_t lba,
328 const char *bootstrap, ERROR *err )
329 {
330 sector_mode_t mode = (type == CDROM_DISC_NONXA ? SECTOR_MODE1 : SECTOR_MODE2_FORM1 );
331 sector_source_t source = iso_sector_source_new( iso, mode, lba, bootstrap, err );
332 if( source != NULL ) {
333 cdrom_disc_t disc = cdrom_disc_new_from_track(type, source, lba, err);
334 if( disc == NULL ) {
335 sector_source_unref( source );
336 } else {
337 return disc;
338 }
339 }
340 return NULL;
341 }
343 /**
344 * Get the track information for the given track. If there is no such track,
345 * return NULL;
346 */
347 cdrom_track_t cdrom_disc_get_track( cdrom_disc_t disc, cdrom_trackno_t track )
348 {
349 if( track < 1 || track >= disc->track_count )
350 return NULL;
351 return &disc->track[track-1];
352 }
354 /**
355 * Get the track information for the first track of the given session. If there
356 * is no such session, return NULL;
357 */
358 cdrom_track_t cdrom_disc_get_session( cdrom_disc_t disc, cdrom_sessionno_t session )
359 {
360 for( unsigned i=0; i< disc->track_count; i++ ) {
361 if( disc->track[i].sessionno == session )
362 return &disc->track[i];
363 }
364 return NULL;
365 }
367 cdrom_count_t cdrom_disc_get_track_size( cdrom_disc_t disc, cdrom_track_t track )
368 {
369 if( track->trackno == disc->track_count )
370 return disc->leadout - track->lba;
371 else
372 return disc->track[track->trackno].lba - track->lba;
373 }
375 cdrom_track_t cdrom_disc_get_last_track( cdrom_disc_t disc )
376 {
377 if( disc->track_count == 0 )
378 return NULL;
379 return &disc->track[disc->track_count-1];
380 }
382 cdrom_track_t cdrom_disc_get_last_data_track( cdrom_disc_t disc )
383 {
384 for( unsigned i=disc->track_count; i>0; i-- ) {
385 if( disc->track[i-1].flags & TRACK_FLAG_DATA ) {
386 return &disc->track[i-1];
387 }
388 }
389 return NULL;
390 }
391 cdrom_track_t cdrom_disc_prev_track( cdrom_disc_t disc, cdrom_track_t track )
392 {
393 if( track->trackno <= 1 )
394 return NULL;
395 return cdrom_disc_get_track( disc, track->trackno-1 );
396 }
398 cdrom_track_t cdrom_disc_next_track( cdrom_disc_t disc, cdrom_track_t track )
399 {
400 if( track->trackno >= disc->track_count )
401 return NULL;
402 return cdrom_disc_get_track( disc, track->trackno+1 );
403 }
405 /**
406 * Find the track containing the sector specified by LBA.
407 * Note: this function does not check for media change.
408 * @return The track, or NULL if no track contains the sector.
409 */
410 cdrom_track_t cdrom_disc_get_track_by_lba( cdrom_disc_t disc, cdrom_lba_t lba )
411 {
412 if( disc->track_count == 0 || disc->track[0].lba > lba || lba >= disc->leadout )
413 return NULL; /* LBA outside disc bounds */
415 for( unsigned i=1; i< disc->track_count; i++ ) {
416 if( lba < disc->track[i].lba )
417 return &disc->track[i-1];
418 }
419 return &disc->track[disc->track_count-1];
420 }
422 cdrom_error_t cdrom_disc_read_sectors( cdrom_disc_t disc, cdrom_lba_t lba, cdrom_count_t count,
423 cdrom_read_mode_t mode, unsigned char *buf, size_t *length )
424 {
425 return disc->source.read_sectors( &disc->source, lba, count, mode, buf, length );
426 }
428 /**
429 * Check if the disc contains valid media.
430 * @return CDROM_ERROR_OK if disc is present, otherwise CDROM_ERROR_NODISC
431 */
432 cdrom_error_t cdrom_disc_check_media( cdrom_disc_t disc )
433 {
434 if( disc == NULL )
435 return CDROM_ERROR_NODISC;
436 if( disc->check_media != NULL )
437 disc->check_media(disc);
438 return disc->disc_type == CDROM_DISC_NONE ? CDROM_ERROR_NODISC : CDROM_ERROR_OK;
439 }
441 void cdrom_disc_print_toc( FILE *f, cdrom_disc_t disc )
442 {
443 int i;
444 int session = 0;
446 if( disc == NULL || disc->track_count == 0 ) {
447 fprintf( f, "No disc\n" );
448 return;
449 }
450 for( i=0; i<disc->track_count; i++ ) {
451 cdrom_track_t track = &disc->track[i];
452 if( track->sessionno != session ) {
453 session = disc->track[i].sessionno;
454 fprintf( f, "Session %d:\n", session );
455 }
456 fprintf( f, " %02d. %6d %02x\n", track->trackno, track->lba, track->flags );
457 }
458 }
460 void cdrom_disc_dump_toc( cdrom_disc_t disc )
461 {
462 cdrom_disc_print_toc( stderr, disc );
463 }
.