]> git.sesse.net Git - vlc/blob - plugins/dvd/dvd_udf.c
* Mandatory step for video output IV and the audio output quality
[vlc] / plugins / dvd / dvd_udf.c
1 /*****************************************************************************
2  * dvd_udf.c: udf filesystem tools.
3  *****************************************************************************
4  * Mainly used to find asolute logical block adress of *.ifo files. It only
5  * contains the basic udf handling functions
6  *****************************************************************************
7  * Copyright (C) 1998-2001 VideoLAN
8  * $Id: dvd_udf.c,v 1.6 2001/04/28 03:36:25 sam Exp $
9  *
10  * Author: Stéphane Borel <stef@via.ecp.fr>
11  *
12  * based on:
13  *  - dvdudf by Christian Wolff <scarabaeus@convergence.de>
14  *  - fixes by Billy Biggs <vektor@dumbterm.net>
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2 of the License, or
19  * (at your option) any later version.
20  * 
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
29  *****************************************************************************/
30
31 /*
32  * Preamble
33  */
34 #include "defs.h"
35
36 #ifdef HAVE_CSS
37 #define MODULE_NAME dvd
38 #else /* HAVE_CSS */
39 #define MODULE_NAME dvdnocss
40 #endif /* HAVE_CSS */
41 #include "modules_inner.h"
42
43 #include <stdio.h>
44 #include <unistd.h>
45 #include <string.h>
46 #ifdef STRNCASECMP_IN_STRINGS_H
47 #   include <strings.h>
48 #endif
49 #include <fcntl.h>
50
51 #include "common.h"
52 #include "intf_msg.h"
53 #include "dvd_css.h"
54 #include "dvd_ifo.h"
55 #include "input_dvd.h"
56
57 #define UDFADshort      1
58 #define UDFADlong       2
59 #define UDFADext        4
60
61 typedef struct partition_s
62 {
63     boolean_t   b_valid;
64     u8          pi_volume_desc[128];
65     u16         i_flags;
66     u16         i_number;
67     u8          pi_contents[32];
68     u32         i_access_type;
69     u32         i_start;
70     u32         i_length;
71     int         i_fd;
72 } partition_t;
73
74 typedef struct ad_s
75 {
76     u32         i_location;
77     u32         i_length;
78     u8          i_flags;
79     u16         i_partition;
80 } ad_t;
81
82 /* for direct data access, LSB first */
83 #define GETN1(p) ((u8)pi_data[p])
84 #define GETN2(p) ((u16)pi_data[p]|((u16)pi_data[(p)+1]<<8))
85 #define GETN4(p) ((u32)pi_data[p]|((u32)pi_data[(p)+1]<<8)|((u32)pi_data[(p)+2]<<16)|((u32)pi_data[(p)+3]<<24))
86 #define GETN(p,n,target) memcpy(target,&pi_data[p],n)
87
88
89 /*****************************************************************************
90  * UDFReadLB: reads absolute Logical Block of the disc
91  *****************************************************************************
92  * Returns number of read bytes on success, 0 on error
93  *****************************************************************************/
94 static int UDFReadLB( int i_fd, off_t i_lba, size_t i_block_count, u8 *pi_data )
95 {
96     if( i_fd < 0 )
97     {
98         return 0;
99     }
100
101     if( lseek( i_fd, i_lba * (off_t) DVD_LB_SIZE, SEEK_SET ) < 0 )
102     {
103         intf_ErrMsg( "UDF: Postion not found" );
104         return 0;
105     }
106
107     return read( i_fd, pi_data, i_block_count *DVD_LB_SIZE);
108 }
109
110
111 /*****************************************************************************
112  * UDFDecode: decode unicode encoded udf data
113  *****************************************************************************/
114 static int UDFDecode( u8 * pi_data, int i_len, char * psz_target )
115 {
116     int     p = 1;
117     int     i = 0;
118
119     if( !( pi_data[0] & 0x18 ) )
120     {
121         psz_target[0] = '\0';
122         return 0;
123     }
124
125     if( pi_data[0] & 0x10 )
126     {
127         /* ignore MSB of unicode16 */
128         p++;
129
130         while( p < i_len )
131         {
132             psz_target[i++] = pi_data[p+=2];
133         }
134     }
135     else
136     {
137         while( p < i_len )
138         {
139             psz_target[i++] = pi_data[p++];
140         }
141     }
142     
143     psz_target[i]='\0';
144
145     return 0;
146 }
147
148 #if 0
149 /**
150  *
151  **/
152
153 int UDFEntity (u8 *data, u8 *Flags, char *Identifier)
154 {
155     Flags[0] = data[0];
156     strncpy (Identifier, &data[1], 5);
157
158     return 0;
159 }
160 #endif
161
162
163 /*****************************************************************************
164  * UDFDescriptor: gives a tag ID from your data to find out what it refers to
165  *****************************************************************************/
166 static int UDFDescriptor( u8 * pi_data, u16 * pi_tag_id )
167 {
168     pi_tag_id[0] = GETN2( 0 );
169     /* TODO: check CRC 'n stuff */
170
171     return 0;
172 }
173
174
175 /*****************************************************************************
176  * UDFExtendAD: main volume information
177  *****************************************************************************/
178 static int UDFExtentAD (u8 * pi_data, u32 * pi_length, u32 * pi_location)
179 {
180     pi_length[0] = GETN4( 0 );
181     pi_location[0] = GETN4( 4 );
182
183     return 0;
184 }
185
186
187 /*****************************************************************************
188  * UDFAD: file set information
189  *****************************************************************************/
190 static int UDFAD( u8 * pi_data, struct ad_s * p_ad, u8 i_type,
191                   struct partition_s partition )
192 {
193     p_ad->i_length = GETN4( 0 );
194     p_ad->i_flags = p_ad->i_length >> 30;
195     p_ad->i_length &= 0x3FFFFFFF;
196
197     switch( i_type )
198     {
199         case UDFADshort:
200             p_ad->i_location = GETN4( 4 );
201             /* use number of current partition */
202             p_ad->i_partition = partition.i_number;
203              break;
204
205         case UDFADlong:
206             p_ad->i_location = GETN4( 4 );
207             p_ad->i_partition = GETN2( 8 );
208             break;
209
210         case UDFADext:
211             p_ad->i_location = GETN4( 12 );
212             p_ad->i_partition = GETN2( 16 );
213             break;
214     }
215
216     return 0;
217 }
218
219
220 /*****************************************************************************
221  * UDFICB: takes Information Control Block from pi_data
222  *****************************************************************************/
223 static int UDFICB( u8 * pi_data, u8 * pi_file_type, u16 * pi_flags)
224 {
225     pi_file_type[0] = GETN1( 11 );
226     pi_flags[0] = GETN2( 18 );
227
228     return 0;
229 }
230
231
232 /*****************************************************************************
233  * UDFPartition: gets partition descriptor
234  *****************************************************************************/
235 static int UDFPartition( u8 * pi_data, u16 * pi_flags, u16 * pi_nb,
236                          char * ps_contents, u32 * pi_start, u32 * pi_length )
237 {
238     pi_flags[0] = GETN2( 20 );
239     pi_nb[0] = GETN2( 22 );
240     GETN( 24, 32, ps_contents );
241     pi_start[0] = GETN4( 188 );
242     pi_length[0] = GETN4( 192 );
243
244     return 0;
245 }
246
247
248 /*****************************************************************************
249  * UDFLogVolume: reads the volume descriptor and checks the parameters
250  *****************************************************************************
251  * Returns 0 on OK, 1 on error
252  *****************************************************************************/
253 static int UDFLogVolume(u8 * pi_data, char * p_volume_descriptor )
254 {
255     u32 i_lb_size;
256     u32 i_MT_L;
257     u32 i_N_PM;
258
259     UDFDecode( &pi_data[84], 128, p_volume_descriptor );
260
261     i_lb_size = GETN4( 212 );        // should be 2048
262     i_MT_L = GETN4( 264 );        // should be 6
263     i_N_PM = GETN4( 268 );        // should be 1
264
265     if( i_lb_size != DVD_LB_SIZE )
266     {
267         intf_ErrMsg( "UDF: Non valid sector size (%d)", i_lb_size );
268         return 1;
269     }
270
271     return 0;
272 }
273
274
275 /*****************************************************************************
276  * UDFFileEntry: fills a ad_t struct with information at pi_data
277  *****************************************************************************/
278 static int UDFFileEntry( u8 * pi_data, u8 * pi_file_type, struct ad_s * p_ad,
279                          struct partition_s partition )
280 {
281     u8      i_file_type;
282     u16     i_flags;
283     u32     i_L_EA;
284     u32     i_L_AD;
285     int     p;
286
287     UDFICB( &pi_data[16], &i_file_type, &i_flags );
288
289     pi_file_type[0] = i_file_type;
290     i_L_EA = GETN4( 168 );
291     i_L_AD = GETN4( 172 );
292     p = 176 + i_L_EA;
293
294     while( p < 176 + i_L_EA + i_L_AD )
295     {
296         switch( i_flags & 0x07 )
297         {
298         case 0:
299             UDFAD( &pi_data[p], p_ad, UDFADshort, partition );
300             p += 0x08;
301             break;
302         case 1:
303             UDFAD( &pi_data[p], p_ad, UDFADlong, partition );
304             p += 0x10;
305             break;
306         case 2:
307             UDFAD( &pi_data[p], p_ad, UDFADext, partition );
308             p += 0x14;
309             break;
310         case 3:
311             switch( i_L_AD )
312             {
313             case 0x08:
314                 UDFAD( &pi_data[p], p_ad, UDFADshort, partition );
315                 break;
316             case 0x10:
317                 UDFAD( &pi_data[p], p_ad, UDFADlong, partition );
318                 break;
319             case 0x14:
320                 UDFAD( &pi_data[p], p_ad, UDFADext, partition );
321                 break;
322             }
323         default:
324             p += i_L_AD;
325             break;
326         }
327     }
328
329     return 0;
330 }
331
332
333 /*****************************************************************************
334  * UDFFileIdentifier: gives filename and characteristics of pi_data
335  *****************************************************************************/
336 static int UDFFileIdentifier( u8 * pi_data, u8 * pi_file_characteristics,
337                               char * psz_filename, struct ad_s * p_file_icb,
338                               struct partition_s partition )
339 {
340     u8      i_L_FI;
341     u16     i_L_IU;
342   
343     pi_file_characteristics[0] = GETN1( 18 );
344     i_L_FI = GETN1( 19 );
345     UDFAD( &pi_data[20], p_file_icb, UDFADlong, partition );
346     i_L_IU = GETN2( 36 );
347
348     if( i_L_FI )
349     {
350         UDFDecode( &pi_data[38+i_L_IU], i_L_FI, psz_filename );
351     }
352     else
353     {
354         psz_filename[0]='\0';
355     }
356
357     return  4 * ( ( 38 + i_L_FI + i_L_IU + 3 ) / 4 );
358 }
359
360
361 /*****************************************************************************
362  * UDFMapICB: Maps ICB to FileAD
363  *****************************************************************************
364  * ICB: Location of ICB of directory to scan
365  * FileType: Type of the file
366  * File: Location of file the ICB is pointing to
367  * return 1 on success, 0 on error;
368  *****************************************************************************/
369 static int UDFMapICB( struct ad_s icb, u8 * pi_file_type, struct ad_s * p_file,
370                       struct partition_s partition )
371 {
372     u8      pi_lb[DVD_LB_SIZE];
373     u32     i_lba;
374     u16     i_tag_id;
375
376     i_lba = partition.i_start + icb.i_location;
377
378     do
379     {
380         if( !UDFReadLB( partition.i_fd, i_lba++, 1, pi_lb ) )
381         {
382             i_tag_id = 0;
383         }
384         else
385         {
386             UDFDescriptor( pi_lb , &i_tag_id );
387         }
388
389         if( i_tag_id == 261 )
390         {
391             UDFFileEntry( pi_lb, pi_file_type, p_file, partition );
392             return 1;
393         }
394
395     } while( ( i_lba <= partition.i_start + icb.i_location +
396                ( icb.i_length - 1 ) / DVD_LB_SIZE ) && ( i_tag_id != 261 ) );
397
398     return 0;
399 }
400
401 /*****************************************************************************
402  * UDFScanDir: serach filename in dir
403  *****************************************************************************
404  * Dir: Location of directory to scan
405  * FileName: Name of file to look for
406  * FileICB: Location of ICB of the found file
407  * return 1 on success, 0 on error;
408  *****************************************************************************/
409 static int UDFScanDir( struct ad_s dir, char * psz_filename,
410                        struct ad_s * p_file_icb, struct partition_s partition )
411 {
412     u8      pi_lb[2*DVD_LB_SIZE];
413     u32     i_lba;
414     u16     i_tag_id;
415     u8      i_file_char;
416     char    psz_temp[DVD_LB_SIZE];
417     int     p;
418   
419     /* Scan dir for ICB of file */
420     i_lba = partition.i_start + dir.i_location;
421 #if 0
422     do
423     {
424         if( !UDFReadLB( partition.i_fd, i_lba++, 1, pi_lb ) )
425         {
426             i_tag_id = 0;
427         }
428         else
429         {
430             p=0;
431             while( p < DVD_LB_SIZE )
432             {
433                 UDFDescriptor( &pi_lb[p], &i_tag_id );
434
435                 if( i_tag_id == 257 )
436                 {
437                     p += UDFFileIdentifier( &pi_lb[p], &i_file_char,
438                                             psz_temp, p_file_icb, partition );
439                     if( !strcasecmp( psz_filename, psz_temp ) )
440                     {
441                         return 1;
442                     }
443                 }
444                 else
445                 {
446                     p = DVD_LB_SIZE;
447                 }
448             }
449         }
450
451     } while( i_lba <=
452       partition.i_start + dir.i_location + ( dir.i_length - 1 ) / DVD_LB_SIZE );
453
454 #else
455
456     if( UDFReadLB( partition.i_fd, i_lba, 2, pi_lb ) <= 0 ) {
457         return 0;
458     }
459
460     p = 0;
461     while( p < dir.i_length )
462     {
463         if( p > DVD_LB_SIZE )
464         {
465             ++i_lba;
466             p -= DVD_LB_SIZE;
467             dir.i_length -= DVD_LB_SIZE;
468             if( UDFReadLB( partition.i_fd, i_lba, 2, pi_lb ) <= 0 )
469             {
470                 return 0;
471             }
472         }
473
474         UDFDescriptor( &pi_lb[p], &i_tag_id );
475
476         if( i_tag_id == 257 )
477         {
478             p += UDFFileIdentifier( &pi_lb[p], &i_file_char,
479                                     psz_temp, p_file_icb, partition );
480             if( !strcasecmp( psz_filename, psz_temp ) )
481             {
482                 return 1;
483             }
484         }
485         else
486         {
487             return 0;
488         }
489     }
490
491 #endif
492     return 0;
493 }
494
495 /*****************************************************************************
496  * UDFFindPartition: looks for a partition on the disc
497  *****************************************************************************
498  *   partnum: number of the partition, starting at 0
499  *   part: structure to fill with the partition information
500  *   return 1 if partition found, 0 on error;
501  *****************************************************************************/
502 static int UDFFindPartition( int i_part_nb, struct partition_s *p_partition )
503 {
504     u8          pi_lb[DVD_LB_SIZE];
505     u8          pi_anchor[DVD_LB_SIZE];
506     u16         i_tag_id;
507     u32         i_lba;
508     u32         i_MVDS_location;
509     u32         i_MVDS_length;
510     u32         i_last_sector;
511     boolean_t   b_term;
512     boolean_t   b_vol_valid;
513     int         i;
514
515     /* Find Anchor */
516     i_last_sector = 0;
517  
518     /* try #1, prime anchor */
519     i_lba = 256;    
520     b_term = 0;
521
522     /* Search anchor loop */
523     while( 1 )
524     {
525         if( UDFReadLB( p_partition->i_fd, i_lba, 1, pi_anchor ) )
526         {
527             UDFDescriptor( pi_anchor, &i_tag_id );
528         }
529         else
530         {
531             i_tag_id = 0;
532         }
533
534         if( i_tag_id != 2 )
535         {                
536             /* not an anchor? */
537             if( b_term )
538             {
539                 /* final try failed */
540                 return 0;
541             }
542
543             if( i_last_sector )
544             {
545                 /* we already found the last sector
546                  * try #3, alternative backup anchor */
547                 i_lba = i_last_sector;    
548             
549                 /* but that's just about enough, then! */
550                 b_term = 1;            
551             }
552             else
553             {
554                 /* TODO: find last sector of the disc (this is optional) */
555                 if( i_last_sector )
556                 {
557                     /* try #2, backup anchor */
558                     i_lba = i_last_sector - 256;                
559                 }
560                 else
561                 {
562                     /* unable to find last sector */
563                     return 0;
564                 }
565             }
566         }
567         else
568         {
569             /* it is an anchor! continue... */
570             break;
571         }
572     }
573
574     /* main volume descriptor */
575     UDFExtentAD( &pi_anchor[16], &i_MVDS_length, &i_MVDS_location );
576   
577     p_partition->b_valid = 0;
578     b_vol_valid = 0;
579     p_partition->pi_volume_desc[0] = '\0';
580
581     i = 1;
582     /* Find Volume Descriptor */
583     do
584     {
585         i_lba = i_MVDS_location;
586
587         do
588         {
589             if( !UDFReadLB( p_partition->i_fd, i_lba++, 1, pi_lb ) )
590             {
591                 i_tag_id = 0;
592             }
593             else
594             {
595                 UDFDescriptor( pi_lb, &i_tag_id );
596             }
597
598             if( ( i_tag_id == 5 ) && ( !p_partition->b_valid ) )
599             {
600                 /* Partition Descriptor */
601                 UDFPartition( pi_lb,
602                               &p_partition->i_flags,
603                               &p_partition->i_number,
604                               p_partition->pi_contents,
605                               &p_partition->i_start,
606                               &p_partition->i_length );
607                 p_partition->b_valid = ( i_part_nb == p_partition->i_number );
608             }
609             else if( ( i_tag_id == 6 ) && ( !b_vol_valid) )
610             {
611                 /* Logical Volume Descriptor */
612                 if( UDFLogVolume( pi_lb , p_partition->pi_volume_desc ) )
613                 {  
614                 /* TODO: sector size wrong! */
615                 }
616                 else
617                 {
618                     b_vol_valid = 1;
619                 }
620             }
621
622         } while( ( i_lba <= i_MVDS_location +
623                    ( i_MVDS_length - 1 ) / DVD_LB_SIZE )
624                  && ( i_tag_id != 8 )
625                  && ( ( !p_partition->b_valid ) || ( !b_vol_valid ) ) );
626     
627         if( ( !p_partition->b_valid ) || ( !b_vol_valid ) )
628         {
629             /* backup volume descriptor */
630             UDFExtentAD( &pi_anchor[24], &i_MVDS_length, &i_MVDS_location );
631         }
632     } while( i-- && ( ( !p_partition->b_valid ) || ( !b_vol_valid ) ) );
633
634     /* we only care for the partition, not the volume */
635     return( p_partition->b_valid);
636 }
637
638
639 /*****************************************************************************
640  * UDFFindFile: looks for a file on the UDF disc/imagefile
641  *****************************************************************************
642  * Path has to be the absolute pathname on the UDF filesystem,
643  * starting with '/'.
644  * returns absolute LB number, or 0 on error
645  *****************************************************************************/
646 u32 UDFFindFile( int i_fd, char * psz_path )
647 {
648     struct partition_s  partition;
649     struct ad_s         root_icb;
650     struct ad_s         file;
651     struct ad_s         icb;
652     u32                 i_lba;
653     u16                 i_tag_id;
654     u8                  pi_lb[DVD_LB_SIZE];
655     u8                  i_file_type;
656     char                psz_tokenline[DVD_LB_SIZE] = "";
657     char *              psz_token;
658     int                 i_partition;
659  
660     strcat( psz_tokenline, psz_path );
661
662     /* Init file descriptor of UDF filesystem (== DVD) */
663     partition.i_fd = i_fd;
664
665     /* Find partition 0, standard partition for DVD-Video */
666     i_partition = 0;
667     if( !UDFFindPartition( i_partition, &partition ) )
668     {
669         intf_ErrMsg( "UDF: Partition 0 not found" );
670         return 0;
671     }
672   
673     /* Find root dir ICB */
674     i_lba = partition.i_start;
675
676     do
677     {
678         if( !UDFReadLB( i_fd, i_lba++, 1, pi_lb ) )
679         {
680             i_tag_id = 0;
681         }
682         else
683         {
684             UDFDescriptor( pi_lb, &i_tag_id );
685         }
686
687         if( i_tag_id == 256 )
688         {
689             /* File Set Descriptor */
690             UDFAD( &pi_lb[400], &root_icb, UDFADlong, partition );
691         }
692
693     } while( ( i_lba < partition.i_start + partition.i_length )
694           && ( i_tag_id != 8) && ( i_tag_id != 256 ) );
695
696     if( i_tag_id != 256 )
697     {
698         intf_ErrMsg( "UDF: Bad descriptor" );
699         return 0;
700     }
701     if( root_icb.i_partition != i_partition )
702     {
703         intf_ErrMsg( "UDF: Bad partition" );
704         return 0;
705     }
706   
707     /* Find root dir */
708     if( !UDFMapICB( root_icb, &i_file_type, &file, partition ) )
709     {
710         intf_ErrMsg( "UDF: Can't find root dir" );
711         return 0;
712     }
713
714     /* root dir should be dir */
715     if( i_file_type != 4 )
716     {
717         intf_ErrMsg( "UDF: Root dir error" );
718         return 0;
719     }
720
721     /* Tokenize filepath */
722     psz_token = strtok( psz_tokenline, "/" );
723     while( psz_token )
724     {
725         if( !UDFScanDir( file, psz_token, &icb, partition ) )
726         {
727             intf_ErrMsg( "UDF: Scan dir error" );
728             return 0;
729         }
730
731         if( !UDFMapICB ( icb, &i_file_type, &file, partition ) )
732         {
733             intf_ErrMsg( "UDF: ICB error" );
734             return 0;
735         }
736
737         psz_token = strtok( NULL, "/" );
738     }
739
740     return partition.i_start + file.i_location;
741 }
742