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