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