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