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