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