]> git.sesse.net Git - vlc/blob - modules/access/vcdx/access.c
VCDX: use after free
[vlc] / modules / access / vcdx / access.c
1 /*****************************************************************************
2  * vcd.c : VCD input module for vlc using libcdio, libvcd and libvcdinfo.
3  *         vlc-specific things tend to go here.
4  *****************************************************************************
5  * Copyright (C) 2000, 2003, 2004, 2005 the VideoLAN team
6  * $Id$
7  *
8  * Authors: Rocky Bernstein <rocky@panix.com>
9  *   Some code is based on the non-libcdio VCD plugin (as there really
10  *   isn't real developer documentation yet on how to write a
11  *   navigable plugin.)
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License along
24  * with this program; if not, write to the Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <vlc_common.h>
37 #include <vlc_interface.h>
38 #include <vlc_input.h>
39 #include <vlc_access.h>
40 #include <vlc_charset.h>
41
42 #include <cdio/cdio.h>
43 #include <cdio/cd_types.h>
44 #include <cdio/logging.h>
45 #include <cdio/util.h>
46 #include <libvcd/info.h>
47 #include <libvcd/logging.h>
48 #include "vcd.h"
49 #include "info.h"
50 #include "access.h"
51
52 /*****************************************************************************
53  * Local prototypes
54  *****************************************************************************/
55
56 /* First those which are accessed from outside (via pointers). */
57 static block_t *VCDReadBlock    ( access_t * );
58
59 static int      VCDControl      ( access_t *p_access, int i_query,
60                                   va_list args );
61
62 /* Now those which are strictly internal */
63 static bool  VCDEntryPoints  ( access_t * );
64 static bool  VCDLIDs         ( access_t * );
65 static bool  VCDSegments     ( access_t * );
66 static int  VCDTitles       ( access_t * );
67 static char *VCDParse       ( access_t *,
68                               /*out*/ vcdinfo_itemid_t * p_itemid ,
69                               /*out*/ bool *play_single_item );
70
71 static void VCDUpdateVar( access_t *p_access, int i_entry, int i_action,
72                           const char *p_varname, char *p_label,
73                           const char *p_debug_label );
74
75 static vcdinfo_obj_t *vcd_Open   ( vlc_object_t *p_this, const char *psz_dev );
76
77 /****************************************************************************
78  * Private functions
79  ****************************************************************************/
80
81 /* FIXME: This variable is a hack. Would be nice to eliminate the
82    global-ness. */
83
84 static access_t *p_vcd_access = NULL;
85
86 /* process messages that originate from libcdio. */
87 static void
88 cdio_log_handler (cdio_log_level_t level, const char message[])
89 {
90     const vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_vcd_access->p_sys;
91     switch (level) {
92     case CDIO_LOG_DEBUG:
93     case CDIO_LOG_INFO:
94         if (p_vcdplayer->i_debug & INPUT_DBG_CDIO)
95             msg_Dbg( p_vcd_access, "%s", message);
96         break;
97     case CDIO_LOG_WARN:
98         msg_Warn( p_vcd_access, "%s", message);
99         break;
100     case CDIO_LOG_ERROR:
101     case CDIO_LOG_ASSERT:
102         msg_Err( p_vcd_access, "%s", message);
103         break;
104     default:
105         msg_Warn( p_vcd_access, "%s\n%s %d", message,
106                   "The above message had unknown log level", level);
107     }
108     return;
109 }
110
111 /* process messages that originate from vcdinfo. */
112 static void
113 vcd_log_handler (vcd_log_level_t level, const char message[])
114 {
115     vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_vcd_access->p_sys;
116     switch (level) {
117     case VCD_LOG_DEBUG:
118     case VCD_LOG_INFO:
119         if (p_vcdplayer->i_debug & INPUT_DBG_VCDINFO)
120             msg_Dbg( p_vcd_access, "%s", message);
121         break;
122     case VCD_LOG_WARN:
123         msg_Warn( p_vcd_access, "%s", message);
124         break;
125     case VCD_LOG_ERROR:
126     case VCD_LOG_ASSERT:
127         msg_Err( p_vcd_access, "%s", message);
128         break;
129     default:
130         msg_Warn( p_vcd_access, "%s\n%s %d", message,
131                   "The above message had unknown vcdimager log level", level);
132     }
133     return;
134 }
135
136 /*****************************************************************************
137   VCDRead: reads VCD_BLOCKS_ONCE from the VCD and returns that.
138   NULL is returned if something went wrong.
139  *****************************************************************************/
140 static block_t *
141 VCDReadBlock( access_t * p_access )
142 {
143     vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
144     const int    i_blocks   = p_vcdplayer->i_blocks_per_read;
145     block_t     *p_block;
146     int          i_read;
147     uint8_t     *p_buf;
148
149     dbg_print( (INPUT_DBG_LSN), "lsn: %lu",
150                (long unsigned int) p_vcdplayer->i_lsn );
151
152     /* Allocate a block for the reading */
153     if( !( p_block = block_New( p_access, i_blocks * M2F2_SECTOR_SIZE ) ) )
154     {
155         msg_Err( p_access, "cannot get a new block of size: %i",
156                  i_blocks * M2F2_SECTOR_SIZE );
157         block_Release( p_block );
158         return NULL;
159     }
160
161     p_buf = (uint8_t *) p_block->p_buffer;
162     for ( i_read = 0 ; i_read < i_blocks ; i_read++ )
163     {
164         vcdplayer_read_status_t read_status = vcdplayer_read(p_access, p_buf);
165
166         p_access->info.i_pos += M2F2_SECTOR_SIZE;
167
168         switch ( read_status ) {
169         case READ_END:
170             /* End reached. Return NULL to indicated this. */
171             /* We also set the postion to the end so the higher level
172                (demux?) doesn't try to keep reading. If everything works out
173                right this shouldn't have to happen.
174              */
175 #if 0
176             if( p_access->info.i_pos != p_access->info.i_size ) {
177                 msg_Warn( p_access,
178                     "At end but pos (%llu) is not size (%llu). Adjusting.",
179                     p_access->info.i_pos, p_access->info.i_size );
180                 p_access->info.i_pos = p_access->info.i_size;
181             }
182 #endif
183
184             block_Release( p_block );
185             return NULL;
186
187         case READ_ERROR:
188             /* Some sort of error. Should we increment lsn? to skip block? */
189             block_Release( p_block );
190             return NULL;
191         case READ_STILL_FRAME:
192           /* FIXME The below should be done in an event thread.
193              Until then...
194            */
195 #if 1
196             msleep( MILLISECONDS_PER_SEC * *p_buf );
197             VCDSetOrigin(p_access, p_vcdplayer->origin_lsn,
198                          p_vcdplayer->i_track, &(p_vcdplayer->play_item));
199             // p_vcd->in_still = false;
200             dbg_print(INPUT_DBG_STILL, "still wait time done");
201 #else
202             vcdIntfStillTime(p_vcdplayer->p_intf, *p_buf);
203 #endif
204
205             block_Release( p_block );
206             return NULL;
207
208         default:
209         case READ_BLOCK:
210             /* Read buffer */
211             break;
212         }
213
214         p_buf += M2F2_SECTOR_SIZE;
215         /* Update seekpoint */
216         if ( VCDINFO_ITEM_TYPE_ENTRY == p_vcdplayer->play_item.type )
217         {
218             size_t i_entry = p_vcdplayer->play_item.num+1;
219             lsn_t  i_lsn   = vcdinfo_get_entry_lsn(p_vcdplayer->vcd, i_entry);
220             if ( p_vcdplayer->i_lsn >= i_lsn && i_lsn != VCDINFO_NULL_LSN )
221             {
222                 dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
223                            "entry change to %zu, current LSN %u >= end %u",
224                            i_entry, p_vcdplayer->i_lsn, i_lsn);
225
226                 p_vcdplayer->play_item.num = i_entry;
227
228                 VCDSetOrigin( p_access,  i_lsn, p_vcdplayer->i_track,
229                               &(p_vcdplayer->play_item) );
230             }
231         }
232     }
233
234     return p_block;
235 }
236
237
238 /****************************************************************************
239  * VCDSeek
240  ****************************************************************************/
241 int
242 VCDSeek( access_t * p_access, int64_t i_pos )
243 {
244     if (!p_access || !p_access->p_sys) return VLC_EGENERIC;
245     {
246         vcdplayer_t         *p_vcdplayer = (vcdplayer_t *)p_vcd_access->p_sys;
247         const input_title_t *t = p_vcdplayer->p_title[p_access->info.i_title];
248         unsigned int         i_entry = VCDINFO_INVALID_ENTRY;
249         int i_seekpoint;
250
251         /* Next sector to read */
252         p_access->info.i_pos = i_pos;
253         p_vcdplayer->i_lsn = (i_pos / (int64_t) M2F2_SECTOR_SIZE) +
254                              p_vcdplayer->origin_lsn;
255
256         switch (p_vcdplayer->play_item.type)
257         {
258         case VCDINFO_ITEM_TYPE_TRACK:
259         case VCDINFO_ITEM_TYPE_ENTRY:
260             break;
261         default:
262             p_vcdplayer->b_valid_ep = false;
263             break;
264         }
265
266         /* Find entry */
267         if( p_vcdplayer->b_valid_ep )
268         {
269             for( i_entry = 0 ; i_entry < p_vcdplayer->i_entries ; i_entry ++ )
270             {
271                 if( p_vcdplayer->i_lsn < p_vcdplayer->p_entries[i_entry] )
272                 {
273                     VCDUpdateVar( p_access, i_entry, VLC_VAR_SETVALUE,
274                                   "chapter", _("Entry"), "Setting entry" );
275                     break;
276                 }
277             }
278
279             {
280                 vcdinfo_itemid_t itemid;
281                 itemid.num  = i_entry;
282                 itemid.type = VCDINFO_ITEM_TYPE_ENTRY;
283                 VCDSetOrigin(p_access, p_vcdplayer->i_lsn,
284                              p_vcdplayer->i_track, &itemid);
285             }
286         }
287
288         dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
289                    "orig %lu, cur: %lu, offset: %lld, entry %d",
290                    (long unsigned int) p_vcdplayer->origin_lsn,
291                    (long unsigned int) p_vcdplayer->i_lsn, i_pos,
292                    i_entry );
293  
294         /* Find seekpoint */
295         for( i_seekpoint = 0; i_seekpoint < t->i_seekpoint; i_seekpoint++ )
296         {
297             if( i_seekpoint + 1 >= t->i_seekpoint ) break;
298             if( i_pos < t->seekpoint[i_seekpoint + 1]->i_byte_offset ) break;
299         }
300  
301         /* Update current seekpoint */
302         if( i_seekpoint != p_access->info.i_seekpoint )
303         {
304             dbg_print( (INPUT_DBG_SEEK), "seekpoint change %lu",
305                        (long unsigned int) i_seekpoint );
306             p_access->info.i_update |= INPUT_UPDATE_SEEKPOINT;
307             p_access->info.i_seekpoint = i_seekpoint;
308         }
309     }
310     p_access->info.b_eof = false;
311     return VLC_SUCCESS;
312 }
313
314 /*****************************************************************************
315   VCDEntryPoints: Reads the information about the entry points on the disc
316   and initializes area information with that.
317   Before calling this track information should have been read in.
318  *****************************************************************************/
319 static bool
320 VCDEntryPoints( access_t * p_access )
321 {
322     if (!p_access || !p_access->p_sys) return false;
323  
324     vcdplayer_t       *p_vcdplayer = (vcdplayer_t *) p_access->p_sys;
325     const unsigned int i_entries   = vcdinfo_get_num_entries(p_vcdplayer->vcd);
326     const track_t      i_last_track
327            = cdio_get_num_tracks(vcdinfo_get_cd_image(p_vcdplayer->vcd))
328            + cdio_get_first_track_num(vcdinfo_get_cd_image(p_vcdplayer->vcd));
329     unsigned int i;
330  
331     if (0 == i_entries) {
332         LOG_ERR ("no entires found -- something is wrong" );
333         return false;
334     }
335  
336     p_vcdplayer->p_entries  = malloc( sizeof( lsn_t ) * i_entries );
337  
338     if( p_vcdplayer->p_entries == NULL )
339     {
340         LOG_ERR ("not enough memory for entry points treatment" );
341         return false;
342     }
343  
344     p_vcdplayer->i_entries = i_entries;
345  
346     for( i = 0 ; i < i_entries ; i++ )
347     {
348         const track_t i_track = vcdinfo_get_track(p_vcdplayer->vcd, i);
349         if( i_track <= i_last_track )
350         {
351             seekpoint_t *s = vlc_seekpoint_New();
352             char psz_entry[100];
353     
354             snprintf(psz_entry, sizeof(psz_entry), "%s %02d", _("Entry"), i );
355
356             p_vcdplayer->p_entries[i] =
357                                    vcdinfo_get_entry_lsn(p_vcdplayer->vcd, i);
358     
359             s->psz_name      = strdup(psz_entry);
360             s->i_byte_offset = (p_vcdplayer->p_entries[i]
361                              - vcdinfo_get_track_lsn(p_vcdplayer->vcd,i_track))
362                              * M2F2_SECTOR_SIZE;
363     
364             dbg_print( INPUT_DBG_MRL, "%s, lsn %d,  byte_offset %ld",
365                        s->psz_name, p_vcdplayer->p_entries[i],
366                        (unsigned long int) s->i_byte_offset);
367             TAB_APPEND( p_vcdplayer->p_title[i_track-1]->i_seekpoint,
368                         p_vcdplayer->p_title[i_track-1]->seekpoint, s );
369
370         } else
371             msg_Warn( p_access, "wrong track number found in entry points" );
372     }
373     p_vcdplayer->b_valid_ep = true;
374     return true;
375 }
376
377 /*****************************************************************************
378  * VCDSegments: Reads the information about the segments the disc.
379  *****************************************************************************/
380 static bool
381 VCDSegments( access_t * p_access )
382 {
383     vcdplayer_t   *p_vcdplayer = (vcdplayer_t *) p_access->p_sys;
384     unsigned int  i;
385     input_title_t *t;
386
387     p_vcdplayer->i_segments = vcdinfo_get_num_segments(p_vcdplayer->vcd);
388
389     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
390                "Segments: %d", p_vcdplayer->i_segments);
391
392     if ( 0 == p_vcdplayer->i_segments ) return false;
393
394     t = p_vcdplayer->p_title[p_vcdplayer->i_titles] = vlc_input_title_New();
395     p_vcdplayer->i_titles++;
396
397     t->i_size    = 0; /* Not sure Segments have a size associated */
398     t->psz_name  = strdup(_("Segments"));
399
400     /* We have one additional segment allocated so we can get the size
401        by subtracting seg[i+1] - seg[i].
402      */
403     p_vcdplayer->p_segments=malloc(sizeof(lsn_t)*(p_vcdplayer->i_segments+1));
404     if( p_vcdplayer->p_segments == NULL )
405     {
406         LOG_ERR ("not enough memory for segment treatment" );
407         return false;
408     }
409
410     for( i = 0 ; i < p_vcdplayer->i_segments ; i++ )
411     {
412         char psz_segment[100];
413         seekpoint_t *s = vlc_seekpoint_New();
414         p_vcdplayer->p_segments[i] = vcdinfo_get_seg_lsn(p_vcdplayer->vcd, i);
415
416         snprintf( psz_segment, sizeof(psz_segment), "%s %02d", _("Segment"),
417                   i );
418
419         s->i_byte_offset = 0; /* Not sure what this would mean here */
420         s->psz_name  = strdup(psz_segment);
421         TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
422     }
423
424     p_vcdplayer->p_segments[p_vcdplayer->i_segments] =
425       p_vcdplayer->p_segments[p_vcdplayer->i_segments-1]+
426       vcdinfo_get_seg_sector_count(p_vcdplayer->vcd,
427                                    p_vcdplayer->i_segments-1);
428
429     return true;
430 }
431
432 /*****************************************************************************
433  Build title table which will be returned via ACCESS_GET_TITLE_INFO.
434
435  We start area addressing for tracks at 1 since the default area 0
436  is reserved for segments.
437  *****************************************************************************/
438 static int
439 VCDTitles( access_t * p_access )
440 {
441     /* We'll assume a VCD has its first MPEG track
442        cdio_get_first_track_num()+1 could be used if one wanted to be
443        very careful about this. Note: cdio_get_first_track() will give the
444        ISO-9660 track before the MPEG tracks.
445      */
446  
447     if (!p_access || !p_access->p_sys) return VLC_EGENERIC;
448
449     {
450         vcdplayer_t *p_vcdplayer = (vcdplayer_t *) p_access->p_sys;
451         track_t      i;
452
453         p_vcdplayer->i_titles = 0;
454         for( i = 1 ; i <= p_vcdplayer->i_tracks ; i++ )
455         {
456             input_title_t *t = p_vcdplayer->p_title[i-1] =
457                                                         vlc_input_title_New();
458             char psz_track[80];
459
460             snprintf( psz_track, sizeof(psz_track), "%s %02d", _("Track"), i );
461             t->i_size    = (int64_t) vcdinfo_get_track_size( p_vcdplayer->vcd,
462                                  i ) * M2F2_SECTOR_SIZE / CDIO_CD_FRAMESIZE ;
463             t->psz_name  = strdup(psz_track);
464
465             dbg_print( INPUT_DBG_MRL, "track[%d] i_size: %lld", i, t->i_size );
466
467             p_vcdplayer->i_titles++;
468         }
469
470       return VLC_SUCCESS;
471     }
472 }
473
474 /*****************************************************************************
475   VCDLIDs: Reads the LIST IDs from the LOT.
476  *****************************************************************************/
477 static bool
478 VCDLIDs( access_t * p_access )
479 {
480     vcdplayer_t   *p_vcdplayer = (vcdplayer_t *) p_access->p_sys;
481     input_title_t *t;
482     unsigned int   i_lid, i_title;
483
484     p_vcdplayer->i_lids = vcdinfo_get_num_LIDs(p_vcdplayer->vcd);
485     p_vcdplayer->i_lid  = VCDINFO_INVALID_ENTRY;
486
487     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
488                "LIDs: %d", p_vcdplayer->i_lids);
489
490     if ( 0 == p_vcdplayer->i_lids ) return false;
491
492     if (vcdinfo_read_psd (p_vcdplayer->vcd)) {
493
494       vcdinfo_visit_lot (p_vcdplayer->vcd, false);
495
496 #ifdef FIXED
497     /*
498        We need to change libvcdinfo to be more robust when there are
499        problems reading the extended PSD. Given that area-highlighting and
500        selection features in the extended PSD haven't been implemented,
501        it's best then to not try to read this at all.
502      */
503       if (vcdinfo_get_psd_x_size(p_vcdplayer->vcd))
504         vcdinfo_visit_lot (p_vcdplayer->vcd, true);
505 #endif
506     }
507
508     /* Set up LIDs Navigation Menu */
509     t = vlc_input_title_New();
510     t->b_menu = true;
511     t->psz_name = strdup( "LIDs" );
512
513     i_title = p_vcdplayer->i_tracks;
514     for( i_lid =  1 ; i_lid <=  p_vcdplayer->i_lids ; i_lid++ )
515     {
516         char psz_lid[100];
517         seekpoint_t *s = vlc_seekpoint_New();
518
519         snprintf( psz_lid, sizeof(psz_lid), "%s %02d", _("LID"), i_lid );
520
521         s->i_byte_offset = 0; /*  A lid doesn't have an offset
522                                   size associated with it */
523         s->psz_name  = strdup(psz_lid);
524         TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
525     }
526
527 #ifdef DYNAMICALLY_ALLOCATED
528     TAB_APPEND( p_vcdplayer->i_titles, p_vcdplayer->p_title, t );
529 #else
530     p_vcdplayer->p_title[p_vcdplayer->i_titles] = t;
531     p_vcdplayer->i_titles++;
532 #endif
533
534     return true;
535 }
536
537 /*****************************************************************************
538  * VCDParse: parse command line
539  *****************************************************************************/
540 static char *
541 VCDParse( access_t * p_access, /*out*/ vcdinfo_itemid_t * p_itemid,
542           /*out*/ bool *play_single_item )
543 {
544     vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_access->p_sys;
545     char        *psz_parser;
546     char        *psz_source;
547     char        *psz_next;
548
549     if( config_GetInt( p_access, MODULE_STRING "-PBC" ) ) {
550       p_itemid->type = VCDINFO_ITEM_TYPE_LID;
551       p_itemid->num = 1;
552       *play_single_item = false;
553     }
554     else
555     {
556       p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
557       p_itemid->num = 0;
558     }
559
560 #ifdef WIN32
561     /* On Win32 we want the VCD access plugin to be explicitly requested,
562      * we end up with lots of problems otherwise */
563     if( !p_access->psz_access || !*p_access->psz_access ) return NULL;
564 #endif
565
566     if( !p_access->psz_path )
567     {
568         return NULL;
569     }
570
571     psz_parser = psz_source = strdup( p_access->psz_path );
572
573     /* Parse input string :
574      * [device][@[type][title]] */
575     while( *psz_parser && *psz_parser != '@' )
576     {
577         psz_parser++;
578     }
579
580     if( *psz_parser == '@' )
581     {
582       /* Found the divide between the source name and the
583          type+entry number. */
584       unsigned int num;
585
586         *psz_parser = '\0';
587         ++psz_parser;
588         if( *psz_parser )
589         switch(*psz_parser) {
590         case 'E':
591             p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
592             ++psz_parser;
593             *play_single_item = true;
594             break;
595         case 'P':
596             p_itemid->type = VCDINFO_ITEM_TYPE_LID;
597             ++psz_parser;
598             *play_single_item = false;
599             break;
600         case 'S':
601             p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
602             ++psz_parser;
603             *play_single_item = true;
604             break;
605         case 'T':
606             p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
607             ++psz_parser;
608             *play_single_item = true;
609             break;
610         default:
611             break;
612         }
613
614         num = strtol( psz_parser, &psz_next, 10 );
615         if ( *psz_parser != '\0' && *psz_next == '\0')
616         {
617             p_itemid->num = num;
618         }
619
620     } else {
621         *play_single_item = ( VCDINFO_ITEM_TYPE_LID == p_itemid->type );
622     }
623
624
625     if( !*psz_source )
626     {
627
628         /* No source specified, so figure it out. */
629         if( !p_access->psz_access ) return NULL;
630
631         psz_source = config_GetPsz( p_access, "vcd" );
632
633         if( !psz_source || 0==strlen(psz_source) )
634         {
635             free( psz_source );
636             /* Scan for a CD-ROM drive with a VCD in it. */
637             char **cd_drives = cdio_get_devices_with_cap(NULL,
638                                        (CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD
639                                        |CDIO_FS_ANAL_VIDEOCD|CDIO_FS_UNKNOWN),
640                                        true);
641             if( NULL == cd_drives ) return NULL;
642             if( cd_drives[0] == NULL )
643             {
644                 cdio_free_device_list( cd_drives );
645                 return NULL;
646             }
647             psz_source = strdup( cd_drives[0] );
648             cdio_free_device_list( cd_drives );
649         }
650     }
651
652     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
653                "source=%s entry=%d type=%d",
654                psz_source, p_itemid->num, p_itemid->type);
655
656     return psz_source;
657 }
658
659 /*
660    Sets start origin for subsequent seeks/reads
661 */
662 void
663 VCDSetOrigin( access_t *p_access, lsn_t i_lsn, track_t i_track,
664               const vcdinfo_itemid_t *p_itemid )
665 {
666     vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
667
668     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
669                "i_lsn: %lu, track: %d", (long unsigned int) i_lsn, i_track );
670
671     vcdplayer_set_origin(p_access, i_lsn, i_track, p_itemid);
672
673     switch (p_vcdplayer->play_item.type)
674     {
675     case VCDINFO_ITEM_TYPE_ENTRY:
676         VCDUpdateVar( p_access, p_itemid->num, VLC_VAR_SETVALUE,
677                       "chapter", _("Entry"), "Setting entry/segment");
678         p_access->info.i_title     = i_track-1;
679         if (p_vcdplayer->b_track_length)
680         {
681             p_access->info.i_size = p_vcdplayer->p_title[i_track-1]->i_size;
682             p_access->info.i_pos  = (int64_t) M2F2_SECTOR_SIZE *
683                      (vcdinfo_get_track_lsn(p_vcdplayer->vcd, i_track)-i_lsn);
684         } else {
685             p_access->info.i_size = M2F2_SECTOR_SIZE * (int64_t)
686                  vcdinfo_get_entry_sect_count(p_vcdplayer->vcd,p_itemid->num);
687             p_access->info.i_pos = 0;
688         }
689         dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), "size: %llu, pos: %llu",
690                    p_access->info.i_size, p_access->info.i_pos );
691         p_access->info.i_seekpoint = p_itemid->num;
692         break;
693
694     case VCDINFO_ITEM_TYPE_SEGMENT:
695         VCDUpdateVar( p_access, p_itemid->num, VLC_VAR_SETVALUE,
696                       "chapter", _("Segment"),  "Setting entry/segment");
697         /* The last title entry is the for segments (when segments exist
698            and they must here. The segment seekpoints are stored after
699            the entry seekpoints and (zeroed) lid seekpoints.
700         */
701         p_access->info.i_title     = p_vcdplayer->i_titles - 1;
702         p_access->info.i_size      = 0; /* No seeking on stills, please. */
703         p_access->info.i_pos       = 0;
704         p_access->info.i_seekpoint = p_vcdplayer->i_entries
705                                    + p_vcdplayer->i_lids + p_itemid->num;
706         break;
707
708     case VCDINFO_ITEM_TYPE_TRACK:
709         p_access->info.i_title     = i_track-1;
710         p_access->info.i_size      = p_vcdplayer->p_title[i_track-1]->i_size;
711         p_access->info.i_pos       = 0;
712         p_access->info.i_seekpoint = vcdinfo_track_get_entry(p_vcdplayer->vcd,
713                                                              i_track);
714         break;
715
716     default:
717         msg_Warn( p_access, "can't set origin for play type %d",
718                   p_vcdplayer->play_item.type );
719     }
720
721     p_access->info.i_update = INPUT_UPDATE_TITLE|INPUT_UPDATE_SIZE
722                               |INPUT_UPDATE_SEEKPOINT;
723
724     VCDUpdateTitle( p_access );
725
726 }
727
728 /*****************************************************************************
729  * vcd_Open: Opens a VCD device or file initializes, a list of
730    tracks, segements and entry lsns and sizes and returns an opaque handle.
731  *****************************************************************************/
732 static vcdinfo_obj_t *
733 vcd_Open( vlc_object_t *p_this, const char *psz_dev )
734 {
735     access_t    *p_access = (access_t *)p_this;
736     vcdplayer_t *p_vcdplayer    = (vcdplayer_t *) p_access->p_sys;
737     vcdinfo_obj_t *p_vcdobj;
738     char  *actual_dev;
739     unsigned int i;
740
741     dbg_print(INPUT_DBG_CALL, "called with %s", psz_dev);
742
743     if( !psz_dev ) return NULL;
744
745     actual_dev= ToLocaleDup(psz_dev);
746     if( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) !=
747                                                     VCDINFO_OPEN_VCD)
748     {
749         free(actual_dev);
750         return NULL;
751     }
752     free(actual_dev);
753
754     /*
755        Save summary info on tracks, segments and entries...
756     */
757
758     if ( 0 < (p_vcdplayer->i_tracks = vcdinfo_get_num_tracks(p_vcdobj)) )
759     {
760         p_vcdplayer->track = (vcdplayer_play_item_info_t *)
761           calloc(p_vcdplayer->i_tracks, sizeof(vcdplayer_play_item_info_t));
762
763         for (i=0; i<p_vcdplayer->i_tracks; i++)
764         {
765             unsigned int track_num=i+1;
766             p_vcdplayer->track[i].size  =
767             vcdinfo_get_track_sect_count(p_vcdobj, track_num);
768             p_vcdplayer->track[i].start_LSN =
769             vcdinfo_get_track_lsn(p_vcdobj, track_num);
770         }
771     } else
772         p_vcdplayer->track = NULL;
773
774     if( 0 < (p_vcdplayer->i_entries = vcdinfo_get_num_entries(p_vcdobj)) )
775     {
776         p_vcdplayer->entry = (vcdplayer_play_item_info_t *)
777             calloc(p_vcdplayer->i_entries, sizeof(vcdplayer_play_item_info_t));
778
779         for (i=0; i<p_vcdplayer->i_entries; i++)
780         {
781             p_vcdplayer->entry[i].size =
782                                     vcdinfo_get_entry_sect_count(p_vcdobj, i);
783             p_vcdplayer->entry[i].start_LSN =
784                                            vcdinfo_get_entry_lsn(p_vcdobj, i);
785         }
786     } else
787       p_vcdplayer->entry = NULL;
788
789     if ( 0 < (p_vcdplayer->i_segments = vcdinfo_get_num_segments(p_vcdobj)) )
790     {
791         p_vcdplayer->segment = (vcdplayer_play_item_info_t *)
792           calloc(p_vcdplayer->i_segments,  sizeof(vcdplayer_play_item_info_t));
793
794         for (i=0; i<p_vcdplayer->i_segments; i++)
795         {
796             p_vcdplayer->segment[i].size =
797                                     vcdinfo_get_seg_sector_count(p_vcdobj, i);
798             p_vcdplayer->segment[i].start_LSN =
799                                              vcdinfo_get_seg_lsn(p_vcdobj, i);
800         }
801     } else
802       p_vcdplayer->segment = NULL;
803
804     return p_vcdobj;
805 }
806
807 /****************************************************************************
808  Update the "varname" variable to i_num without triggering a callback.
809 ****************************************************************************/
810 static void
811 VCDUpdateVar( access_t *p_access, int i_num, int i_action,
812               const char *p_varname, char *p_label,
813               const char *p_debug_label)
814 {
815     vlc_value_t val;
816     val.i_int = i_num;
817     if( p_access )
818     {
819         const vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_vcd_access->p_sys;
820         dbg_print( INPUT_DBG_PBC, "%s %d", p_debug_label, i_num );
821     }
822     if( p_label )
823     {
824         vlc_value_t text;
825         text.psz_string = p_label;
826         var_Change( p_access, p_varname, VLC_VAR_SETTEXT, &text, NULL );
827     }
828     var_Change( p_access, p_varname, i_action, &val, NULL );
829 }
830
831
832 /*****************************************************************************
833  * Public routines.
834  *****************************************************************************/
835
836 /*****************************************************************************
837   VCDOpen: open VCD.
838   read in meta-information about VCD: the number of tracks, segments,
839   entries, size and starting information. Then set up state variables so
840   that we read/seek starting at the location specified.
841
842   On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
843   and VLC_EGENERIC for some other error.
844  *****************************************************************************/
845 int
846 VCDOpen ( vlc_object_t *p_this )
847 {
848     access_t         *p_access = (access_t *)p_this;
849     vcdplayer_t      *p_vcdplayer;
850     char             *psz_source;
851     vcdinfo_itemid_t  itemid;
852     bool        play_single_item = false;
853
854     p_access->pf_read          = NULL;
855     p_access->pf_block         = VCDReadBlock;
856     p_access->pf_control       = VCDControl;
857     p_access->pf_seek          = VCDSeek;
858
859     p_access->info.i_update    = 0;
860     p_access->info.i_size      = 0;
861     p_access->info.i_pos       = 0;
862     p_access->info.b_eof       = false;
863     p_access->info.i_title     = 0;
864     p_access->info.i_seekpoint = 0;
865
866     p_vcdplayer = malloc( sizeof(vcdplayer_t) );
867
868     if( p_vcdplayer == NULL )
869         return VLC_ENOMEM;
870
871     p_vcdplayer->i_debug = config_GetInt( p_this, MODULE_STRING "-debug" );
872     p_access->p_sys = (access_sys_t *) p_vcdplayer;
873
874     /* Set where to log errors messages from libcdio. */
875     p_vcd_access = p_access;
876     cdio_log_set_handler ( cdio_log_handler );
877     vcd_log_set_handler ( vcd_log_handler );
878
879     psz_source = VCDParse( p_access, &itemid, &play_single_item );
880
881     if ( NULL == psz_source )
882     {
883         free( p_vcdplayer );
884         return( VLC_EGENERIC );
885     }
886
887     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "source: %s: mrl: %s",
888                psz_source, p_access->psz_path );
889
890     p_vcdplayer->psz_source        = strdup(psz_source);
891     p_vcdplayer->i_blocks_per_read = config_GetInt( p_this, MODULE_STRING
892                                                     "-blocks-per-read" );
893     p_vcdplayer->b_track_length    = config_GetInt( p_this, MODULE_STRING
894                                                     "-track-length" );
895     p_vcdplayer->in_still          = false;
896     p_vcdplayer->play_item.type    = VCDINFO_ITEM_TYPE_NOTFOUND;
897     p_vcdplayer->p_input           = access_GetParentInput( p_access );
898 //    p_vcdplayer->p_meta            = vlc_meta_New();
899     p_vcdplayer->p_segments        = NULL;
900     p_vcdplayer->p_entries         = NULL;
901
902     /* set up input  */
903
904     if( !(p_vcdplayer->vcd = vcd_Open( p_this, psz_source )) )
905     {
906         goto err_exit;
907     }
908
909     p_vcdplayer->b_svd = vcdinfo_get_tracksSVD(p_vcdplayer->vcd);
910
911     /* Get track information. */
912     p_vcdplayer->i_tracks = vcdinfo_get_num_tracks(p_vcdplayer->vcd);
913
914     if( p_vcdplayer->i_tracks<1 || CDIO_INVALID_TRACK==p_vcdplayer->i_tracks )
915     {
916         vcdinfo_close( p_vcdplayer->vcd );
917         LOG_ERR ("no movie tracks found" );
918         goto err_exit;
919     }
920
921     /* Build Navigation Title table for the tracks. */
922     VCDTitles( p_access );
923
924     /* Add into the above entry points as "Chapters". */
925     if( ! VCDEntryPoints( p_access ) )
926     {
927         msg_Warn( p_access, "could not read entry points, will not use them" );
928         p_vcdplayer->b_valid_ep = false;
929     }
930
931     /* Initialize LID info and add that as a menu item */
932     if( ! VCDLIDs( p_access ) )
933     {
934         msg_Warn( p_access, "could not read entry LIDs" );
935     }
936
937     /* Do we set PBC (via LID) on? */
938     p_vcdplayer->i_lid =
939       ( VCDINFO_ITEM_TYPE_LID == itemid.type
940         && p_vcdplayer->i_lids > itemid.num )
941       ? itemid.num
942       :  VCDINFO_INVALID_ENTRY;
943
944     /* Initialize segment information and add that a "Track". */
945     VCDSegments( p_access );
946
947     vcdplayer_play( p_access, itemid );
948
949     free( p_access->psz_demux );
950     p_access->psz_demux = strdup( "ps" );
951
952 #ifdef FIXED
953     if( play_single_item )
954         VCDFixupPlayList(p_access,p_vcd,psz_source,&itemid,play_single_item);
955 #endif
956
957 #ifdef FIXED
958     p_vcdplayer->p_intf = intf_Create( p_access, "vcdx" );
959     p_vcdplayer->p_intf->b_block = false;
960 #endif
961     p_vcdplayer->p_access = p_access;
962
963     free( psz_source );
964
965     return VLC_SUCCESS;
966  err_exit:
967     if( p_vcdplayer->p_input ) vlc_object_release( p_vcdplayer->p_input );
968     free( psz_source );
969     free( p_vcdplayer->psz_source );
970     free( p_vcdplayer );
971     return VLC_EGENERIC;
972 }
973
974 /*****************************************************************************
975  * VCDClose: closes VCD releasing allocated memory.
976  *****************************************************************************/
977 void
978 VCDClose ( vlc_object_t *p_this )
979 {
980     access_t    *p_access = (access_t *)p_this;
981     vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_access->p_sys;
982
983     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
984
985     {
986         unsigned int i;
987         for (i=0 ; i<p_vcdplayer->i_titles; i++)
988             if (p_vcdplayer->p_title[i])
989                 free(p_vcdplayer->p_title[i]->psz_name);
990     }
991  
992     vcdinfo_close( p_vcdplayer->vcd );
993
994     if( p_vcdplayer->p_input ) vlc_object_release( p_vcdplayer->p_input );
995
996     FREENULL( p_vcdplayer->p_entries );
997     FREENULL( p_vcdplayer->p_segments );
998     FREENULL( p_vcdplayer->psz_source );
999     FREENULL( p_vcdplayer->track );
1000     FREENULL( p_vcdplayer->segment );
1001     FREENULL( p_vcdplayer->entry );
1002     FREENULL( p_access->psz_demux );
1003     FREENULL( p_vcdplayer );
1004     p_vcd_access    = NULL;
1005 }
1006
1007 /*****************************************************************************
1008  * Control: The front-end or vlc engine calls here to ether get
1009  * information such as meta information or plugin capabilities or to
1010  * issue miscellaneous "set" requests.
1011  *****************************************************************************/
1012 static int VCDControl( access_t *p_access, int i_query, va_list args )
1013 {
1014     vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_access->p_sys;
1015     int         *pi_int;
1016     int i;
1017
1018     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_EVENT),
1019                "query %d", i_query );
1020
1021     switch( i_query )
1022     {
1023         /* Pass back a copy of meta information that was gathered when we
1024            during the Open/Initialize call.
1025          */
1026     case ACCESS_GET_META:
1027         dbg_print( INPUT_DBG_EVENT, "get meta info" );
1028 #if 0
1029         if( p_vcdplayer->p_meta )
1030         {
1031             vlc_meta_t **pp_meta = (vlc_meta_t**)va_arg(args,vlc_meta_t**);
1032
1033             *pp_meta = vlc_meta_Duplicate( p_vcdplayer->p_meta );
1034             dbg_print( INPUT_DBG_META, "%s", "Meta copied" );
1035         }
1036         else
1037 #endif
1038             msg_Warn( p_access, "tried to copy NULL meta info" );
1039
1040         return VLC_SUCCESS;
1041
1042     case ACCESS_CAN_SEEK:
1043     case ACCESS_CAN_FASTSEEK:
1044     case ACCESS_CAN_PAUSE:
1045     case ACCESS_CAN_CONTROL_PACE:
1046
1047         dbg_print( INPUT_DBG_EVENT,
1048                    "seek/fastseek/pause/can_control_pace" );
1049         *((bool*)va_arg( args, bool* )) = true;
1050         return VLC_SUCCESS;
1051
1052     /* */
1053     case ACCESS_GET_PTS_DELAY:
1054         *(int64_t*)va_arg(args,int64_t *) = MILLISECONDS_PER_SEC *
1055                          var_GetInteger( p_access, MODULE_STRING "-caching" );
1056         dbg_print( INPUT_DBG_EVENT, "GET PTS DELAY" );
1057         return VLC_SUCCESS;
1058
1059         /* */
1060     case ACCESS_SET_PAUSE_STATE:
1061         dbg_print( INPUT_DBG_EVENT, "SET PAUSE STATE" );
1062         return VLC_SUCCESS;
1063
1064     case ACCESS_GET_TITLE_INFO:
1065     {
1066         unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX)
1067           + strlen(p_vcdplayer->psz_source) + sizeof("@E999")+3;
1068         input_title_t ***ppp_title
1069           = (input_title_t***)va_arg( args, input_title_t*** );
1070         char *psz_mrl = malloc( psz_mrl_max );
1071         unsigned int i;
1072
1073         pi_int    = (int*)va_arg( args, int* );
1074
1075         dbg_print( INPUT_DBG_EVENT, "GET TITLE: i_titles %d",
1076                    p_vcdplayer->i_titles );
1077
1078         if( psz_mrl  )
1079         {
1080             snprintf(psz_mrl, psz_mrl_max, "%s%s",
1081                      VCD_MRL_PREFIX, p_vcdplayer->psz_source);
1082             VCDMetaInfo( p_access, psz_mrl );
1083             free(psz_mrl);
1084         }
1085
1086         /* Duplicate title info */
1087         if( p_vcdplayer->i_titles == 0 )
1088         {
1089             *pi_int = 0; ppp_title = NULL;
1090             return VLC_SUCCESS;
1091         }
1092         *pi_int = p_vcdplayer->i_titles;
1093         *ppp_title = malloc(sizeof(input_title_t **)*p_vcdplayer->i_titles);
1094
1095         if (!*ppp_title) return VLC_ENOMEM;
1096
1097         for( i = 0; i < p_vcdplayer->i_titles; i++ )
1098             if( p_vcdplayer->p_title[i] )
1099                 (*ppp_title)[i] =
1100                            vlc_input_title_Duplicate(p_vcdplayer->p_title[i]);
1101     }
1102     break;
1103
1104     case ACCESS_SET_TITLE:
1105         i = (int)va_arg( args, int );
1106
1107         dbg_print( INPUT_DBG_EVENT, "set title %d" , i);
1108         if( i != p_access->info.i_title )
1109         {
1110             vcdinfo_itemid_t itemid;
1111             track_t          i_track = i+1;
1112             unsigned int     i_entry =
1113                         vcdinfo_track_get_entry(p_vcdplayer->vcd,i_track);
1114
1115             if( i < p_vcdplayer->i_tracks )
1116             {
1117                 /* FIXME! For now we are assuming titles are only
1118                    tracks and that track == title+1 */
1119                 itemid.num = i_track;
1120                 itemid.type = VCDINFO_ITEM_TYPE_TRACK;
1121             }
1122             else
1123             {
1124                 /* FIXME! i_tracks+2 are Segments, but we need to be able
1125                    to figure out which segment of that. i_tracks+1 is
1126                    either Segments (if no LIDs) or LIDs otherwise. Again
1127                    need a way to get the LID number. */
1128
1129                 msg_Warn(p_access,"Trying to set track (%u) beyond end "
1130                          "of last track (%u).",i+1,p_vcdplayer->i_tracks);
1131                 return VLC_EGENERIC;
1132             }
1133
1134             VCDSetOrigin(p_access,
1135                          vcdinfo_get_entry_lsn(p_vcdplayer->vcd,i_entry),
1136                          i_track, &itemid);
1137         }
1138         break;
1139
1140     case ACCESS_SET_SEEKPOINT:
1141     {
1142         input_title_t *t = p_vcdplayer->p_title[p_access->info.i_title];
1143         unsigned int i = (unsigned int)va_arg( args, unsigned int );
1144
1145         dbg_print( INPUT_DBG_EVENT, "set seekpoint %d", i );
1146         if( t->i_seekpoint > 0 )
1147         {
1148             track_t i_track = p_access->info.i_title+1;
1149             lsn_t lsn;
1150
1151             /* FIXME! For now we are assuming titles are only tracks and
1152                that track == title+1 and we the play item is entries (not
1153                tracks or lids). We need to generalize all of this.
1154              */
1155
1156             if (i < p_vcdplayer->i_entries)
1157             {
1158                 p_vcdplayer->play_item.num  = i;
1159                 p_vcdplayer->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
1160                 lsn = vcdinfo_get_entry_lsn(p_vcdplayer->vcd, i);
1161             } else if ( i < p_vcdplayer->i_entries + p_vcdplayer->i_lids )
1162             {
1163                 p_vcdplayer->play_item.num = i = i - p_vcdplayer->i_entries;
1164                 p_vcdplayer->play_item.type = VCDINFO_ITEM_TYPE_LID;
1165                 lsn = 0;
1166             } else
1167             {
1168                 p_vcdplayer->play_item.num  = i
1169                       = i - p_vcdplayer->i_entries - p_vcdplayer->i_lids;
1170                 p_vcdplayer->play_item.type = VCDINFO_ITEM_TYPE_SEGMENT;
1171                 lsn = vcdinfo_get_seg_lsn(p_vcdplayer->vcd, i);
1172             }
1173
1174             VCDSetOrigin(p_access,vcdinfo_get_entry_lsn(p_vcdplayer->vcd,i),
1175                          i_track,&(p_vcdplayer->play_item));
1176         }
1177         return VLC_SUCCESS;
1178     }
1179
1180     case ACCESS_SET_PRIVATE_ID_STATE:
1181         dbg_print( INPUT_DBG_EVENT, "set private id" );
1182         return VLC_EGENERIC;
1183
1184     default:
1185         msg_Warn( p_access, "unimplemented query in control" );
1186         return VLC_EGENERIC;
1187
1188     }
1189     return VLC_SUCCESS;
1190 }