Mercurial > mplayer.hg
comparison libmpdvdkit2/dvd_udf.c @ 7029:9db58ffbd73c
importing libdvdread 0.9.3 files
author | arpi |
---|---|
date | Fri, 16 Aug 2002 22:37:48 +0000 |
parents | |
children | 596919e4f601 |
comparison
equal
deleted
inserted
replaced
7028:9d4273713562 | 7029:9db58ffbd73c |
---|---|
1 /* | |
2 * This code is based on dvdudf by: | |
3 * Christian Wolff <scarabaeus@convergence.de>. | |
4 * | |
5 * Modifications by: | |
6 * Billy Biggs <vektor@dumbterm.net>. | |
7 * | |
8 * dvdudf: parse and read the UDF volume information of a DVD Video | |
9 * Copyright (C) 1999 Christian Wolff for convergence integrated media | |
10 * GmbH The author can be reached at scarabaeus@convergence.de, the | |
11 * project's page is at http://linuxtv.org/dvd/ | |
12 * | |
13 * This program is free software; you can redistribute it and/or modify | |
14 * it under the terms of the GNU General Public License as published by | |
15 * the Free Software Foundation; either version 2 of the License, or (at | |
16 * your option) any later version. | |
17 * | |
18 * This program is distributed in the hope that it will be useful, but | |
19 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
21 * General Public License for more details. | |
22 * | |
23 * You should have received a copy of the GNU General Public License | |
24 * along with this program; if not, write to the Free Software | |
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
26 * 02111-1307, USA. Or, point your browser to | |
27 * http://www.gnu.org/copyleft/gpl.html | |
28 */ | |
29 | |
30 #include <stdio.h> | |
31 #include <stdlib.h> | |
32 #include <string.h> | |
33 #include <assert.h> | |
34 #include <sys/ioctl.h> | |
35 #include <sys/types.h> | |
36 #include <sys/stat.h> | |
37 #include <unistd.h> | |
38 #include <inttypes.h> | |
39 | |
40 #include "dvd_reader.h" | |
41 #include "dvd_udf.h" | |
42 | |
43 /* Private but located in/shared with dvd_reader.c */ | |
44 extern int DVDReadBlocksUDFRaw( dvd_reader_t *device, uint32_t lb_number, | |
45 size_t block_count, unsigned char *data, | |
46 int encrypted ); | |
47 | |
48 /* It's required to either fail or deliver all the blocks asked for. */ | |
49 static int DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number, | |
50 size_t block_count, unsigned char *data, | |
51 int encrypted ) | |
52 { | |
53 int ret; | |
54 size_t count = block_count; | |
55 | |
56 while(count > 0) { | |
57 | |
58 ret = DVDReadBlocksUDFRaw(device, lb_number, count, data, encrypted); | |
59 | |
60 if(ret <= 0) { | |
61 /* One of the reads failed or nothing more to read, too bad. | |
62 * We won't even bother returning the reads that went ok. */ | |
63 return ret; | |
64 } | |
65 | |
66 count -= (size_t)ret; | |
67 lb_number += (uint32_t)ret; | |
68 } | |
69 | |
70 return block_count; | |
71 } | |
72 | |
73 | |
74 #ifndef NULL | |
75 #define NULL ((void *)0) | |
76 #endif | |
77 | |
78 struct Partition { | |
79 int valid; | |
80 char VolumeDesc[128]; | |
81 uint16_t Flags; | |
82 uint16_t Number; | |
83 char Contents[32]; | |
84 uint32_t AccessType; | |
85 uint32_t Start; | |
86 uint32_t Length; | |
87 }; | |
88 | |
89 struct AD { | |
90 uint32_t Location; | |
91 uint32_t Length; | |
92 uint8_t Flags; | |
93 uint16_t Partition; | |
94 }; | |
95 | |
96 /* For direct data access, LSB first */ | |
97 #define GETN1(p) ((uint8_t)data[p]) | |
98 #define GETN2(p) ((uint16_t)data[p] | ((uint16_t)data[(p) + 1] << 8)) | |
99 #define GETN3(p) ((uint32_t)data[p] | ((uint32_t)data[(p) + 1] << 8) \ | |
100 | ((uint32_t)data[(p) + 2] << 16)) | |
101 #define GETN4(p) ((uint32_t)data[p] \ | |
102 | ((uint32_t)data[(p) + 1] << 8) \ | |
103 | ((uint32_t)data[(p) + 2] << 16) \ | |
104 | ((uint32_t)data[(p) + 3] << 24)) | |
105 /* This is wrong with regard to endianess */ | |
106 #define GETN(p, n, target) memcpy(target, &data[p], n) | |
107 | |
108 static int Unicodedecode( uint8_t *data, int len, char *target ) | |
109 { | |
110 int p = 1, i = 0; | |
111 | |
112 if( ( data[ 0 ] == 8 ) || ( data[ 0 ] == 16 ) ) do { | |
113 if( data[ 0 ] == 16 ) p++; /* Ignore MSB of unicode16 */ | |
114 if( p < len ) { | |
115 target[ i++ ] = data[ p++ ]; | |
116 } | |
117 } while( p < len ); | |
118 | |
119 target[ i ] = '\0'; | |
120 return 0; | |
121 } | |
122 | |
123 static int UDFDescriptor( uint8_t *data, uint16_t *TagID ) | |
124 { | |
125 *TagID = GETN2(0); | |
126 // TODO: check CRC 'n stuff | |
127 return 0; | |
128 } | |
129 | |
130 static int UDFExtentAD( uint8_t *data, uint32_t *Length, uint32_t *Location ) | |
131 { | |
132 *Length = GETN4(0); | |
133 *Location = GETN4(4); | |
134 return 0; | |
135 } | |
136 | |
137 static int UDFShortAD( uint8_t *data, struct AD *ad, | |
138 struct Partition *partition ) | |
139 { | |
140 ad->Length = GETN4(0); | |
141 ad->Flags = ad->Length >> 30; | |
142 ad->Length &= 0x3FFFFFFF; | |
143 ad->Location = GETN4(4); | |
144 ad->Partition = partition->Number; // use number of current partition | |
145 return 0; | |
146 } | |
147 | |
148 static int UDFLongAD( uint8_t *data, struct AD *ad ) | |
149 { | |
150 ad->Length = GETN4(0); | |
151 ad->Flags = ad->Length >> 30; | |
152 ad->Length &= 0x3FFFFFFF; | |
153 ad->Location = GETN4(4); | |
154 ad->Partition = GETN2(8); | |
155 //GETN(10, 6, Use); | |
156 return 0; | |
157 } | |
158 | |
159 static int UDFExtAD( uint8_t *data, struct AD *ad ) | |
160 { | |
161 ad->Length = GETN4(0); | |
162 ad->Flags = ad->Length >> 30; | |
163 ad->Length &= 0x3FFFFFFF; | |
164 ad->Location = GETN4(12); | |
165 ad->Partition = GETN2(16); | |
166 //GETN(10, 6, Use); | |
167 return 0; | |
168 } | |
169 | |
170 static int UDFICB( uint8_t *data, uint8_t *FileType, uint16_t *Flags ) | |
171 { | |
172 *FileType = GETN1(11); | |
173 *Flags = GETN2(18); | |
174 return 0; | |
175 } | |
176 | |
177 | |
178 static int UDFPartition( uint8_t *data, uint16_t *Flags, uint16_t *Number, | |
179 char *Contents, uint32_t *Start, uint32_t *Length ) | |
180 { | |
181 *Flags = GETN2(20); | |
182 *Number = GETN2(22); | |
183 GETN(24, 32, Contents); | |
184 *Start = GETN4(188); | |
185 *Length = GETN4(192); | |
186 return 0; | |
187 } | |
188 | |
189 /** | |
190 * Reads the volume descriptor and checks the parameters. Returns 0 on OK, 1 | |
191 * on error. | |
192 */ | |
193 static int UDFLogVolume( uint8_t *data, char *VolumeDescriptor ) | |
194 { | |
195 uint32_t lbsize, MT_L, N_PM; | |
196 Unicodedecode(&data[84], 128, VolumeDescriptor); | |
197 lbsize = GETN4(212); // should be 2048 | |
198 MT_L = GETN4(264); // should be 6 | |
199 N_PM = GETN4(268); // should be 1 | |
200 if (lbsize != DVD_VIDEO_LB_LEN) return 1; | |
201 return 0; | |
202 } | |
203 | |
204 static int UDFFileEntry( uint8_t *data, uint8_t *FileType, | |
205 struct Partition *partition, struct AD *ad ) | |
206 { | |
207 uint16_t flags; | |
208 uint32_t L_EA, L_AD; | |
209 unsigned int p; | |
210 | |
211 UDFICB( &data[ 16 ], FileType, &flags ); | |
212 | |
213 /* Init ad for an empty file (i.e. there isn't a AD, L_AD == 0 ) */ | |
214 ad->Length = GETN4( 60 ); // Really 8 bytes a 56 | |
215 ad->Flags = 0; | |
216 ad->Location = 0; // what should we put here? | |
217 ad->Partition = partition->Number; // use number of current partition | |
218 | |
219 L_EA = GETN4( 168 ); | |
220 L_AD = GETN4( 172 ); | |
221 p = 176 + L_EA; | |
222 while( p < 176 + L_EA + L_AD ) { | |
223 switch( flags & 0x0007 ) { | |
224 case 0: UDFShortAD( &data[ p ], ad, partition ); p += 8; break; | |
225 case 1: UDFLongAD( &data[ p ], ad ); p += 16; break; | |
226 case 2: UDFExtAD( &data[ p ], ad ); p += 20; break; | |
227 case 3: | |
228 switch( L_AD ) { | |
229 case 8: UDFShortAD( &data[ p ], ad, partition ); break; | |
230 case 16: UDFLongAD( &data[ p ], ad ); break; | |
231 case 20: UDFExtAD( &data[ p ], ad ); break; | |
232 } | |
233 p += L_AD; | |
234 break; | |
235 default: | |
236 p += L_AD; break; | |
237 } | |
238 } | |
239 return 0; | |
240 } | |
241 | |
242 static int UDFFileIdentifier( uint8_t *data, uint8_t *FileCharacteristics, | |
243 char *FileName, struct AD *FileICB ) | |
244 { | |
245 uint8_t L_FI; | |
246 uint16_t L_IU; | |
247 | |
248 *FileCharacteristics = GETN1(18); | |
249 L_FI = GETN1(19); | |
250 UDFLongAD(&data[20], FileICB); | |
251 L_IU = GETN2(36); | |
252 if (L_FI) Unicodedecode(&data[38 + L_IU], L_FI, FileName); | |
253 else FileName[0] = '\0'; | |
254 return 4 * ((38 + L_FI + L_IU + 3) / 4); | |
255 } | |
256 | |
257 /** | |
258 * Maps ICB to FileAD | |
259 * ICB: Location of ICB of directory to scan | |
260 * FileType: Type of the file | |
261 * File: Location of file the ICB is pointing to | |
262 * return 1 on success, 0 on error; | |
263 */ | |
264 static int UDFMapICB( dvd_reader_t *device, struct AD ICB, uint8_t *FileType, | |
265 struct Partition *partition, struct AD *File ) | |
266 { | |
267 uint8_t LogBlock[DVD_VIDEO_LB_LEN]; | |
268 uint32_t lbnum; | |
269 uint16_t TagID; | |
270 | |
271 lbnum = partition->Start + ICB.Location; | |
272 do { | |
273 if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) { | |
274 TagID = 0; | |
275 } else { | |
276 UDFDescriptor( LogBlock, &TagID ); | |
277 } | |
278 | |
279 if( TagID == 261 ) { | |
280 UDFFileEntry( LogBlock, FileType, partition, File ); | |
281 return 1; | |
282 }; | |
283 } while( ( lbnum <= partition->Start + ICB.Location + ( ICB.Length - 1 ) | |
284 / DVD_VIDEO_LB_LEN ) && ( TagID != 261 ) ); | |
285 | |
286 return 0; | |
287 } | |
288 | |
289 /** | |
290 * Dir: Location of directory to scan | |
291 * FileName: Name of file to look for | |
292 * FileICB: Location of ICB of the found file | |
293 * return 1 on success, 0 on error; | |
294 */ | |
295 static int UDFScanDir( dvd_reader_t *device, struct AD Dir, char *FileName, | |
296 struct Partition *partition, struct AD *FileICB ) | |
297 { | |
298 char filename[ MAX_UDF_FILE_NAME_LEN ]; | |
299 uint8_t directory[ 2 * DVD_VIDEO_LB_LEN ]; | |
300 uint32_t lbnum; | |
301 uint16_t TagID; | |
302 uint8_t filechar; | |
303 unsigned int p; | |
304 | |
305 /* Scan dir for ICB of file */ | |
306 lbnum = partition->Start + Dir.Location; | |
307 | |
308 if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) { | |
309 return 0; | |
310 } | |
311 | |
312 p = 0; | |
313 while( p < Dir.Length ) { | |
314 if( p > DVD_VIDEO_LB_LEN ) { | |
315 ++lbnum; | |
316 p -= DVD_VIDEO_LB_LEN; | |
317 Dir.Length -= DVD_VIDEO_LB_LEN; | |
318 if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) { | |
319 return 0; | |
320 } | |
321 } | |
322 UDFDescriptor( &directory[ p ], &TagID ); | |
323 if( TagID == 257 ) { | |
324 p += UDFFileIdentifier( &directory[ p ], &filechar, | |
325 filename, FileICB ); | |
326 if( !strcasecmp( FileName, filename ) ) { | |
327 return 1; | |
328 } | |
329 } else { | |
330 return 0; | |
331 } | |
332 } | |
333 | |
334 return 0; | |
335 } | |
336 | |
337 /** | |
338 * Looks for partition on the disc. Returns 1 if partition found, 0 on error. | |
339 * partnum: Number of the partition, starting at 0. | |
340 * part: structure to fill with the partition information | |
341 */ | |
342 static int UDFFindPartition( dvd_reader_t *device, int partnum, | |
343 struct Partition *part ) | |
344 { | |
345 uint8_t LogBlock[ DVD_VIDEO_LB_LEN ], Anchor[ DVD_VIDEO_LB_LEN ]; | |
346 uint32_t lbnum, MVDS_location, MVDS_length; | |
347 uint16_t TagID; | |
348 uint32_t lastsector; | |
349 int i, terminate, volvalid; | |
350 | |
351 /* Find Anchor */ | |
352 lastsector = 0; | |
353 lbnum = 256; /* Try #1, prime anchor */ | |
354 terminate = 0; | |
355 | |
356 for(;;) { | |
357 if( DVDReadLBUDF( device, lbnum, 1, Anchor, 0 ) > 0 ) { | |
358 UDFDescriptor( Anchor, &TagID ); | |
359 } else { | |
360 TagID = 0; | |
361 } | |
362 if (TagID != 2) { | |
363 /* Not an anchor */ | |
364 if( terminate ) return 0; /* Final try failed */ | |
365 | |
366 if( lastsector ) { | |
367 | |
368 /* We already found the last sector. Try #3, alternative | |
369 * backup anchor. If that fails, don't try again. | |
370 */ | |
371 lbnum = lastsector; | |
372 terminate = 1; | |
373 } else { | |
374 /* TODO: Find last sector of the disc (this is optional). */ | |
375 if( lastsector ) { | |
376 /* Try #2, backup anchor */ | |
377 lbnum = lastsector - 256; | |
378 } else { | |
379 /* Unable to find last sector */ | |
380 return 0; | |
381 } | |
382 } | |
383 } else { | |
384 /* It's an anchor! We can leave */ | |
385 break; | |
386 } | |
387 } | |
388 /* Main volume descriptor */ | |
389 UDFExtentAD( &Anchor[ 16 ], &MVDS_length, &MVDS_location ); | |
390 | |
391 part->valid = 0; | |
392 volvalid = 0; | |
393 part->VolumeDesc[ 0 ] = '\0'; | |
394 i = 1; | |
395 do { | |
396 /* Find Volume Descriptor */ | |
397 lbnum = MVDS_location; | |
398 do { | |
399 | |
400 if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) { | |
401 TagID = 0; | |
402 } else { | |
403 UDFDescriptor( LogBlock, &TagID ); | |
404 } | |
405 | |
406 if( ( TagID == 5 ) && ( !part->valid ) ) { | |
407 /* Partition Descriptor */ | |
408 UDFPartition( LogBlock, &part->Flags, &part->Number, | |
409 part->Contents, &part->Start, &part->Length ); | |
410 part->valid = ( partnum == part->Number ); | |
411 } else if( ( TagID == 6 ) && ( !volvalid ) ) { | |
412 /* Logical Volume Descriptor */ | |
413 if( UDFLogVolume( LogBlock, part->VolumeDesc ) ) { | |
414 /* TODO: sector size wrong! */ | |
415 } else { | |
416 volvalid = 1; | |
417 } | |
418 } | |
419 | |
420 } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 ) | |
421 / DVD_VIDEO_LB_LEN ) && ( TagID != 8 ) | |
422 && ( ( !part->valid ) || ( !volvalid ) ) ); | |
423 | |
424 if( ( !part->valid) || ( !volvalid ) ) { | |
425 /* Backup volume descriptor */ | |
426 UDFExtentAD( &Anchor[ 24 ], &MVDS_length, &MVDS_location ); | |
427 } | |
428 } while( i-- && ( ( !part->valid ) || ( !volvalid ) ) ); | |
429 | |
430 /* We only care for the partition, not the volume */ | |
431 return part->valid; | |
432 } | |
433 | |
434 uint32_t UDFFindFile( dvd_reader_t *device, char *filename, | |
435 uint32_t *filesize ) | |
436 { | |
437 uint8_t LogBlock[ DVD_VIDEO_LB_LEN ]; | |
438 uint32_t lbnum; | |
439 uint16_t TagID; | |
440 struct Partition partition; | |
441 struct AD RootICB, File, ICB; | |
442 char tokenline[ MAX_UDF_FILE_NAME_LEN ]; | |
443 char *token; | |
444 uint8_t filetype; | |
445 | |
446 *filesize = 0; | |
447 tokenline[0] = '\0'; | |
448 strcat( tokenline, filename ); | |
449 | |
450 /* Find partition, 0 is the standard location for DVD Video.*/ | |
451 if( !UDFFindPartition( device, 0, &partition ) ) return 0; | |
452 | |
453 /* Find root dir ICB */ | |
454 lbnum = partition.Start; | |
455 do { | |
456 if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) { | |
457 TagID = 0; | |
458 } else { | |
459 UDFDescriptor( LogBlock, &TagID ); | |
460 } | |
461 | |
462 /* File Set Descriptor */ | |
463 if( TagID == 256 ) { // File Set Descriptor | |
464 UDFLongAD( &LogBlock[ 400 ], &RootICB ); | |
465 } | |
466 } while( ( lbnum < partition.Start + partition.Length ) | |
467 && ( TagID != 8 ) && ( TagID != 256 ) ); | |
468 | |
469 /* Sanity checks. */ | |
470 if( TagID != 256 ) return 0; | |
471 if( RootICB.Partition != 0 ) return 0; | |
472 | |
473 /* Find root dir */ | |
474 if( !UDFMapICB( device, RootICB, &filetype, &partition, &File ) ) return 0; | |
475 if( filetype != 4 ) return 0; /* Root dir should be dir */ | |
476 | |
477 /* Tokenize filepath */ | |
478 token = strtok(tokenline, "/"); | |
479 while( token != NULL ) { | |
480 if( !UDFScanDir( device, File, token, &partition, &ICB ) ) return 0; | |
481 if( !UDFMapICB( device, ICB, &filetype, &partition, &File ) ) return 0; | |
482 token = strtok( NULL, "/" ); | |
483 } | |
484 | |
485 /* Sanity check. */ | |
486 if( File.Partition != 0 ) return 0; | |
487 | |
488 *filesize = File.Length; | |
489 /* Hack to not return partition.Start for empty files. */ | |
490 if( !File.Location ) | |
491 return 0; | |
492 else | |
493 return partition.Start + File.Location; | |
494 } |