filename | src/gdrom/gdimage.c |
changeset | 1023:264e2fd90be8 |
prev | 840:c6a778c228a6 |
next | 1030:864417a57662 |
author | nkeynes |
date | Mon Jun 08 04:12:21 2009 +0000 (14 years ago) |
permissions | -rw-r--r-- |
last change | General cleanup of the GD-rom subsystem - merge gdrom_image_t and gdrom_disc_t - Abstract MMC devices using a lower-level scsi transport - OSX: only look at the whole disc device, and ignore partitions |
view | annotate | diff | log | raw |
1 /**
2 * $Id$
3 *
4 * GD-Rom image-file common functions.
5 *
6 * Copyright (c) 2005 Nathan Keynes.
7 *
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.
12 *
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.
17 */
19 #include <assert.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <netinet/in.h>
28 #include "gdrom/gddriver.h"
29 #include "gdrom/packet.h"
30 #include "ecc.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,
45 &nrg_image_class,
46 &cdi_image_class,
47 &gdi_image_class,
48 NULL };
50 struct cdrom_sector_header {
51 uint8_t sync[12];
52 uint8_t msf[3];
53 uint8_t mode;
54 uint8_t subhead[8]; // Mode-2 XA sectors only
55 };
57 gdrom_disc_t gdrom_disc_new( const gchar *filename, FILE *f )
58 {
59 gdrom_disc_t disc = (gdrom_disc_t)g_malloc0(sizeof(struct gdrom_disc));
60 if( disc == NULL ) {
61 return NULL;
62 }
63 disc->disc_type = IDE_DISC_NONE;
64 disc->file = f;
65 if( filename == NULL ) {
66 disc->name = NULL;
67 } else {
68 disc->name = g_strdup(filename);
69 }
71 disc->check_status = gdrom_null_check_status;
72 disc->destroy = gdrom_disc_destroy;
73 return disc;
74 }
76 void gdrom_disc_destroy( gdrom_disc_t disc, gboolean close_fh )
77 {
78 int i;
79 FILE *lastfile = NULL;
80 if( disc->file != NULL ) {
81 if( close_fh ) {
82 fclose(disc->file);
83 }
84 disc->file = NULL;
85 }
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 */
90 fclose(lastfile);
91 disc->track[i].file = NULL;
92 }
93 }
94 if( disc->name != NULL ) {
95 g_free( (gpointer)disc->name );
96 disc->name = NULL;
97 }
98 if( disc->display_name != NULL ) {
99 g_free( (gpointer)disc->name );
100 disc->display_name = NULL;
101 }
102 free( disc );
103 }
105 /**
106 * Construct a new gdrom_disc_t and initalize the vtable to the gdrom image
107 * default functions.
108 */
109 gdrom_disc_t gdrom_image_new( const gchar *filename, FILE *f )
110 {
111 gdrom_disc_t disc = gdrom_disc_new( filename, f );
112 if( disc != NULL ) {
113 disc->read_sector = gdrom_image_read_sector;
114 disc->play_audio = NULL; /* not supported yet */
115 disc->run_time_slice = NULL; /* not needed */
116 }
117 }
120 gdrom_disc_t gdrom_image_open( const gchar *inFilename )
121 {
122 const gchar *filename = inFilename;
123 const gchar *ext = strrchr(filename, '.');
124 gdrom_disc_t disc = NULL;
125 int fd;
126 FILE *f;
127 int i;
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 ) {
140 filename = path;
141 } else if( strcasecmp( method, "dvd" ) == 0 ||
142 strcasecmp( method, "cd" ) == 0 ||
143 strcasecmp( method, "cdrom" ) ) {
144 return cdrom_open_device( method, path );
145 } else {
146 ERROR( "Unrecognized URL method '%s' in filename '%s'", method, filename );
147 return NULL;
148 }
149 }
151 fd = open( filename, O_RDONLY | O_NONBLOCK );
152 if( fd == -1 ) {
153 return NULL;
154 }
156 f = fdopen(fd, "ro");
159 /* try extensions */
160 if( ext != NULL ) {
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);
168 if( disc != NULL )
169 return disc;
170 }
171 break;
172 }
173 }
174 }
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) ) {
181 recognized = TRUE;
182 disc = gdrom_image_classes[i]->open_image_file(filename, f);
183 if( disc != NULL )
184 return disc;
185 }
186 }
188 fclose(f);
189 return NULL;
190 }
192 /**
193 * Read a block from an image file, handling negative file offsets
194 * with 0-fill.
195 */
196 static gboolean gdrom_read_block( unsigned char *buf, int file_offset, int length, FILE *f )
197 {
198 if( file_offset < 0 ) {
199 int size = -file_offset;
200 if( size >= length ) {
201 memset( buf, 0, length );
202 return TRUE;
203 } else {
204 memset( buf, 0, size );
205 file_offset = 0;
206 length -= size;
207 }
208 }
209 fseek( f, file_offset, SEEK_SET );
210 return fread( buf, length, 1, f ) == 1;
211 }
213 static void gdrom_build_sector_header( unsigned char *buf, uint32_t lba,
214 gdrom_track_mode_t sector_mode )
215 {
216 memcpy( buf, gdrom_default_sync, 12 );
217 cd_build_address( buf, sector_mode, lba );
218 }
220 /**
221 * Return TRUE if the given read mode + track modes are compatible,
222 * otherwise FALSE.
223 * @param track_mode one of the GDROM_MODE* constants
224 * @param read_mode the READ_CD_MODE from the read request
225 */
226 static gboolean gdrom_is_compatible_read_mode( int track_mode, int read_mode )
227 {
228 switch( read_mode ) {
229 case READ_CD_MODE_ANY:
230 return TRUE;
231 case READ_CD_MODE_CDDA:
232 return track_mode == GDROM_CDDA;
233 case READ_CD_MODE_1:
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;
239 case READ_CD_MODE_2:
240 return track_mode == GDROM_MODE2_FORMLESS;
241 default:
242 return FALSE;
243 }
244 }
246 /**
247 * Determine the start position in a raw sector, and the amount of data to read
248 * in bytes, for a given combination of sector mode and read mode.
249 */
250 static void gdrom_get_read_bounds( int sector_mode, int read_mode, int *start, int *size )
251 {
252 if( READ_CD_RAW(read_mode) ) {
253 // whole sector
254 *start = 0;
255 *size = 2352;
256 } else {
257 *size = 0;
258 if( READ_CD_DATA(read_mode) ) {
259 *start = gdrom_data_offset[sector_mode];
260 *size = gdrom_sector_size[sector_mode];
261 }
263 if( READ_CD_SUBHEAD(read_mode) &&
264 (sector_mode == GDROM_MODE2_FORM1 || sector_mode == GDROM_MODE2_FORM2) ) {
265 *start = SECTOR_HEADER_SIZE;
266 *size += SECTOR_SUBHEADER_SIZE;
267 }
269 if( READ_CD_HEADER(read_mode) ) {
270 *size += SECTOR_HEADER_SIZE;
271 *start = 0;
272 }
274 }
275 }
277 void gdrom_extract_raw_data_sector( char *sector_data, int channels, unsigned char *buf, uint32_t *length )
278 {
279 int sector_mode;
280 int start, size;
281 struct cdrom_sector_header *secthead = (struct cdrom_sector_header *)sector_data;
282 if( secthead->mode == 1 ) {
283 sector_mode = GDROM_MODE1;
284 } else {
285 sector_mode = ((secthead->subhead[2] & 0x20) == 0 ) ? GDROM_MODE2_FORM1 : GDROM_MODE2_FORM2;
286 }
287 gdrom_get_read_bounds( sector_mode, channels, &start, &size );
289 memcpy( buf, sector_data+start, size );
290 *length = size;
291 }
293 /**
294 * Default check media status that does nothing and always returns
295 * false (unchanged).
296 */
297 static gboolean gdrom_null_check_status( gdrom_disc_t disc )
298 {
299 return FALSE;
300 }
302 /**
303 * Read a single sector from a disc image. If you thought this would be simple,
304 * I have just one thing to say to you: Bwahahahahahahahah.
305 *
306 * Once we've decided that there's a real sector at the requested lba, there's
307 * really two things we need to care about:
308 * 1. Is the sector mode compatible with the requested read mode
309 * 2. Which parts of the sector do we need to return?
310 * (header/subhead/data/raw sector)
311 *
312 * Also note that the disc image may supply us with just the data (most common
313 * case), or may have the full raw sector. In the former case we may need to
314 * generate the missing data on the fly, for which we use libedc to compute the
315 * data correction codes.
316 */
317 static gdrom_error_t gdrom_image_read_sector( gdrom_disc_t disc, uint32_t lba,
318 int mode, unsigned char *buf, uint32_t *length )
319 {
320 struct cdrom_sector_header secthead;
321 int file_offset, read_len, track_no;
323 FILE *f;
325 track_no = gdrom_disc_get_track_by_lba( disc, lba );
326 if( track_no == -1 ) {
327 return PKT_ERR_BADREAD;
328 }
329 struct gdrom_track *track = &disc->track[track_no-1];
330 file_offset = track->offset + track->sector_size * (lba - track->lba);
331 read_len = track->sector_size;
332 if( track->file != NULL ) {
333 f = track->file;
334 } else {
335 f = disc->file;
336 }
338 /* First figure out what the real sector mode is for raw/semiraw sectors */
339 int sector_mode;
340 switch( track->mode ) {
341 case GDROM_RAW_NONXA:
342 gdrom_read_block( (unsigned char *)(§head), file_offset, sizeof(secthead), f );
343 sector_mode = (secthead.mode == 1) ? GDROM_MODE1 : GDROM_MODE2_FORMLESS;
344 break;
345 case GDROM_RAW_XA:
346 gdrom_read_block( (unsigned char *)(§head), file_offset, sizeof(secthead), f );
347 if( secthead.mode == 1 ) {
348 sector_mode = GDROM_MODE1;
349 } else {
350 sector_mode = ((secthead.subhead[2] & 0x20) == 0 ) ? GDROM_MODE2_FORM1 : GDROM_MODE2_FORM2;
351 }
352 break;
353 case GDROM_SEMIRAW_MODE2:
354 gdrom_read_block( secthead.subhead, file_offset, 8, f );
355 sector_mode = ((secthead.subhead[2] & 0x20) == 0 ) ? GDROM_MODE2_FORM1 : GDROM_MODE2_FORM2;
356 break;
357 default:
358 /* In the other cases, the track mode completely defines the sector mode */
359 sector_mode = track->mode;
360 break;
361 }
363 if( !gdrom_is_compatible_read_mode(sector_mode, READ_CD_MODE(mode)) ) {
364 return PKT_ERR_BADREADMODE;
365 }
367 /* Ok, we've got a valid sector, check what parts of the sector we need to
368 * return - header | subhead | data | everything
369 */
370 int channels = READ_CD_CHANNELS(mode);
372 if( channels == 0 ) {
373 // legal, if weird
374 *length = 0;
375 return PKT_ERR_OK;
376 } else if( channels == 0xA0 &&
377 (sector_mode == GDROM_MODE2_FORM1 || sector_mode == GDROM_MODE2_FORM2 )) {
378 // caller requested a non-contiguous region
379 return PKT_ERR_BADFIELD;
380 } else if( READ_CD_RAW(channels) ) {
381 channels = 0xF0; // implies everything
382 }
384 read_len = 0;
385 int start, size;
386 switch( track->mode ) {
387 case GDROM_CDDA:
388 // audio is nice and simple (assume perfect reads for now)
389 *length = 2352;
390 gdrom_read_block( buf, file_offset, track->sector_size, f );
391 return PKT_ERR_OK;
392 case GDROM_RAW_XA:
393 case GDROM_RAW_NONXA:
394 gdrom_get_read_bounds( sector_mode, channels, &start, &size );
395 gdrom_read_block( buf, file_offset+start, size, f );
396 read_len = size;
397 break;
398 case GDROM_SEMIRAW_MODE2:
399 gdrom_get_read_bounds( sector_mode, channels, &start, &size );
400 if( READ_CD_HEADER(channels) ) {
401 gdrom_build_sector_header( buf, lba, sector_mode );
402 read_len += SECTOR_HEADER_SIZE;
403 size -= SECTOR_HEADER_SIZE;
404 } else {
405 start -= SECTOR_HEADER_SIZE;
406 }
407 gdrom_read_block( buf + read_len, file_offset+start, size, f );
408 read_len += size;
409 break;
410 default: // Data track w/ data only in file
411 if( READ_CD_RAW(channels) ) {
412 gdrom_read_block( buf + gdrom_data_offset[track->mode], file_offset,
413 track->sector_size, f );
414 do_encode_L2( buf, sector_mode, lba );
415 read_len = 2352;
416 } else {
417 if( READ_CD_HEADER(channels) ) {
418 gdrom_build_sector_header( buf, lba, sector_mode );
419 read_len += SECTOR_HEADER_SIZE;
420 }
421 if( READ_CD_SUBHEAD(channels) &&
422 (sector_mode == GDROM_MODE2_FORM1 || sector_mode == GDROM_MODE2_FORM2) ) {
423 if( sector_mode == GDROM_MODE2_FORM1 ) {
424 *((uint32_t *)(buf+read_len)) = 0;
425 *((uint32_t *)(buf+read_len+4)) = 0;
426 } else {
427 *((uint32_t *)(buf+read_len)) = 0x00200000;
428 *((uint32_t *)(buf+read_len+4)) = 0x00200000;
429 }
430 read_len += 8;
431 }
432 if( READ_CD_DATA(channels) ) {
433 gdrom_read_block( buf+read_len, file_offset, track->sector_size, f );
434 read_len += track->sector_size;
435 }
436 }
437 }
438 *length = read_len;
439 return PKT_ERR_OK;
440 }
.