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