comparison libdvdread/dvd_udf.c @ 367:1274107d0eac src

moved dvdread to libdvdread; it's the first step for the separation of the lib from dvdnav
author nicodvb
date Sat, 31 May 2008 12:29:19 +0000
parents dvdread/dvd_udf.c@b6fa98f690ee
children 78e68cde11cf
comparison
equal deleted inserted replaced
366:5b8539cacebf 367:1274107d0eac
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 * Björn Englund <d4bjorn@dtek.chalmers.se>.
8 *
9 * dvdudf: parse and read the UDF volume information of a DVD Video
10 * Copyright (C) 1999 Christian Wolff for convergence integrated media
11 * GmbH The author can be reached at scarabaeus@convergence.de, the
12 * project's page is at http://linuxtv.org/dvd/
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or (at
17 * your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 * 02111-1307, USA. Or, point your browser to
28 * http://www.gnu.org/copyleft/gpl.html
29 */
30
31 #include "config.h"
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <unistd.h>
40 #include <inttypes.h>
41
42 #include "dvd_reader.h"
43 #include "dvd_udf.h"
44
45 /* Private but located in/shared with dvd_reader.c */
46 extern int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
47 size_t block_count, unsigned char *data,
48 int encrypted );
49
50 /* It's required to either fail or deliver all the blocks asked for. */
51 static int DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number,
52 size_t block_count, unsigned char *data,
53 int encrypted )
54 {
55 int ret;
56 size_t count = block_count;
57
58 while(count > 0) {
59 ret = UDFReadBlocksRaw(device, lb_number, count, data, encrypted);
60
61 if(ret <= 0) {
62 /* One of the reads failed or nothing more to read, too bad.
63 * We won't even bother returning the reads that went ok. */
64 return ret;
65 }
66
67 count -= (size_t)ret;
68 lb_number += (uint32_t)ret;
69 }
70
71 return block_count;
72 }
73
74
75 #ifndef NULL
76 #define NULL ((void *)0)
77 #endif
78
79 struct Partition {
80 int valid;
81 char VolumeDesc[128];
82 uint16_t Flags;
83 uint16_t Number;
84 char Contents[32];
85 uint32_t AccessType;
86 uint32_t Start;
87 uint32_t Length;
88 };
89
90 struct AD {
91 uint32_t Location;
92 uint32_t Length;
93 uint8_t Flags;
94 uint16_t Partition;
95 };
96
97 struct extent_ad {
98 uint32_t location;
99 uint32_t length;
100 };
101
102 struct avdp_t {
103 struct extent_ad mvds;
104 struct extent_ad rvds;
105 };
106
107 struct pvd_t {
108 uint8_t VolumeIdentifier[32];
109 uint8_t VolumeSetIdentifier[128];
110 };
111
112 struct lbudf {
113 uint32_t lb;
114 uint8_t *data;
115 /* needed for proper freeing */
116 uint8_t *data_base;
117 };
118
119 struct icbmap {
120 uint32_t lbn;
121 struct AD file;
122 uint8_t filetype;
123 };
124
125 struct udf_cache {
126 int avdp_valid;
127 struct avdp_t avdp;
128 int pvd_valid;
129 struct pvd_t pvd;
130 int partition_valid;
131 struct Partition partition;
132 int rooticb_valid;
133 struct AD rooticb;
134 int lb_num;
135 struct lbudf *lbs;
136 int map_num;
137 struct icbmap *maps;
138 };
139
140 typedef enum {
141 PartitionCache, RootICBCache, LBUDFCache, MapCache, AVDPCache, PVDCache
142 } UDFCacheType;
143
144 void FreeUDFCache(void *cache)
145 {
146 struct udf_cache *c = (struct udf_cache *)cache;
147 if(c == NULL)
148 return;
149
150 if(c->lbs) {
151 int n;
152 for(n = 0; n < c->lb_num; n++)
153 free(c->lbs[n].data_base);
154 free(c->lbs);
155 }
156 if(c->maps)
157 free(c->maps);
158 free(c);
159 }
160
161
162 static int GetUDFCache(dvd_reader_t *device, UDFCacheType type,
163 uint32_t nr, void *data)
164 {
165 int n;
166 struct udf_cache *c;
167
168 if(DVDUDFCacheLevel(device, -1) <= 0)
169 return 0;
170
171 c = (struct udf_cache *)GetUDFCacheHandle(device);
172
173 if(c == NULL)
174 return 0;
175
176 switch(type) {
177 case AVDPCache:
178 if(c->avdp_valid) {
179 *(struct avdp_t *)data = c->avdp;
180 return 1;
181 }
182 break;
183 case PVDCache:
184 if(c->pvd_valid) {
185 *(struct pvd_t *)data = c->pvd;
186 return 1;
187 }
188 break;
189 case PartitionCache:
190 if(c->partition_valid) {
191 *(struct Partition *)data = c->partition;
192 return 1;
193 }
194 break;
195 case RootICBCache:
196 if(c->rooticb_valid) {
197 *(struct AD *)data = c->rooticb;
198 return 1;
199 }
200 break;
201 case LBUDFCache:
202 for(n = 0; n < c->lb_num; n++) {
203 if(c->lbs[n].lb == nr) {
204 *(uint8_t **)data = c->lbs[n].data;
205 return 1;
206 }
207 }
208 break;
209 case MapCache:
210 for(n = 0; n < c->map_num; n++) {
211 if(c->maps[n].lbn == nr) {
212 *(struct icbmap *)data = c->maps[n];
213 return 1;
214 }
215 }
216 break;
217 default:
218 break;
219 }
220
221 return 0;
222 }
223
224 static int SetUDFCache(dvd_reader_t *device, UDFCacheType type,
225 uint32_t nr, void *data)
226 {
227 int n;
228 struct udf_cache *c;
229 void *tmp;
230
231 if(DVDUDFCacheLevel(device, -1) <= 0)
232 return 0;
233
234 c = (struct udf_cache *)GetUDFCacheHandle(device);
235
236 if(c == NULL) {
237 c = calloc(1, sizeof(struct udf_cache));
238 /* fprintf(stderr, "calloc: %d\n", sizeof(struct udf_cache)); */
239 if(c == NULL)
240 return 0;
241 SetUDFCacheHandle(device, c);
242 }
243
244
245 switch(type) {
246 case AVDPCache:
247 c->avdp = *(struct avdp_t *)data;
248 c->avdp_valid = 1;
249 break;
250 case PVDCache:
251 c->pvd = *(struct pvd_t *)data;
252 c->pvd_valid = 1;
253 break;
254 case PartitionCache:
255 c->partition = *(struct Partition *)data;
256 c->partition_valid = 1;
257 break;
258 case RootICBCache:
259 c->rooticb = *(struct AD *)data;
260 c->rooticb_valid = 1;
261 break;
262 case LBUDFCache:
263 for(n = 0; n < c->lb_num; n++) {
264 if(c->lbs[n].lb == nr) {
265 /* replace with new data */
266 c->lbs[n].data_base = ((uint8_t **)data)[0];
267 c->lbs[n].data = ((uint8_t **)data)[1];
268 c->lbs[n].lb = nr;
269 return 1;
270 }
271 }
272 c->lb_num++;
273 tmp = realloc(c->lbs, c->lb_num * sizeof(struct lbudf));
274 /*
275 fprintf(stderr, "realloc lb: %d * %d = %d\n",
276 c->lb_num, sizeof(struct lbudf),
277 c->lb_num * sizeof(struct lbudf));
278 */
279 if(tmp == NULL) {
280 if(c->lbs) free(c->lbs);
281 c->lb_num = 0;
282 return 0;
283 }
284 c->lbs = tmp;
285 c->lbs[n].data_base = ((uint8_t **)data)[0];
286 c->lbs[n].data = ((uint8_t **)data)[1];
287 c->lbs[n].lb = nr;
288 break;
289 case MapCache:
290 for(n = 0; n < c->map_num; n++) {
291 if(c->maps[n].lbn == nr) {
292 /* replace with new data */
293 c->maps[n] = *(struct icbmap *)data;
294 c->maps[n].lbn = nr;
295 return 1;
296 }
297 }
298 c->map_num++;
299 tmp = realloc(c->maps, c->map_num * sizeof(struct icbmap));
300 /*
301 fprintf(stderr, "realloc maps: %d * %d = %d\n",
302 c->map_num, sizeof(struct icbmap),
303 c->map_num * sizeof(struct icbmap));
304 */
305 if(tmp == NULL) {
306 if(c->maps) free(c->maps);
307 c->map_num = 0;
308 return 0;
309 }
310 c->maps = tmp;
311 c->maps[n] = *(struct icbmap *)data;
312 c->maps[n].lbn = nr;
313 break;
314 default:
315 return 0;
316 }
317
318 return 1;
319 }
320
321
322 /* For direct data access, LSB first */
323 #define GETN1(p) ((uint8_t)data[p])
324 #define GETN2(p) ((uint16_t)data[p] | ((uint16_t)data[(p) + 1] << 8))
325 #define GETN3(p) ((uint32_t)data[p] | ((uint32_t)data[(p) + 1] << 8) \
326 | ((uint32_t)data[(p) + 2] << 16))
327 #define GETN4(p) ((uint32_t)data[p] \
328 | ((uint32_t)data[(p) + 1] << 8) \
329 | ((uint32_t)data[(p) + 2] << 16) \
330 | ((uint32_t)data[(p) + 3] << 24))
331 /* This is wrong with regard to endianess */
332 #define GETN(p, n, target) memcpy(target, &data[p], n)
333
334 static int Unicodedecode( uint8_t *data, int len, char *target )
335 {
336 int p = 1, i = 0;
337
338 if( ( data[ 0 ] == 8 ) || ( data[ 0 ] == 16 ) ) do {
339 if( data[ 0 ] == 16 ) p++; /* Ignore MSB of unicode16 */
340 if( p < len ) {
341 target[ i++ ] = data[ p++ ];
342 }
343 } while( p < len );
344
345 target[ i ] = '\0';
346 return 0;
347 }
348
349 static int UDFDescriptor( uint8_t *data, uint16_t *TagID )
350 {
351 *TagID = GETN2(0);
352 /* TODO: check CRC 'n stuff */
353 return 0;
354 }
355
356 static int UDFExtentAD( uint8_t *data, uint32_t *Length, uint32_t *Location )
357 {
358 *Length = GETN4(0);
359 *Location = GETN4(4);
360 return 0;
361 }
362
363 static int UDFShortAD( uint8_t *data, struct AD *ad,
364 struct Partition *partition )
365 {
366 ad->Length = GETN4(0);
367 ad->Flags = ad->Length >> 30;
368 ad->Length &= 0x3FFFFFFF;
369 ad->Location = GETN4(4);
370 ad->Partition = partition->Number; /* use number of current partition */
371 return 0;
372 }
373
374 static int UDFLongAD( uint8_t *data, struct AD *ad )
375 {
376 ad->Length = GETN4(0);
377 ad->Flags = ad->Length >> 30;
378 ad->Length &= 0x3FFFFFFF;
379 ad->Location = GETN4(4);
380 ad->Partition = GETN2(8);
381 /* GETN(10, 6, Use); */
382 return 0;
383 }
384
385 static int UDFExtAD( uint8_t *data, struct AD *ad )
386 {
387 ad->Length = GETN4(0);
388 ad->Flags = ad->Length >> 30;
389 ad->Length &= 0x3FFFFFFF;
390 ad->Location = GETN4(12);
391 ad->Partition = GETN2(16);
392 /* GETN(10, 6, Use); */
393 return 0;
394 }
395
396 static int UDFICB( uint8_t *data, uint8_t *FileType, uint16_t *Flags )
397 {
398 *FileType = GETN1(11);
399 *Flags = GETN2(18);
400 return 0;
401 }
402
403
404 static int UDFPartition( uint8_t *data, uint16_t *Flags, uint16_t *Number,
405 char *Contents, uint32_t *Start, uint32_t *Length )
406 {
407 *Flags = GETN2(20);
408 *Number = GETN2(22);
409 GETN(24, 32, Contents);
410 *Start = GETN4(188);
411 *Length = GETN4(192);
412 return 0;
413 }
414
415 /**
416 * Reads the volume descriptor and checks the parameters. Returns 0 on OK, 1
417 * on error.
418 */
419 static int UDFLogVolume( uint8_t *data, char *VolumeDescriptor )
420 {
421 uint32_t lbsize, MT_L, N_PM;
422 Unicodedecode(&data[84], 128, VolumeDescriptor);
423 lbsize = GETN4(212); /* should be 2048 */
424 MT_L = GETN4(264); /* should be 6 */
425 N_PM = GETN4(268); /* should be 1 */
426 if (lbsize != DVD_VIDEO_LB_LEN) return 1;
427 return 0;
428 }
429
430 static int UDFFileEntry( uint8_t *data, uint8_t *FileType,
431 struct Partition *partition, struct AD *ad )
432 {
433 uint16_t flags;
434 uint32_t L_EA, L_AD;
435 unsigned int p;
436
437 UDFICB( &data[ 16 ], FileType, &flags );
438
439 /* Init ad for an empty file (i.e. there isn't a AD, L_AD == 0 ) */
440 ad->Length = GETN4( 60 ); /* Really 8 bytes a 56 */
441 ad->Flags = 0;
442 ad->Location = 0; /* what should we put here? */
443 ad->Partition = partition->Number; /* use number of current partition */
444
445 L_EA = GETN4( 168 );
446 L_AD = GETN4( 172 );
447 p = 176 + L_EA;
448 while( p < 176 + L_EA + L_AD ) {
449 switch( flags & 0x0007 ) {
450 case 0: UDFShortAD( &data[ p ], ad, partition ); p += 8; break;
451 case 1: UDFLongAD( &data[ p ], ad ); p += 16; break;
452 case 2: UDFExtAD( &data[ p ], ad ); p += 20; break;
453 case 3:
454 switch( L_AD ) {
455 case 8: UDFShortAD( &data[ p ], ad, partition ); break;
456 case 16: UDFLongAD( &data[ p ], ad ); break;
457 case 20: UDFExtAD( &data[ p ], ad ); break;
458 }
459 p += L_AD;
460 break;
461 default:
462 p += L_AD; break;
463 }
464 }
465 return 0;
466 }
467
468 static int UDFFileIdentifier( uint8_t *data, uint8_t *FileCharacteristics,
469 char *FileName, struct AD *FileICB )
470 {
471 uint8_t L_FI;
472 uint16_t L_IU;
473
474 *FileCharacteristics = GETN1(18);
475 L_FI = GETN1(19);
476 UDFLongAD(&data[20], FileICB);
477 L_IU = GETN2(36);
478 if (L_FI) Unicodedecode(&data[38 + L_IU], L_FI, FileName);
479 else FileName[0] = '\0';
480 return 4 * ((38 + L_FI + L_IU + 3) / 4);
481 }
482
483 /**
484 * Maps ICB to FileAD
485 * ICB: Location of ICB of directory to scan
486 * FileType: Type of the file
487 * File: Location of file the ICB is pointing to
488 * return 1 on success, 0 on error;
489 */
490 static int UDFMapICB( dvd_reader_t *device, struct AD ICB, uint8_t *FileType,
491 struct Partition *partition, struct AD *File )
492 {
493 uint8_t LogBlock_base[DVD_VIDEO_LB_LEN + 2048];
494 uint8_t *LogBlock = (uint8_t *)(((uintptr_t)LogBlock_base & ~((uintptr_t)2047)) + 2048);
495 uint32_t lbnum;
496 uint16_t TagID;
497 struct icbmap tmpmap;
498
499 lbnum = partition->Start + ICB.Location;
500 tmpmap.lbn = lbnum;
501 if(GetUDFCache(device, MapCache, lbnum, &tmpmap)) {
502 *FileType = tmpmap.filetype;
503 memcpy(File, &tmpmap.file, sizeof(tmpmap.file));
504 return 1;
505 }
506
507 do {
508 if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 )
509 TagID = 0;
510 else
511 UDFDescriptor( LogBlock, &TagID );
512
513 if( TagID == 261 ) {
514 UDFFileEntry( LogBlock, FileType, partition, File );
515 memcpy(&tmpmap.file, File, sizeof(tmpmap.file));
516 tmpmap.filetype = *FileType;
517 SetUDFCache(device, MapCache, tmpmap.lbn, &tmpmap);
518 return 1;
519 };
520 } while( ( lbnum <= partition->Start + ICB.Location + ( ICB.Length - 1 )
521 / DVD_VIDEO_LB_LEN ) && ( TagID != 261 ) );
522
523 return 0;
524 }
525
526 /**
527 * Dir: Location of directory to scan
528 * FileName: Name of file to look for
529 * FileICB: Location of ICB of the found file
530 * return 1 on success, 0 on error;
531 */
532 static int UDFScanDir( dvd_reader_t *device, struct AD Dir, char *FileName,
533 struct Partition *partition, struct AD *FileICB,
534 int cache_file_info)
535 {
536 char filename[ MAX_UDF_FILE_NAME_LEN ];
537 uint8_t directory_base[ 2 * DVD_VIDEO_LB_LEN + 2048];
538 uint8_t *directory = (uint8_t *)(((uintptr_t)directory_base & ~((uintptr_t)2047)) + 2048);
539 uint32_t lbnum;
540 uint16_t TagID;
541 uint8_t filechar;
542 unsigned int p;
543 uint8_t *cached_dir_base = NULL, *cached_dir;
544 uint32_t dir_lba;
545 struct AD tmpICB;
546 int found = 0;
547 int in_cache = 0;
548
549 /* Scan dir for ICB of file */
550 lbnum = partition->Start + Dir.Location;
551
552 if(DVDUDFCacheLevel(device, -1) > 0) {
553 /* caching */
554
555 if(!GetUDFCache(device, LBUDFCache, lbnum, &cached_dir)) {
556 dir_lba = (Dir.Length + DVD_VIDEO_LB_LEN) / DVD_VIDEO_LB_LEN;
557 if((cached_dir_base = malloc(dir_lba * DVD_VIDEO_LB_LEN + 2048)) == NULL)
558 return 0;
559
560 cached_dir = (uint8_t *)(((uintptr_t)cached_dir_base & ~((uintptr_t)2047)) + 2048);
561 if( DVDReadLBUDF( device, lbnum, dir_lba, cached_dir, 0) <= 0 ) {
562 free(cached_dir_base);
563 cached_dir_base = NULL;
564 cached_dir = NULL;
565 }
566 /*
567 if(cached_dir) {
568 fprintf(stderr, "malloc dir: %d\n", dir_lba * DVD_VIDEO_LB_LEN);
569 }
570 */
571 {
572 uint8_t *data[2];
573 data[0] = cached_dir_base;
574 data[1] = cached_dir;
575 SetUDFCache(device, LBUDFCache, lbnum, data);
576 }
577 } else
578 in_cache = 1;
579
580 if(cached_dir == NULL)
581 return 0;
582
583 p = 0;
584
585 while( p < Dir.Length ) {
586 UDFDescriptor( &cached_dir[ p ], &TagID );
587 if( TagID == 257 ) {
588 p += UDFFileIdentifier( &cached_dir[ p ], &filechar, filename, &tmpICB );
589 if(cache_file_info && !in_cache) {
590 uint8_t tmpFiletype;
591 struct AD tmpFile;
592
593 if( !strcasecmp( FileName, filename ) ) {
594 *FileICB = tmpICB;
595 found = 1;
596 }
597 UDFMapICB(device, tmpICB, &tmpFiletype, partition, &tmpFile);
598 } else {
599 if( !strcasecmp( FileName, filename ) ) {
600 *FileICB = tmpICB;
601 return 1;
602 }
603 }
604 } else {
605 if(cache_file_info && (!in_cache) && found)
606 return 1;
607 return 0;
608 }
609 }
610 if(cache_file_info && (!in_cache) && found)
611 return 1;
612 return 0;
613 }
614
615 if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 )
616 return 0;
617
618 p = 0;
619 while( p < Dir.Length ) {
620 if( p > DVD_VIDEO_LB_LEN ) {
621 ++lbnum;
622 p -= DVD_VIDEO_LB_LEN;
623 Dir.Length -= DVD_VIDEO_LB_LEN;
624 if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
625 return 0;
626 }
627 }
628 UDFDescriptor( &directory[ p ], &TagID );
629 if( TagID == 257 ) {
630 p += UDFFileIdentifier( &directory[ p ], &filechar,
631 filename, FileICB );
632 if( !strcasecmp( FileName, filename ) ) {
633 return 1;
634 }
635 } else
636 return 0;
637 }
638
639 return 0;
640 }
641
642
643 static int UDFGetAVDP( dvd_reader_t *device,
644 struct avdp_t *avdp)
645 {
646 uint8_t Anchor_base[ DVD_VIDEO_LB_LEN + 2048 ];
647 uint8_t *Anchor = (uint8_t *)(((uintptr_t)Anchor_base & ~((uintptr_t)2047)) + 2048);
648 uint32_t lbnum, MVDS_location, MVDS_length;
649 uint16_t TagID;
650 uint32_t lastsector;
651 int terminate;
652 struct avdp_t;
653
654 if(GetUDFCache(device, AVDPCache, 0, avdp))
655 return 1;
656
657 /* Find Anchor */
658 lastsector = 0;
659 lbnum = 256; /* Try #1, prime anchor */
660 terminate = 0;
661
662 for(;;) {
663 if( DVDReadLBUDF( device, lbnum, 1, Anchor, 0 ) > 0 ) {
664 UDFDescriptor( Anchor, &TagID );
665 } else {
666 TagID = 0;
667 }
668 if (TagID != 2) {
669 /* Not an anchor */
670 if( terminate ) return 0; /* Final try failed */
671
672 if( lastsector ) {
673 /*
674 * We already found the last sector. Try #3, alternative
675 * backup anchor. If that fails, don't try again.
676 */
677 lbnum = lastsector;
678 terminate = 1;
679 } else {
680 /* TODO: Find last sector of the disc (this is optional). */
681 if( lastsector )
682 /* Try #2, backup anchor */
683 lbnum = lastsector - 256;
684 else
685 /* Unable to find last sector */
686 return 0;
687 }
688 } else
689 /* It's an anchor! We can leave */
690 break;
691 }
692 /* Main volume descriptor */
693 UDFExtentAD( &Anchor[ 16 ], &MVDS_length, &MVDS_location );
694 avdp->mvds.location = MVDS_location;
695 avdp->mvds.length = MVDS_length;
696
697 /* Backup volume descriptor */
698 UDFExtentAD( &Anchor[ 24 ], &MVDS_length, &MVDS_location );
699 avdp->rvds.location = MVDS_location;
700 avdp->rvds.length = MVDS_length;
701
702 SetUDFCache(device, AVDPCache, 0, avdp);
703
704 return 1;
705 }
706
707 /**
708 * Looks for partition on the disc. Returns 1 if partition found, 0 on error.
709 * partnum: Number of the partition, starting at 0.
710 * part: structure to fill with the partition information
711 */
712 static int UDFFindPartition( dvd_reader_t *device, int partnum,
713 struct Partition *part )
714 {
715 uint8_t LogBlock_base[ DVD_VIDEO_LB_LEN + 2048 ];
716 uint8_t *LogBlock = (uint8_t *)(((uintptr_t)LogBlock_base & ~((uintptr_t)2047)) + 2048);
717 uint32_t lbnum, MVDS_location, MVDS_length;
718 uint16_t TagID;
719 int i, volvalid;
720 struct avdp_t avdp;
721
722 if(!UDFGetAVDP(device, &avdp))
723 return 0;
724
725 /* Main volume descriptor */
726 MVDS_location = avdp.mvds.location;
727 MVDS_length = avdp.mvds.length;
728
729 part->valid = 0;
730 volvalid = 0;
731 part->VolumeDesc[ 0 ] = '\0';
732 i = 1;
733 do {
734 /* Find Volume Descriptor */
735 lbnum = MVDS_location;
736 do {
737
738 if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 )
739 TagID = 0;
740 else
741 UDFDescriptor( LogBlock, &TagID );
742
743 if( ( TagID == 5 ) && ( !part->valid ) ) {
744 /* Partition Descriptor */
745 UDFPartition( LogBlock, &part->Flags, &part->Number,
746 part->Contents, &part->Start, &part->Length );
747 part->valid = ( partnum == part->Number );
748 } else if( ( TagID == 6 ) && ( !volvalid ) ) {
749 /* Logical Volume Descriptor */
750 if( UDFLogVolume( LogBlock, part->VolumeDesc ) ) {
751 /* TODO: sector size wrong! */
752 } else
753 volvalid = 1;
754 }
755
756 } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 )
757 / DVD_VIDEO_LB_LEN ) && ( TagID != 8 )
758 && ( ( !part->valid ) || ( !volvalid ) ) );
759
760 if( ( !part->valid) || ( !volvalid ) ) {
761 /* Backup volume descriptor */
762 MVDS_location = avdp.mvds.location;
763 MVDS_length = avdp.mvds.length;
764 }
765 } while( i-- && ( ( !part->valid ) || ( !volvalid ) ) );
766
767 /* We only care for the partition, not the volume */
768 return part->valid;
769 }
770
771 uint32_t UDFFindFile( dvd_reader_t *device, char *filename,
772 uint32_t *filesize )
773 {
774 uint8_t LogBlock_base[ DVD_VIDEO_LB_LEN + 2048 ];
775 uint8_t *LogBlock = (uint8_t *)(((uintptr_t)LogBlock_base & ~((uintptr_t)2047)) + 2048);
776 uint32_t lbnum;
777 uint16_t TagID;
778 struct Partition partition;
779 struct AD RootICB, File, ICB;
780 char tokenline[ MAX_UDF_FILE_NAME_LEN ];
781 char *token;
782 uint8_t filetype;
783
784 *filesize = 0;
785 tokenline[0] = '\0';
786 strncat(tokenline, filename, MAX_UDF_FILE_NAME_LEN - 1);
787
788 if(!(GetUDFCache(device, PartitionCache, 0, &partition) &&
789 GetUDFCache(device, RootICBCache, 0, &RootICB))) {
790 /* Find partition, 0 is the standard location for DVD Video.*/
791 if( !UDFFindPartition( device, 0, &partition ) ) return 0;
792 SetUDFCache(device, PartitionCache, 0, &partition);
793
794 /* Find root dir ICB */
795 lbnum = partition.Start;
796 do {
797 if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 )
798 TagID = 0;
799 else
800 UDFDescriptor( LogBlock, &TagID );
801
802 /* File Set Descriptor */
803 if( TagID == 256 ) /* File Set Descriptor */
804 UDFLongAD( &LogBlock[ 400 ], &RootICB );
805 } while( ( lbnum < partition.Start + partition.Length )
806 && ( TagID != 8 ) && ( TagID != 256 ) );
807
808 /* Sanity checks. */
809 if( TagID != 256 ) return 0;
810 if( RootICB.Partition != 0 ) return 0;
811 SetUDFCache(device, RootICBCache, 0, &RootICB);
812 }
813
814 /* Find root dir */
815 if( !UDFMapICB( device, RootICB, &filetype, &partition, &File ) ) return 0;
816 if( filetype != 4 ) return 0; /* Root dir should be dir */
817
818 {
819 int cache_file_info = 0;
820 /* Tokenize filepath */
821 token = strtok(tokenline, "/");
822 while( token != NULL ) {
823 if( !UDFScanDir( device, File, token, &partition, &ICB,
824 cache_file_info))
825 return 0;
826 if( !UDFMapICB( device, ICB, &filetype, &partition, &File ) )
827 return 0;
828 if(!strcmp(token, "VIDEO_TS"))
829 cache_file_info = 1;
830 token = strtok( NULL, "/" );
831 }
832 }
833
834 /* Sanity check. */
835 if( File.Partition != 0 ) return 0;
836 *filesize = File.Length;
837 /* Hack to not return partition.Start for empty files. */
838 if( !File.Location )
839 return 0;
840 else
841 return partition.Start + File.Location;
842 }
843
844
845
846 /**
847 * Gets a Descriptor .
848 * Returns 1 if descriptor found, 0 on error.
849 * id, tagid of descriptor
850 * bufsize, size of BlockBuf (must be >= DVD_VIDEO_LB_LEN).
851 */
852 static int UDFGetDescriptor( dvd_reader_t *device, int id,
853 uint8_t *descriptor, int bufsize)
854 {
855 uint32_t lbnum, MVDS_location, MVDS_length;
856 struct avdp_t avdp;
857 uint16_t TagID;
858 uint32_t lastsector;
859 int i, terminate;
860 int desc_found = 0;
861 /* Find Anchor */
862 lastsector = 0;
863 lbnum = 256; /* Try #1, prime anchor */
864 terminate = 0;
865 if(bufsize < DVD_VIDEO_LB_LEN)
866 return 0;
867
868 if(!UDFGetAVDP(device, &avdp))
869 return 0;
870
871 /* Main volume descriptor */
872 MVDS_location = avdp.mvds.location;
873 MVDS_length = avdp.mvds.length;
874 i = 1;
875 do {
876 /* Find Descriptor */
877 lbnum = MVDS_location;
878 do {
879 if( DVDReadLBUDF( device, lbnum++, 1, descriptor, 0 ) <= 0 )
880 TagID = 0;
881 else
882 UDFDescriptor( descriptor, &TagID );
883 if( (TagID == id) && ( !desc_found ) )
884 /* Descriptor */
885 desc_found = 1;
886 } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 )
887 / DVD_VIDEO_LB_LEN ) && ( TagID != 8 )
888 && ( !desc_found) );
889
890 if( !desc_found ) {
891 /* Backup volume descriptor */
892 MVDS_location = avdp.rvds.location;
893 MVDS_length = avdp.rvds.length;
894 }
895 } while( i-- && ( !desc_found ) );
896
897 return desc_found;
898 }
899
900
901 static int UDFGetPVD(dvd_reader_t *device, struct pvd_t *pvd)
902 {
903 uint8_t pvd_buf_base[DVD_VIDEO_LB_LEN + 2048];
904 uint8_t *pvd_buf = (uint8_t *)(((uintptr_t)pvd_buf_base & ~((uintptr_t)2047)) + 2048);
905 if(GetUDFCache(device, PVDCache, 0, pvd))
906 return 1;
907
908 if(!UDFGetDescriptor( device, 1, pvd_buf, sizeof(pvd_buf)))
909 return 0;
910
911 memcpy(pvd->VolumeIdentifier, &pvd_buf[24], 32);
912 memcpy(pvd->VolumeSetIdentifier, &pvd_buf[72], 128);
913 SetUDFCache(device, PVDCache, 0, pvd);
914 return 1;
915 }
916
917 /**
918 * Gets the Volume Identifier string, in 8bit unicode (latin-1)
919 * volid, place to put the string
920 * volid_size, size of the buffer volid points to
921 * returns the size of buffer needed for all data
922 */
923 int UDFGetVolumeIdentifier(dvd_reader_t *device, char *volid,
924 unsigned int volid_size)
925 {
926 struct pvd_t pvd;
927 unsigned int volid_len;
928
929 /* get primary volume descriptor */
930 if(!UDFGetPVD(device, &pvd))
931 return 0;
932
933 volid_len = pvd.VolumeIdentifier[31];
934 if(volid_len > 31)
935 /* this field is only 32 bytes something is wrong */
936 volid_len = 31;
937 if(volid_size > volid_len)
938 volid_size = volid_len;
939 Unicodedecode(pvd.VolumeIdentifier, volid_size, volid);
940 return volid_len;
941 }
942
943 /**
944 * Gets the Volume Set Identifier, as a 128-byte dstring (not decoded)
945 * WARNING This is not a null terminated string
946 * volsetid, place to put the data
947 * volsetid_size, size of the buffer volsetid points to
948 * the buffer should be >=128 bytes to store the whole volumesetidentifier
949 * returns the size of the available volsetid information (128)
950 * or 0 on error
951 */
952 int UDFGetVolumeSetIdentifier(dvd_reader_t *device, uint8_t *volsetid,
953 unsigned int volsetid_size)
954 {
955 struct pvd_t pvd;
956
957 /* get primary volume descriptor */
958 if(!UDFGetPVD(device, &pvd))
959 return 0;
960
961
962 if(volsetid_size > 128)
963 volsetid_size = 128;
964
965 memcpy(volsetid, pvd.VolumeSetIdentifier, volsetid_size);
966 return 128;
967 }