filename | src/drivers/cdrom/cdrom.c |
changeset | 1097:d4807997e450 |
next | 1099:566cdeb157ec |
author | nkeynes |
date | Sun Jan 31 18:35:06 2010 +1000 (14 years ago) |
permissions | -rw-r--r-- |
last change | Refactor CDROM host support - Completely separate GDROM hardware (in gdrom/gdrom.c) from generic CDROM support (now in drivers/cdrom) - Add concept of 'sector sources' that can be mixed and matched to create cdrom discs (makes support of arbitrary disc types much simpler) |
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"
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 *length = len;
78 return CDROM_ERROR_OK;
79 }
81 void default_cdrom_disc_destroy( sector_source_t source )
82 {
83 assert( IS_SECTOR_SOURCE_TYPE(source,DISC_SECTOR_SOURCE) );
84 cdrom_disc_t disc = (cdrom_disc_t)source;
85 int i;
87 for( i=0; i<disc->track_count; i++ ) {
88 sector_source_unref( disc->track[i].source );
89 }
90 sector_source_unref( disc->base_source );
91 g_free( (char *)disc->name );
93 default_sector_source_destroy( source );
94 }
96 cdrom_disc_t cdrom_disc_init( cdrom_disc_t disc, const char *filename )
97 {
98 sector_source_init( &disc->source, DISC_SECTOR_SOURCE, SECTOR_UNKNOWN, 0, default_image_read_blocks,
99 default_cdrom_disc_destroy );
100 disc->source.read_sectors = default_image_read_sectors;
101 disc->disc_type = CDROM_DISC_NONE;
102 disc->track_count = disc->session_count = 0;
103 for( int i=0; i<99; i++ ) {
104 disc->track[i].trackno = i+1;
105 }
106 if( filename != NULL )
107 disc->name = g_strdup(filename);
108 return disc;
109 }
111 cdrom_disc_t cdrom_disc_new( const char *name, ERROR *err )
112 {
113 cdrom_disc_t disc = g_malloc0( sizeof(struct cdrom_disc) );
114 if( disc != NULL ) {
115 cdrom_disc_init( disc, name );
116 } else {
117 SET_ERROR(err, ENOMEM, "Unable to allocate memory for cdrom disc");
118 }
119 return disc;
120 }
122 /**
123 * Construct a new image-based disc using the given filename as the base source.
124 * TOC is initialized to the empty values.
125 */
126 static cdrom_disc_t cdrom_disc_image_new( const char *filename, ERROR *err )
127 {
128 cdrom_disc_t disc = cdrom_disc_new( filename, err );
129 if( disc != NULL && filename != NULL ) {
130 disc->base_source = file_sector_source_new_filename( filename, SECTOR_UNKNOWN, 0, FILE_SECTOR_FULL_FILE );
131 if( disc->base_source == NULL ) {
132 SET_ERROR( err, errno, "Unable to open cdrom file '%s': %s", filename, strerror(errno) );
133 cdrom_disc_unref(disc);
134 disc = NULL;
135 } else {
136 sector_source_ref(disc->base_source);
137 }
139 }
140 return disc;
141 }
143 cdrom_lba_t cdrom_disc_compute_leadout( cdrom_disc_t disc )
144 {
145 if( disc->track_count == 0 ) {
146 disc->leadout = 0;
147 } else {
148 cdrom_track_t last_track = &disc->track[disc->track_count-1];
149 if( last_track->source != NULL ) {
150 cdrom_lba_t leadout = last_track->lba + last_track->source->size;
151 if( leadout > disc->leadout )
152 disc->leadout = leadout;
153 }
154 }
155 return disc->leadout;
156 }
159 void cdrom_disc_set_default_disc_type( cdrom_disc_t disc )
160 {
161 int type = CDROM_DISC_NONE, i;
162 for( i=0; i<disc->track_count; i++ ) {
163 if( (disc->track[i].flags & TRACK_FLAG_DATA == 0) ) {
164 if( type == CDROM_DISC_NONE )
165 type = CDROM_DISC_AUDIO;
166 } else if( disc->track[i].source != NULL &&
167 (disc->track[i].source->mode == SECTOR_MODE1 ||
168 disc->track[i].source->mode == SECTOR_RAW_NONXA) ) {
169 if( type != CDROM_DISC_XA )
170 type = CDROM_DISC_NONXA;
171 } else {
172 type = CDROM_DISC_XA;
173 break;
174 }
175 }
176 disc->disc_type = type;
177 }
179 void cdrom_disc_clear_toc( cdrom_disc_t disc )
180 {
181 disc->disc_type = CDROM_DISC_NONE;
182 disc->leadout = 0;
183 disc->track_count = 0;
184 disc->session_count = 0;
185 for( unsigned i=0; i< CDROM_MAX_TRACKS; i++ ) {
186 if( disc->track[i].source != NULL ) {
187 sector_source_unref( disc->track[i].source );
188 disc->track[i].source = NULL;
189 }
190 }
191 }
193 gboolean cdrom_disc_read_toc( cdrom_disc_t disc, ERROR *err )
194 {
195 /* First set the defaults for an empty disc */
196 cdrom_disc_clear_toc(disc);
198 if( disc->read_toc(disc, err ) ) {
199 /* Success - update disc type and leadout if the TOC read didn't set them */
200 if( disc->disc_type == CDROM_DISC_NONE )
201 cdrom_disc_set_default_disc_type(disc);
202 cdrom_disc_compute_leadout(disc);
203 return TRUE;
204 } else {
205 /* Reset to an empty disc in case the reader left things in an
206 * inconsistent state */
207 cdrom_disc_clear_toc(disc);
208 return FALSE;
209 }
210 }
212 FILE *cdrom_disc_get_base_file( cdrom_disc_t disc )
213 {
214 return file_sector_source_get_file(disc->base_source);
215 }
217 /*************************** Public functions ***************************/
219 cdrom_disc_t cdrom_disc_open( const char *inFilename, ERROR *err )
220 {
221 const gchar *filename = inFilename;
222 const gchar *ext = strrchr(filename, '.');
223 int i;
224 cdrom_disc_factory_t extclz = NULL;
226 /* Ask the drive list if it recognizes the name first */
227 cdrom_drive_t drive = cdrom_drive_find(inFilename);
228 if( drive != NULL ) {
229 return cdrom_drive_open(drive, err);
230 }
232 cdrom_disc_t disc = cdrom_disc_image_new( filename, err );
233 if( disc == NULL )
234 return NULL;
236 /* check file extensions first */
237 FILE *f = file_sector_source_get_file(disc->base_source);
238 if( ext != NULL ) {
239 ext++; /* Skip the '.' */
240 for( i=0; cdrom_disc_factories[i] != NULL; i++ ) {
241 if( cdrom_disc_factories[i]->extension != NULL &&
242 strcasecmp( cdrom_disc_factories[i]->extension, ext ) == 0 ) {
243 extclz = cdrom_disc_factories[i];
244 if( extclz->is_valid_file(f) ) {
245 disc->read_toc = extclz->read_toc;
246 }
247 break;
248 }
249 }
250 }
252 if( disc->read_toc == NULL ) {
253 /* Okay, fall back to magic */
254 for( i=0; cdrom_disc_factories[i] != NULL; i++ ) {
255 if( cdrom_disc_factories[i] != extclz &&
256 cdrom_disc_factories[i]->is_valid_file(f) ) {
257 disc->read_toc = cdrom_disc_factories[i]->read_toc;
258 break;
259 }
260 }
261 }
263 if( disc->read_toc != NULL && cdrom_disc_read_toc( disc, err ) ) {
264 /* All good */
265 return disc;
266 } else {
267 /* No handler found for file */
268 cdrom_disc_unref( disc );
269 SET_ERROR( err, EINVAL, "File '%s' could not be recognized as any known image file or device type" );
270 return NULL;
271 }
272 }
274 /**
275 * Get the track information for the given track. If there is no such track,
276 * return NULL;
277 */
278 cdrom_track_t cdrom_disc_get_track( cdrom_disc_t disc, cdrom_trackno_t track )
279 {
280 if( track < 1 || track >= disc->track_count )
281 return NULL;
282 return &disc->track[track-1];
283 }
285 /**
286 * Get the track information for the first track of the given session. If there
287 * is no such session, return NULL;
288 */
289 cdrom_track_t cdrom_disc_get_session( cdrom_disc_t disc, cdrom_sessionno_t session )
290 {
291 for( unsigned i=0; i< disc->track_count; i++ ) {
292 if( disc->track[i].sessionno == session )
293 return &disc->track[i];
294 }
295 return NULL;
296 }
298 cdrom_count_t cdrom_disc_get_track_size( cdrom_disc_t disc, cdrom_track_t track )
299 {
300 if( track->trackno == disc->track_count )
301 return disc->leadout - track->lba;
302 else
303 return disc->track[track->trackno].lba - track->lba;
304 }
306 cdrom_track_t cdrom_disc_get_last_track( cdrom_disc_t disc )
307 {
308 if( disc->track_count == 0 )
309 return NULL;
310 return &disc->track[disc->track_count-1];
311 }
313 cdrom_track_t cdrom_disc_prev_track( cdrom_disc_t disc, cdrom_track_t track )
314 {
315 if( track->trackno <= 1 )
316 return NULL;
317 return cdrom_disc_get_track( disc, track->trackno-1 );
318 }
320 cdrom_track_t cdrom_disc_next_track( cdrom_disc_t disc, cdrom_track_t track )
321 {
322 if( track->trackno >= disc->track_count )
323 return NULL;
324 return cdrom_disc_get_track( disc, track->trackno+1 );
325 }
327 /**
328 * Find the track containing the sector specified by LBA.
329 * Note: this function does not check for media change.
330 * @return The track, or NULL if no track contains the sector.
331 */
332 cdrom_track_t cdrom_disc_get_track_by_lba( cdrom_disc_t disc, cdrom_lba_t lba )
333 {
334 if( disc->track_count == 0 || disc->track[0].lba > lba || lba >= disc->leadout )
335 return NULL; /* LBA outside disc bounds */
337 for( unsigned i=1; i< disc->track_count; i++ ) {
338 if( lba < disc->track[i].lba )
339 return &disc->track[i-1];
340 }
341 return &disc->track[disc->track_count-1];
342 }
344 cdrom_error_t cdrom_disc_read_sectors( cdrom_disc_t disc, cdrom_lba_t lba, cdrom_count_t count,
345 cdrom_read_mode_t mode, unsigned char *buf, size_t *length )
346 {
347 return disc->source.read_sectors( &disc->source, lba, count, mode, buf, length );
348 }
350 /**
351 * Check if the disc contains valid media.
352 * @return CDROM_ERROR_OK if disc is present, otherwise CDROM_ERROR_NODISC
353 */
354 cdrom_error_t cdrom_disc_check_media( cdrom_disc_t disc )
355 {
356 if( disc == NULL )
357 return CDROM_ERROR_NODISC;
358 if( disc->check_media != NULL )
359 disc->check_media(disc);
360 return disc->disc_type == CDROM_DISC_NONE ? CDROM_ERROR_NODISC : CDROM_ERROR_OK;
361 }
363 void cdrom_disc_print_toc( FILE *f, cdrom_disc_t disc )
364 {
365 int i;
366 int session = 0;
368 if( disc == NULL || disc->track_count == 0 ) {
369 fprintf( f, "No disc\n" );
370 return;
371 }
372 for( i=0; i<disc->track_count; i++ ) {
373 cdrom_track_t track = &disc->track[i];
374 if( track->sessionno != session ) {
375 session = disc->track[i].sessionno;
376 fprintf( f, "Session %d:\n", session );
377 }
378 fprintf( f, " %02d. %6d %02x\n", track->trackno, track->lba, track->flags );
379 }
380 }
382 void cdrom_disc_dump_toc( cdrom_disc_t disc )
383 {
384 cdrom_disc_print_toc( stderr, disc );
385 }
.