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