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