filename | src/drivers/cdrom/cdrom.c |
changeset | 1177:bd5893522efc |
prev | 1109:700c5ab26a63 |
next | 1296:30ecee61f811 |
author | nkeynes |
date | Tue Feb 28 18:22:52 2012 +1000 (12 years ago) |
permissions | -rw-r--r-- |
last change | Add a GL-only video driver for android usage (since the Java code is responsible for creating the context) |
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/gmem.h>
23 #include <glib/gstrfuncs.h>
24 #include "lxdream.h"
25 #include "drivers/cdrom/cdrom.h"
26 #include "drivers/cdrom/cdimpl.h"
27 #include "drivers/cdrom/isofs.h"
29 extern struct cdrom_disc_factory linux_cdrom_drive_factory;
30 extern struct cdrom_disc_factory nrg_disc_factory;
31 extern struct cdrom_disc_factory cdi_disc_factory;
32 extern struct cdrom_disc_factory gdi_disc_factory;
34 cdrom_disc_factory_t cdrom_disc_factories[] = {
35 #ifdef HAVE_LINUX_CDROM
36 &linux_cdrom_drive_factory,
37 #endif
38 &nrg_disc_factory,
39 &cdi_disc_factory,
40 &gdi_disc_factory,
41 NULL };
43 /********************* Implementation Support functions ************************/
45 cdrom_error_t default_image_read_blocks( sector_source_t source, cdrom_lba_t lba, cdrom_count_t count,
46 unsigned char *buf )
47 {
48 assert( 0 && "read_blocks called on a cdrom disc" );
49 return CDROM_ERROR_BADREAD;
50 }
52 cdrom_error_t default_image_read_sectors( sector_source_t source, cdrom_lba_t lba, cdrom_count_t count,
53 cdrom_read_mode_t mode, unsigned char *buf, size_t *length )
54 {
55 assert( IS_SECTOR_SOURCE_TYPE(source,DISC_SECTOR_SOURCE) );
56 cdrom_disc_t disc = (cdrom_disc_t)source;
57 size_t len = 0, tmplen;
58 cdrom_count_t current = 0;
60 while( current < count ) {
61 cdrom_track_t track = cdrom_disc_get_track_by_lba( disc, lba + current );
62 if( track == NULL )
63 return CDROM_ERROR_BADREAD;
64 uint32_t track_size = cdrom_disc_get_track_size( disc, track );
65 cdrom_lba_t track_offset = lba + current - track->lba;
66 cdrom_count_t sub_count = count - current;
67 if( track_size - track_offset < sub_count )
68 /* Read breaks across track boundaries. This will probably fail (due
69 * to inter-track gaps), but try it just in case
70 */
71 sub_count = track_size - track_offset;
72 cdrom_error_t err = track->source->read_sectors( track->source, track_offset, sub_count, mode, &buf[len], &tmplen );
73 if( err != CDROM_ERROR_OK )
74 return err;
75 len += tmplen;
76 current += sub_count;
77 }
78 if( length != NULL )
79 *length = len;
80 return CDROM_ERROR_OK;
81 }
83 void default_cdrom_disc_destroy( sector_source_t source )
84 {
85 assert( IS_SECTOR_SOURCE_TYPE(source,DISC_SECTOR_SOURCE) );
86 cdrom_disc_t disc = (cdrom_disc_t)source;
87 int i;
89 for( i=0; i<disc->track_count; i++ ) {
90 sector_source_unref( disc->track[i].source );
91 }
92 sector_source_unref( disc->base_source );
93 g_free( (char *)disc->name );
95 default_sector_source_destroy( source );
96 }
98 cdrom_disc_t cdrom_disc_init( cdrom_disc_t disc, const char *filename )
99 {
100 sector_source_init( &disc->source, DISC_SECTOR_SOURCE, SECTOR_UNKNOWN, 0, default_image_read_blocks,
101 default_cdrom_disc_destroy );
102 disc->source.read_sectors = default_image_read_sectors;
103 disc->disc_type = CDROM_DISC_NONE;
104 disc->track_count = disc->session_count = 0;
105 for( int i=0; i<99; i++ ) {
106 disc->track[i].trackno = i+1;
107 }
108 if( filename != NULL )
109 disc->name = g_strdup(filename);
110 return disc;
111 }
113 cdrom_disc_t cdrom_disc_new( const char *name, ERROR *err )
114 {
115 cdrom_disc_t disc = g_malloc0( sizeof(struct cdrom_disc) );
116 if( disc != NULL ) {
117 cdrom_disc_init( disc, name );
118 } else {
119 SET_ERROR(err, LX_ERR_NOMEM, "Unable to allocate memory for cdrom disc");
120 }
121 return disc;
122 }
124 /**
125 * Construct a new image-based disc using the given filename as the base source.
126 * TOC is initialized to the empty values.
127 */
128 static cdrom_disc_t cdrom_disc_image_new( const char *filename, ERROR *err )
129 {
130 cdrom_disc_t disc = cdrom_disc_new( filename, err );
131 if( disc != NULL && filename != NULL ) {
132 disc->base_source = file_sector_source_new_filename( filename, SECTOR_UNKNOWN, 0, FILE_SECTOR_FULL_FILE );
133 if( disc->base_source == NULL ) {
134 SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open cdrom file '%s': %s", filename, strerror(errno) );
135 cdrom_disc_unref(disc);
136 disc = NULL;
137 } else {
138 sector_source_ref(disc->base_source);
139 }
141 }
142 return disc;
143 }
145 cdrom_lba_t cdrom_disc_compute_leadout( cdrom_disc_t disc )
146 {
147 if( disc->track_count == 0 ) {
148 disc->leadout = 0;
149 } else {
150 cdrom_track_t last_track = &disc->track[disc->track_count-1];
151 if( last_track->source != NULL ) {
152 cdrom_lba_t leadout = last_track->lba + last_track->source->size;
153 if( leadout > disc->leadout )
154 disc->leadout = leadout;
155 }
156 }
157 return disc->leadout;
158 }
161 void cdrom_disc_set_default_disc_type( cdrom_disc_t disc )
162 {
163 int type = CDROM_DISC_NONE, i;
164 for( i=0; i<disc->track_count; i++ ) {
165 if( (disc->track[i].flags & TRACK_FLAG_DATA == 0) ) {
166 if( type == CDROM_DISC_NONE )
167 type = CDROM_DISC_AUDIO;
168 } else if( disc->track[i].source != NULL &&
169 (disc->track[i].source->mode == SECTOR_MODE1 ||
170 disc->track[i].source->mode == SECTOR_RAW_NONXA) ) {
171 if( type != CDROM_DISC_XA )
172 type = CDROM_DISC_NONXA;
173 } else {
174 type = CDROM_DISC_XA;
175 break;
176 }
177 }
178 disc->disc_type = type;
179 }
181 void cdrom_disc_clear_toc( cdrom_disc_t disc )
182 {
183 disc->disc_type = CDROM_DISC_NONE;
184 disc->leadout = 0;
185 disc->track_count = 0;
186 disc->session_count = 0;
187 for( unsigned i=0; i< CDROM_MAX_TRACKS; i++ ) {
188 if( disc->track[i].source != NULL ) {
189 sector_source_unref( disc->track[i].source );
190 disc->track[i].source = NULL;
191 }
192 }
193 }
195 gboolean cdrom_disc_read_toc( cdrom_disc_t disc, ERROR *err )
196 {
197 if( disc->read_toc != NULL ) {
198 /* First set the defaults for an empty disc */
199 cdrom_disc_clear_toc(disc);
201 if( disc->read_toc(disc, err ) ) {
202 /* Success - update disc type and leadout if the TOC read didn't set them */
203 if( disc->disc_type == CDROM_DISC_NONE )
204 cdrom_disc_set_default_disc_type(disc);
205 cdrom_disc_compute_leadout(disc);
206 return TRUE;
207 } else {
208 /* Reset to an empty disc in case the reader left things in an
209 * inconsistent state */
210 cdrom_disc_clear_toc(disc);
211 return FALSE;
212 }
213 } else {
214 return TRUE;
215 }
216 }
218 FILE *cdrom_disc_get_base_file( cdrom_disc_t disc )
219 {
220 return file_sector_source_get_file(disc->base_source);
221 }
223 /*************************** Public functions ***************************/
225 cdrom_disc_t cdrom_disc_open( const char *inFilename, ERROR *err )
226 {
227 const gchar *filename = inFilename;
228 const gchar *ext = strrchr(filename, '.');
229 int i;
230 cdrom_disc_factory_t extclz = NULL;
232 /* Ask the drive list if it recognizes the name first */
233 cdrom_drive_t drive = cdrom_drive_find(inFilename);
234 if( drive != NULL ) {
235 return cdrom_drive_open(drive, err);
236 }
238 cdrom_disc_t disc = cdrom_disc_image_new( filename, err );
239 if( disc == NULL )
240 return NULL;
242 /* check file extensions first */
243 FILE *f = file_sector_source_get_file(disc->base_source);
244 if( ext != NULL ) {
245 ext++; /* Skip the '.' */
246 for( i=0; cdrom_disc_factories[i] != NULL; i++ ) {
247 if( cdrom_disc_factories[i]->extension != NULL &&
248 strcasecmp( cdrom_disc_factories[i]->extension, ext ) == 0 ) {
249 extclz = cdrom_disc_factories[i];
250 if( extclz->is_valid_file(f) ) {
251 disc->read_toc = extclz->read_toc;
252 }
253 break;
254 }
255 }
256 }
258 if( disc->read_toc == NULL ) {
259 /* Okay, fall back to magic */
260 for( i=0; cdrom_disc_factories[i] != NULL; i++ ) {
261 if( cdrom_disc_factories[i] != extclz &&
262 cdrom_disc_factories[i]->is_valid_file(f) ) {
263 disc->read_toc = cdrom_disc_factories[i]->read_toc;
264 break;
265 }
266 }
267 }
269 if( disc->read_toc == NULL ) {
270 /* No handler found for file */
271 cdrom_disc_unref( disc );
272 SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' could not be recognized as any known image file or device type", filename );
273 return NULL;
274 } else if( !cdrom_disc_read_toc( disc, err ) ) {
275 cdrom_disc_unref( disc );
276 assert( err == NULL || err->code != LX_ERR_NONE ); /* Read-toc should have set an error code in this case */
277 return NULL;
278 } else {
279 /* All good */
280 return disc;
281 }
282 }
284 /**
285 * Construct a disc around a source track.
286 * @param type Disc type, which must be compatible with the track mode
287 * @param track The source of data for the main track
288 * @param lba The position on disc of the main track. If non-zero,
289 * a filler track is added before it, in 2 separate sessions.
290 */
291 cdrom_disc_t cdrom_disc_new_from_track( cdrom_disc_type_t type, sector_source_t track, cdrom_lba_t lba, ERROR *err )
292 {
293 cdrom_disc_t disc = cdrom_disc_new( NULL, NULL );
294 if( disc != NULL ) {
295 disc->disc_type = type;
296 int trackno = 0;
297 if( lba != 0 ) {
298 cdrom_count_t size = lba - 150;
299 if( lba < 150 )
300 size = lba;
301 disc->track[0].trackno = 1;
302 disc->track[0].sessionno = 1;
303 disc->track[0].lba = 0;
304 disc->track[0].flags = 0;
305 disc->track[0].source = null_sector_source_new( SECTOR_CDDA, size );
306 sector_source_ref( disc->track[0].source );
307 trackno++;
308 }
309 disc->track[trackno].trackno = trackno+1;
310 disc->track[trackno].sessionno = trackno+1;
311 disc->track[trackno].lba = lba;
312 disc->track[trackno].flags = (track->mode == SECTOR_CDDA ? 0 : TRACK_FLAG_DATA);
313 disc->track[trackno].source = track;
314 sector_source_ref(track);
316 disc->track_count = trackno+1;
317 disc->session_count = trackno+1;
318 cdrom_disc_compute_leadout(disc);
319 } else {
320 SET_ERROR(err, LX_ERR_NOMEM, "Unable to allocate memory for cdrom disc");
321 }
322 return disc;
323 }
325 /**
326 * Construct a disc around an IsoImage track (convenience function)
327 */
328 cdrom_disc_t cdrom_disc_new_from_iso_image( cdrom_disc_type_t type, IsoImage *iso, cdrom_lba_t lba,
329 const char *bootstrap, ERROR *err )
330 {
331 sector_mode_t mode = (type == CDROM_DISC_NONXA ? SECTOR_MODE1 : SECTOR_MODE2_FORM1 );
332 sector_source_t source = iso_sector_source_new( iso, mode, lba, bootstrap, err );
333 if( source != NULL ) {
334 cdrom_disc_t disc = cdrom_disc_new_from_track(type, source, lba, err);
335 if( disc == NULL ) {
336 sector_source_unref( source );
337 } else {
338 return disc;
339 }
340 }
341 return NULL;
342 }
344 /**
345 * Get the track information for the given track. If there is no such track,
346 * return NULL;
347 */
348 cdrom_track_t cdrom_disc_get_track( cdrom_disc_t disc, cdrom_trackno_t track )
349 {
350 if( track < 1 || track >= disc->track_count )
351 return NULL;
352 return &disc->track[track-1];
353 }
355 /**
356 * Get the track information for the first track of the given session. If there
357 * is no such session, return NULL;
358 */
359 cdrom_track_t cdrom_disc_get_session( cdrom_disc_t disc, cdrom_sessionno_t session )
360 {
361 for( unsigned i=0; i< disc->track_count; i++ ) {
362 if( disc->track[i].sessionno == session )
363 return &disc->track[i];
364 }
365 return NULL;
366 }
368 cdrom_count_t cdrom_disc_get_track_size( cdrom_disc_t disc, cdrom_track_t track )
369 {
370 if( track->trackno == disc->track_count )
371 return disc->leadout - track->lba;
372 else
373 return disc->track[track->trackno].lba - track->lba;
374 }
376 cdrom_track_t cdrom_disc_get_last_track( cdrom_disc_t disc )
377 {
378 if( disc->track_count == 0 )
379 return NULL;
380 return &disc->track[disc->track_count-1];
381 }
383 cdrom_track_t cdrom_disc_get_last_data_track( cdrom_disc_t disc )
384 {
385 for( unsigned i=disc->track_count; i>0; i-- ) {
386 if( disc->track[i-1].flags & TRACK_FLAG_DATA ) {
387 return &disc->track[i-1];
388 }
389 }
390 return NULL;
391 }
392 cdrom_track_t cdrom_disc_prev_track( cdrom_disc_t disc, cdrom_track_t track )
393 {
394 if( track->trackno <= 1 )
395 return NULL;
396 return cdrom_disc_get_track( disc, track->trackno-1 );
397 }
399 cdrom_track_t cdrom_disc_next_track( cdrom_disc_t disc, cdrom_track_t track )
400 {
401 if( track->trackno >= disc->track_count )
402 return NULL;
403 return cdrom_disc_get_track( disc, track->trackno+1 );
404 }
406 /**
407 * Find the track containing the sector specified by LBA.
408 * Note: this function does not check for media change.
409 * @return The track, or NULL if no track contains the sector.
410 */
411 cdrom_track_t cdrom_disc_get_track_by_lba( cdrom_disc_t disc, cdrom_lba_t lba )
412 {
413 if( disc->track_count == 0 || disc->track[0].lba > lba || lba >= disc->leadout )
414 return NULL; /* LBA outside disc bounds */
416 for( unsigned i=1; i< disc->track_count; i++ ) {
417 if( lba < disc->track[i].lba )
418 return &disc->track[i-1];
419 }
420 return &disc->track[disc->track_count-1];
421 }
423 cdrom_error_t cdrom_disc_read_sectors( cdrom_disc_t disc, cdrom_lba_t lba, cdrom_count_t count,
424 cdrom_read_mode_t mode, unsigned char *buf, size_t *length )
425 {
426 return disc->source.read_sectors( &disc->source, lba, count, mode, buf, length );
427 }
429 /**
430 * Check if the disc contains valid media.
431 * @return CDROM_ERROR_OK if disc is present, otherwise CDROM_ERROR_NODISC
432 */
433 cdrom_error_t cdrom_disc_check_media( cdrom_disc_t disc )
434 {
435 if( disc == NULL )
436 return CDROM_ERROR_NODISC;
437 if( disc->check_media != NULL )
438 disc->check_media(disc);
439 return disc->disc_type == CDROM_DISC_NONE ? CDROM_ERROR_NODISC : CDROM_ERROR_OK;
440 }
442 void cdrom_disc_print_toc( FILE *f, cdrom_disc_t disc )
443 {
444 int i;
445 int session = 0;
447 if( disc == NULL || disc->track_count == 0 ) {
448 fprintf( f, "No disc\n" );
449 return;
450 }
451 for( i=0; i<disc->track_count; i++ ) {
452 cdrom_track_t track = &disc->track[i];
453 if( track->sessionno != session ) {
454 session = disc->track[i].sessionno;
455 fprintf( f, "Session %d:\n", session );
456 }
457 fprintf( f, " %02d. %6d %02x\n", track->trackno, track->lba, track->flags );
458 }
459 }
461 void cdrom_disc_dump_toc( cdrom_disc_t disc )
462 {
463 cdrom_disc_print_toc( stderr, disc );
464 }
.