filename | src/gdrom/gdimage.c |
changeset | 1030:864417a57662 |
prev | 1023:264e2fd90be8 |
next | 1071:182cfe43c09e |
author | nkeynes |
date | Sun Jun 28 12:08:16 2009 +0000 (14 years ago) |
permissions | -rw-r--r-- |
last change | Fix creating first VMU (failed to add to list) Fix device-changed causing second device-changed to fire |
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 void gdrom_set_disc_type( gdrom_disc_t disc )
247 {
248 int type = IDE_DISC_NONE, i;
249 for( i=0; i<disc->track_count; i++ ) {
250 if( disc->track[i].mode == GDROM_CDDA ) {
251 if( type == IDE_DISC_NONE )
252 type = IDE_DISC_AUDIO;
253 } else if( disc->track[i].mode == GDROM_MODE1 || disc->track[i].mode == GDROM_RAW_NONXA ) {
254 if( type != IDE_DISC_CDROMXA )
255 type = IDE_DISC_CDROM;
256 } else {
257 type = IDE_DISC_CDROMXA;
258 break;
259 }
260 }
261 disc->disc_type = type;
262 }
264 /**
265 * Determine the start position in a raw sector, and the amount of data to read
266 * in bytes, for a given combination of sector mode and read mode.
267 */
268 static void gdrom_get_read_bounds( int sector_mode, int read_mode, int *start, int *size )
269 {
270 if( READ_CD_RAW(read_mode) ) {
271 // whole sector
272 *start = 0;
273 *size = 2352;
274 } else {
275 *size = 0;
276 if( READ_CD_DATA(read_mode) ) {
277 *start = gdrom_data_offset[sector_mode];
278 *size = gdrom_sector_size[sector_mode];
279 }
281 if( READ_CD_SUBHEAD(read_mode) &&
282 (sector_mode == GDROM_MODE2_FORM1 || sector_mode == GDROM_MODE2_FORM2) ) {
283 *start = SECTOR_HEADER_SIZE;
284 *size += SECTOR_SUBHEADER_SIZE;
285 }
287 if( READ_CD_HEADER(read_mode) ) {
288 *size += SECTOR_HEADER_SIZE;
289 *start = 0;
290 }
292 }
293 }
295 void gdrom_extract_raw_data_sector( char *sector_data, int channels, unsigned char *buf, uint32_t *length )
296 {
297 int sector_mode;
298 int start, size;
299 struct cdrom_sector_header *secthead = (struct cdrom_sector_header *)sector_data;
300 if( secthead->mode == 1 ) {
301 sector_mode = GDROM_MODE1;
302 } else {
303 sector_mode = ((secthead->subhead[2] & 0x20) == 0 ) ? GDROM_MODE2_FORM1 : GDROM_MODE2_FORM2;
304 }
305 gdrom_get_read_bounds( sector_mode, channels, &start, &size );
307 memcpy( buf, sector_data+start, size );
308 *length = size;
309 }
311 /**
312 * Default check media status that does nothing and always returns
313 * false (unchanged).
314 */
315 static gboolean gdrom_null_check_status( gdrom_disc_t disc )
316 {
317 return FALSE;
318 }
320 /**
321 * Read a single sector from a disc image. If you thought this would be simple,
322 * I have just one thing to say to you: Bwahahahahahahahah.
323 *
324 * Once we've decided that there's a real sector at the requested lba, there's
325 * really two things we need to care about:
326 * 1. Is the sector mode compatible with the requested read mode
327 * 2. Which parts of the sector do we need to return?
328 * (header/subhead/data/raw sector)
329 *
330 * Also note that the disc image may supply us with just the data (most common
331 * case), or may have the full raw sector. In the former case we may need to
332 * generate the missing data on the fly, for which we use libedc to compute the
333 * data correction codes.
334 */
335 static gdrom_error_t gdrom_image_read_sector( gdrom_disc_t disc, uint32_t lba,
336 int mode, unsigned char *buf, uint32_t *length )
337 {
338 struct cdrom_sector_header secthead;
339 int file_offset, read_len, track_no;
341 FILE *f;
343 track_no = gdrom_disc_get_track_by_lba( disc, lba );
344 if( track_no == -1 ) {
345 return PKT_ERR_BADREAD;
346 }
347 struct gdrom_track *track = &disc->track[track_no-1];
348 file_offset = track->offset + track->sector_size * (lba - track->lba);
349 read_len = track->sector_size;
350 if( track->file != NULL ) {
351 f = track->file;
352 } else {
353 f = disc->file;
354 }
356 /* First figure out what the real sector mode is for raw/semiraw sectors */
357 int sector_mode;
358 switch( track->mode ) {
359 case GDROM_RAW_NONXA:
360 gdrom_read_block( (unsigned char *)(§head), file_offset, sizeof(secthead), f );
361 sector_mode = (secthead.mode == 1) ? GDROM_MODE1 : GDROM_MODE2_FORMLESS;
362 break;
363 case GDROM_RAW_XA:
364 gdrom_read_block( (unsigned char *)(§head), file_offset, sizeof(secthead), f );
365 if( secthead.mode == 1 ) {
366 sector_mode = GDROM_MODE1;
367 } else {
368 sector_mode = ((secthead.subhead[2] & 0x20) == 0 ) ? GDROM_MODE2_FORM1 : GDROM_MODE2_FORM2;
369 }
370 break;
371 case GDROM_SEMIRAW_MODE2:
372 gdrom_read_block( secthead.subhead, file_offset, 8, f );
373 sector_mode = ((secthead.subhead[2] & 0x20) == 0 ) ? GDROM_MODE2_FORM1 : GDROM_MODE2_FORM2;
374 break;
375 default:
376 /* In the other cases, the track mode completely defines the sector mode */
377 sector_mode = track->mode;
378 break;
379 }
381 if( !gdrom_is_compatible_read_mode(sector_mode, READ_CD_MODE(mode)) ) {
382 return PKT_ERR_BADREADMODE;
383 }
385 /* Ok, we've got a valid sector, check what parts of the sector we need to
386 * return - header | subhead | data | everything
387 */
388 int channels = READ_CD_CHANNELS(mode);
390 if( channels == 0 ) {
391 // legal, if weird
392 *length = 0;
393 return PKT_ERR_OK;
394 } else if( channels == 0xA0 &&
395 (sector_mode == GDROM_MODE2_FORM1 || sector_mode == GDROM_MODE2_FORM2 )) {
396 // caller requested a non-contiguous region
397 return PKT_ERR_BADFIELD;
398 } else if( READ_CD_RAW(channels) ) {
399 channels = 0xF0; // implies everything
400 }
402 read_len = 0;
403 int start, size;
404 switch( track->mode ) {
405 case GDROM_CDDA:
406 // audio is nice and simple (assume perfect reads for now)
407 *length = 2352;
408 gdrom_read_block( buf, file_offset, track->sector_size, f );
409 return PKT_ERR_OK;
410 case GDROM_RAW_XA:
411 case GDROM_RAW_NONXA:
412 gdrom_get_read_bounds( sector_mode, channels, &start, &size );
413 gdrom_read_block( buf, file_offset+start, size, f );
414 read_len = size;
415 break;
416 case GDROM_SEMIRAW_MODE2:
417 gdrom_get_read_bounds( sector_mode, channels, &start, &size );
418 if( READ_CD_HEADER(channels) ) {
419 gdrom_build_sector_header( buf, lba, sector_mode );
420 read_len += SECTOR_HEADER_SIZE;
421 size -= SECTOR_HEADER_SIZE;
422 } else {
423 start -= SECTOR_HEADER_SIZE;
424 }
425 gdrom_read_block( buf + read_len, file_offset+start, size, f );
426 read_len += size;
427 break;
428 default: // Data track w/ data only in file
429 if( READ_CD_RAW(channels) ) {
430 gdrom_read_block( buf + gdrom_data_offset[track->mode], file_offset,
431 track->sector_size, f );
432 do_encode_L2( buf, sector_mode, lba );
433 read_len = 2352;
434 } else {
435 if( READ_CD_HEADER(channels) ) {
436 gdrom_build_sector_header( buf, lba, sector_mode );
437 read_len += SECTOR_HEADER_SIZE;
438 }
439 if( READ_CD_SUBHEAD(channels) &&
440 (sector_mode == GDROM_MODE2_FORM1 || sector_mode == GDROM_MODE2_FORM2) ) {
441 if( sector_mode == GDROM_MODE2_FORM1 ) {
442 *((uint32_t *)(buf+read_len)) = 0;
443 *((uint32_t *)(buf+read_len+4)) = 0;
444 } else {
445 *((uint32_t *)(buf+read_len)) = 0x00200000;
446 *((uint32_t *)(buf+read_len+4)) = 0x00200000;
447 }
448 read_len += 8;
449 }
450 if( READ_CD_DATA(channels) ) {
451 gdrom_read_block( buf+read_len, file_offset, track->sector_size, f );
452 read_len += track->sector_size;
453 }
454 }
455 }
456 *length = read_len;
457 return PKT_ERR_OK;
458 }
.