]> git.sesse.net Git - vlc/blob - modules/access/vcdx/access.c
Tolerate vcdimager < 0.7.21. Patch thanks to Arwed v. Merkatz.
[vlc] / modules / access / vcdx / access.c
1 /*****************************************************************************
2  * vcd.c : VCD input module for vlc
3  *         using libcdio, libvcd and libvcdinfo. vlc-specific things tend
4  *         to go here.
5  *****************************************************************************
6  * Copyright (C) 2000, 2003, 2004 VideoLAN
7  * $Id$
8  *
9  * Authors: Rocky Bernstein <rocky@panix.com>
10  *          Johan Bilien <jobi@via.ecp.fr>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30
31 #include <vlc/vlc.h>
32 #include <vlc/input.h>
33 #include <vlc/intf.h>
34
35 #include "../../demux/mpeg/system.h"
36 #include "vcd.h"
37 #include "vcdplayer.h"
38 #include "intf.h"
39
40 #include <cdio/cdio.h>
41 #include <cdio/cd_types.h>
42 #include <cdio/logging.h>
43 #include <cdio/util.h>
44 #include <libvcd/info.h>
45 #include <libvcd/logging.h>
46
47 #include "cdrom.h"
48
49 /* how many blocks VCDRead will read in each loop */
50 #define VCD_BLOCKS_ONCE 20
51 #define VCD_DATA_ONCE   (VCD_BLOCKS_ONCE * M2F2_SECTOR_SIZE)
52
53 #define VCD_MRL_PREFIX "vcdx://"
54
55 /*****************************************************************************
56  * Local prototypes
57  *****************************************************************************/
58
59 /* First those which are accessed from outside (via pointers). */
60 static int  VCDRead         ( input_thread_t *, byte_t *, size_t );
61 static int  VCDSetProgram   ( input_thread_t *, pgrm_descriptor_t * );
62
63 /* Now those which are strictly internal */
64 static void VCDSetOrigin    ( input_thread_t *, lsn_t origin_lsn,
65                               lsn_t cur_lsn, lsn_t end_lsn,
66                               int cur_entry, track_t track );
67 static int  VCDEntryPoints  ( input_thread_t * );
68 static int  VCDLIDs         ( input_thread_t * );
69 static int  VCDSegments     ( input_thread_t * );
70 static void VCDTracks       ( input_thread_t * );
71 static int  VCDReadSector   ( vlc_object_t *p_this,
72                               const vcdinfo_obj_t *p_vcd, lsn_t cur_lsn,
73                               byte_t * p_buffer );
74 static char *VCDParse       ( input_thread_t *,
75                               /*out*/ vcdinfo_itemid_t * p_itemid ,
76                               /*out*/ bool *play_single_item );
77
78 static void VCDUpdateVar( input_thread_t *p_input, int i_entry, int i_action,
79                           const char *p_varname, char *p_label,
80                           const char *p_debug_label );
81
82 static vcdinfo_obj_t *vcd_Open   ( vlc_object_t *p_this, const char *psz_dev );
83
84 /****************************************************************************
85  * Private functions
86  ****************************************************************************/
87
88 /* FIXME: This variable is a hack. Would be nice to eliminate the
89    global-ness. */
90
91 static input_thread_t *p_vcd_input = NULL;
92
93 /* process messages that originate from libcdio. */
94 static void
95 cdio_log_handler (cdio_log_level_t level, const char message[])
96 {
97   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
98   switch (level) {
99   case CDIO_LOG_DEBUG:
100   case CDIO_LOG_INFO:
101     if (p_vcd->i_debug & INPUT_DBG_CDIO)
102       msg_Dbg( p_vcd_input, message);
103     break;
104   case CDIO_LOG_WARN:
105     msg_Warn( p_vcd_input, message);
106     break;
107   case CDIO_LOG_ERROR:
108   case CDIO_LOG_ASSERT:
109     msg_Err( p_vcd_input, message);
110     break;
111   default:
112     msg_Warn( p_vcd_input, message,
113             _("The above message had unknown log level"),
114             level);
115   }
116   return;
117 }
118
119 /* process messages that originate from vcdinfo. */
120 static void
121 vcd_log_handler (vcd_log_level_t level, const char message[])
122 {
123   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
124   switch (level) {
125   case VCD_LOG_DEBUG:
126   case VCD_LOG_INFO:
127     if (p_vcd->i_debug & INPUT_DBG_VCDINFO)
128       msg_Dbg( p_vcd_input, message);
129     break;
130   case VCD_LOG_WARN:
131     msg_Warn( p_vcd_input, message);
132     break;
133   case VCD_LOG_ERROR:
134   case VCD_LOG_ASSERT:
135     msg_Err( p_vcd_input, message);
136     break;
137   default:
138     msg_Warn( p_vcd_input, "%s\n%s %d", message,
139             _("The above message had unknown vcdimager log level"),
140             level);
141   }
142   return;
143 }
144
145 /*****************************************************************************
146  * VCDRead: reads i_len bytes from the VCD into p_buffer.
147  *****************************************************************************
148  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
149  * bytes.
150  *****************************************************************************/
151 static int
152 VCDRead( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
153 {
154     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
155     int                     i_blocks;
156     int                     i_index;
157     int                     i_read;
158     byte_t                  p_last_sector[ M2F2_SECTOR_SIZE ];
159
160     i_read = 0;
161
162     dbg_print( (INPUT_DBG_CALL), "lsn: %lu", 
163                (long unsigned int) p_vcd->cur_lsn );
164
165     /* Compute the number of blocks we have to read */
166
167     i_blocks = i_len / M2F2_SECTOR_SIZE;
168
169     for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
170     {
171
172       if ( p_vcd->cur_lsn == p_vcd->end_lsn ) {
173         vcdplayer_read_status_t read_status;
174
175         /* We've run off of the end of this entry. Do we continue or stop? */
176         dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
177                    "end reached, cur: %lu", 
178                    (long unsigned int) p_vcd->cur_lsn );
179
180         read_status = vcdplayer_pbc_is_on( p_vcd )
181           ? vcdplayer_pbc_nav( p_input )
182           : vcdplayer_non_pbc_nav( p_input );
183
184         switch (read_status) {
185         case READ_END:
186           /* End reached. Return NULL to indicated this. */
187         case READ_ERROR:
188           /* Some sort of error. */
189           return i_read;
190
191         case READ_STILL_FRAME:
192           {
193             /* Reached the end of a still frame. */
194
195             byte_t * p_buf = p_buffer;
196             pgrm_descriptor_t * p_pgrm = p_input->stream.p_selected_program;;
197
198             p_buf += (i_index*M2F2_SECTOR_SIZE);
199             memset(p_buf, 0, M2F2_SECTOR_SIZE);
200             p_buf += 2;
201             *p_buf = 0x01;
202             dbg_print(INPUT_DBG_STILL, "Handled still event");
203
204 #if 1
205             p_vcd->p_intf->p_sys->b_still = 1;
206             input_SetStatus( p_input, INPUT_STATUS_PAUSE );
207 #endif
208
209             vlc_mutex_lock( &p_input->stream.stream_lock );
210
211             p_pgrm = p_input->stream.p_selected_program;
212             p_pgrm->i_synchro_state = SYNCHRO_REINIT;
213
214             vlc_mutex_unlock( &p_input->stream.stream_lock );
215
216             input_ClockManageControl( p_input, p_pgrm, 0 );
217
218             p_vcd->p_intf->p_sys->b_still = 1;
219             input_SetStatus( p_input, INPUT_STATUS_PAUSE );
220
221             return i_read + M2F2_SECTOR_SIZE;
222           }
223         default:
224         case READ_BLOCK:
225           break;
226         }
227       }
228
229       if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
230                           p_vcd->cur_lsn,
231                           p_buffer + (i_index*M2F2_SECTOR_SIZE) ) < 0 )
232         {
233           LOG_ERR ("could not read sector %lu", 
234                    (long unsigned int) p_vcd->cur_lsn );
235           return -1;
236         }
237
238       p_vcd->cur_lsn ++;
239
240       /* Update chapter */
241       if( p_vcd->b_valid_ep &&
242           /* FIXME kludge so that read does not update chapter
243            * when a manual chapter change was requested and not
244            * yet accomplished */
245           !p_input->stream.p_new_area )
246         {
247           unsigned int i_entry = p_input->stream.p_selected_area->i_part;
248
249           vlc_mutex_lock( &p_input->stream.stream_lock );
250
251           if( i_entry < p_vcd->num_entries &&
252               p_vcd->cur_lsn >= p_vcd->p_entries[i_entry+1] )
253             {
254               dbg_print( INPUT_DBG_PBC,
255                          "new entry, i_entry %d, sector %lu, es %u",
256                          i_entry, (long unsigned int) p_vcd->cur_lsn,
257                          p_vcd->p_entries[i_entry] );
258               p_vcd->play_item.num =
259                 ++ p_input->stream.p_selected_area->i_part;
260               p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
261               VCDUpdateVar( p_input, p_vcd->play_item.num, VLC_VAR_SETVALUE,
262                             "chapter", _("Entry"), "Setting entry" );
263             }
264           vlc_mutex_unlock( &p_input->stream.stream_lock );
265         }
266
267         i_read += M2F2_SECTOR_SIZE;
268     }
269
270     if ( i_len % M2F2_SECTOR_SIZE ) /* this should not happen */
271     {
272         if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
273                             p_vcd->cur_lsn, p_last_sector ) < 0 )
274         {
275             LOG_ERR ("could not read sector %lu", 
276                      (long unsigned int) p_vcd->cur_lsn );
277             return -1;
278         }
279
280         p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * M2F2_SECTOR_SIZE,
281                                    p_last_sector, i_len % M2F2_SECTOR_SIZE );
282         i_read += i_len % M2F2_SECTOR_SIZE;
283     }
284
285     return i_read;
286 }
287
288
289 /*****************************************************************************
290  * VCDSetProgram: Does nothing since a VCD is mono_program
291  *****************************************************************************/
292 static int
293 VCDSetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_program)
294 {
295     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
296     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDSetProgram" );
297     return 0;
298 }
299
300
301 /*****************************************************************************
302  * VCDSetArea: initialize internal data structures and input stream data
303    so set subsequent reading and seeking to reflect that we are
304    at track x, entry or segment y.
305    This is called for each user navigation request, e.g. the GUI
306    Chapter/Title selections or in initial MRL parsing.
307  ****************************************************************************/
308 int
309 VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
310 {
311     thread_vcd_data_t *p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
312     unsigned int i_entry = p_area->i_part;
313     track_t i_track      = p_area->i_id;
314     int old_seekable     = p_input->stream.b_seekable;
315     unsigned int i_nb    = p_area->i_plugin_data + p_area->i_part_nb;
316
317     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT),
318                "track: %d, entry %d, seekable %d, area %lx, select area %lx ",
319                i_track, i_entry, old_seekable,
320                (long unsigned int) p_area,
321                (long unsigned int) p_input->stream.p_selected_area );
322
323     /* we can't use the interface slider until initilization is complete */
324     p_input->stream.b_seekable = 0;
325
326     if( p_area != p_input->stream.p_selected_area )
327     {
328         unsigned int i;
329
330         /* If is the result of a track change, make the entry valid. */
331         if (i_entry < p_area->i_plugin_data || i_entry >= i_nb)
332           i_entry = p_area->i_plugin_data;
333
334         /* Change the default area */
335         p_input->stream.p_selected_area = p_area;
336
337         /* Update the navigation variables without triggering a callback */
338
339         VCDUpdateVar( p_input, i_track, VLC_VAR_SETVALUE, "title",
340                       _("Track"), "Setting track");
341
342         var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
343         for( i = p_area->i_plugin_data; i < i_nb; i++ )
344         {
345           VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
346                         "chapter",  
347                         p_vcd->play_item.type == VCDINFO_ITEM_TYPE_SEGMENT ?
348                         _("Segment"): _("Entry"),
349                         "Adding entry choice");
350         }
351
352         if (p_vcd->b_svd) {
353           unsigned int audio_type = 
354             vcdinfo_get_track_audio_type(p_vcd->vcd, i_track);
355           unsigned int i_channels = 
356             vcdinfo_audio_type_num_channels(p_vcd->vcd, audio_type);
357           
358           var_Change( p_input, "audio_channels", VLC_VAR_CLEARCHOICES, NULL, 
359                       NULL );
360           
361           for( i = 0;  i < i_channels; i++ )
362             {
363               VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
364                             "audio_channels",  NULL, "Adding audio choice");
365             }
366         }
367
368     }
369
370     if (i_track == 0)
371       VCDSetOrigin( p_input, p_vcd->p_segments[i_entry],
372                     p_vcd->p_segments[i_entry], p_vcd->p_segments[i_entry+1],
373                     i_entry, 0 );
374     else
375       VCDSetOrigin( p_input, p_vcd->p_sectors[i_track],
376                     vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry),
377                     p_vcd->p_sectors[i_track+1],
378                     i_entry, i_track );
379
380     p_input->stream.b_seekable = old_seekable;
381     /* warn interface that something has changed */
382     p_input->stream.b_changed = 1;
383
384     return VLC_SUCCESS;
385 }
386
387
388 /****************************************************************************
389  * VCDSeek
390  ****************************************************************************/
391 void
392 VCDSeek( input_thread_t * p_input, off_t i_off )
393 {
394     thread_vcd_data_t * p_vcd;
395     unsigned int i_entry=0; /* invalid entry */
396
397     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
398
399     p_vcd->cur_lsn = p_vcd->origin_lsn + (i_off / (off_t)M2F2_SECTOR_SIZE);
400
401     vlc_mutex_lock( &p_input->stream.stream_lock );
402 #define p_area p_input->stream.p_selected_area
403     /* Find entry */
404     if( p_vcd->b_valid_ep )
405     {
406         for( i_entry = 0 ; i_entry < p_vcd->num_entries ; i_entry ++ )
407         {
408             if( p_vcd->cur_lsn < p_vcd->p_entries[i_entry] )
409             {
410               VCDUpdateVar( p_input, i_entry, VLC_VAR_SETVALUE,
411                             "chapter", _("Entry"), "Setting entry" );
412               break;
413             }
414         }
415         p_vcd->play_item.num  = p_area->i_part = i_entry;
416         p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
417     }
418 #undef p_area
419
420     p_input->stream.p_selected_area->i_tell = i_off;
421
422     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
423                "orig %lu, cur: %lu, offset: %lld, start: %lld, entry %d",
424                (long unsigned int) p_vcd->origin_lsn, 
425                (long unsigned int) p_vcd->cur_lsn, i_off,
426                p_input->stream.p_selected_area->i_start, i_entry );
427
428     vlc_mutex_unlock( &p_input->stream.stream_lock );
429 }
430
431 /*****************************************************************************
432   VCDPlay: set up internal structures so seeking/reading places an item.
433   itemid: the thing to play.
434   user_entry: true if itemid is a user selection (rather than internally-
435   generated selection such as via PBC) in which case we may have to adjust
436   for differences in numbering.
437  *****************************************************************************/
438 int
439 VCDPlay( input_thread_t *p_input, vcdinfo_itemid_t itemid )
440 {
441     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
442     input_area_t *          p_area;
443     bool                    b_was_still;
444
445     dbg_print(INPUT_DBG_CALL, "itemid.num: %d, itemid.type: %d\n",
446               itemid.num, itemid.type);
447
448     if (!p_input->p_access_data) return VLC_EGENERIC;
449     
450     b_was_still = p_vcd->in_still;
451
452 #define area p_input->stream.pp_areas
453
454     switch (itemid.type) {
455     case VCDINFO_ITEM_TYPE_TRACK:
456
457       /* Valid tracks go from 1...num_tracks-1, because track 0 is unplayable.
458        */
459
460       if (itemid.num == 0 || itemid.num >= p_vcd->num_tracks) {
461         LOG_ERR ("Invalid track number %d", itemid.num );
462         return VLC_EGENERIC;
463       }
464       p_vcd->in_still  = false;
465       p_area           = area[itemid.num];
466       p_area->i_part   = p_area->i_plugin_data;
467       p_input->stream.b_seekable = 1;
468       break;
469     case VCDINFO_ITEM_TYPE_SEGMENT:
470       /* Valid segments go from 0...num_segments-1. */
471       if (itemid.num >= p_vcd->num_segments) {
472         LOG_ERR ( "Invalid segment number: %d", itemid.num );
473         return VLC_EGENERIC;
474       } else {
475         vcdinfo_video_segment_type_t segtype =
476           vcdinfo_get_video_type(p_vcd->vcd, itemid.num);
477
478         dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d",
479                   vcdinfo_video_type2str(p_vcd->vcd, itemid.num),
480                   (int) segtype, itemid.num);
481
482         p_area           = area[0];
483         p_area->i_part   = itemid.num;
484
485         switch (segtype)
486           {
487           case VCDINFO_FILES_VIDEO_NTSC_STILL:
488           case VCDINFO_FILES_VIDEO_NTSC_STILL2:
489           case VCDINFO_FILES_VIDEO_PAL_STILL:
490           case VCDINFO_FILES_VIDEO_PAL_STILL2:
491             p_input->stream.b_seekable = 0;
492             p_vcd->in_still = true;
493             break;
494           default:
495             p_input->stream.b_seekable = 1;
496             p_vcd->in_still = false;
497           }
498       }
499       break;
500
501     case VCDINFO_ITEM_TYPE_LID:
502       /* LIDs go from 1..num_lids. */
503       if (itemid.num == 0 || itemid.num > p_vcd->num_lids) {
504         LOG_ERR ( "Invalid LID number: %d", itemid.num );
505         return VLC_EGENERIC;
506       } else {
507         p_vcd->cur_lid = itemid.num;
508         vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
509
510         switch (p_vcd->pxd.descriptor_type) {
511
512         case PSD_TYPE_SELECTION_LIST:
513         case PSD_TYPE_EXT_SELECTION_LIST: {
514           vcdinfo_itemid_t trans_itemid;
515           uint16_t trans_itemid_num;
516
517           if (p_vcd->pxd.psd == NULL) return VLC_EGENERIC;
518           trans_itemid_num  = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
519           vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
520           p_vcd->loop_count = 1;
521           p_vcd->loop_item  = trans_itemid;
522           return VCDPlay( p_input, trans_itemid );
523           break;
524         }
525
526         case PSD_TYPE_PLAY_LIST: {
527           if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
528           p_vcd->pdi = -1;
529           return vcdplayer_inc_play_item(p_input)
530             ? VLC_SUCCESS : VLC_EGENERIC;
531           break;
532         }
533
534         case PSD_TYPE_END_LIST:
535         case PSD_TYPE_COMMAND_LIST:
536
537         default:
538           ;
539         }
540       }
541       return VLC_EGENERIC;
542     case VCDINFO_ITEM_TYPE_ENTRY:
543       /* Entries go from 0..num_entries-1. */
544       if (itemid.num >= p_vcd->num_entries) {
545         LOG_ERR ("Invalid entry number: %d", itemid.num );
546         return VLC_EGENERIC;
547       } else {
548         track_t cur_track  = vcdinfo_get_track(p_vcd->vcd,  itemid.num);
549         p_vcd->in_still    = false;
550         p_area             = area[cur_track];
551         p_area->i_part     = itemid.num;
552         p_input->stream.b_seekable = 1;
553       }
554       break;
555     default:
556       LOG_ERR ("unknown entry type" );
557       return VLC_EGENERIC;
558     }
559
560     VCDSetArea( p_input, p_area );
561
562 #undef area
563
564 #if 1
565     if ( p_vcd->in_still != b_was_still ) {
566       if (p_input->stream.pp_selected_es) {
567         input_SetStatus( p_input, INPUT_STATUS_END );
568         input_SetStatus( p_input, INPUT_STATUS_PLAY );
569       }
570     }
571 #endif
572
573     p_vcd->play_item = itemid;
574
575     dbg_print( (INPUT_DBG_CALL),
576                "i_start %lld, i_size: %lld, i_tell: %lld, lsn %lu",
577                p_area->i_start, p_area->i_size,
578                p_area->i_tell, 
579                (long unsigned int) p_vcd->cur_lsn );
580
581     return VLC_SUCCESS;
582 }
583
584 /*****************************************************************************
585   VCDEntryPoints: Reads the information about the entry points on the disc
586   and initializes area information with that.
587   Before calling this track information should have been read in.
588  *****************************************************************************/
589 static int
590 VCDEntryPoints( input_thread_t * p_input )
591 {
592     thread_vcd_data_t *               p_vcd;
593     unsigned int                      i_nb;
594     unsigned int                      i, i_entry_index = 0;
595     unsigned int                      i_previous_track = CDIO_INVALID_TRACK;
596
597     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
598
599     i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
600     if (0 == i_nb)
601       return -1;
602
603     p_vcd->p_entries  = malloc( sizeof( lba_t ) * i_nb );
604
605     if( p_vcd->p_entries == NULL )
606     {
607         LOG_ERR ("not enough memory for entry points treatment" );
608         return -1;
609     }
610
611     p_vcd->num_entries = 0;
612
613     for( i = 0 ; i < i_nb ; i++ )
614     {
615         track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
616         if( i_track <= p_input->stream.i_area_nb )
617         {
618             p_vcd->p_entries[i] =
619               vcdinfo_get_entry_lsn(p_vcd->vcd, i);
620             p_input->stream.pp_areas[i_track]->i_part_nb ++;
621
622             /* if this entry belongs to a new track */
623             if( i_track != i_previous_track )
624             {
625                 /* i_plugin_data is used to store the first entry of the area*/
626                 p_input->stream.pp_areas[i_track]->i_plugin_data =
627                                                             i_entry_index;
628                 i_previous_track = i_track;
629                 p_input->stream.pp_areas[i_track]->i_part_nb = 1;
630             }
631             i_entry_index ++;
632             p_vcd->num_entries ++;
633         }
634         else
635             msg_Warn( p_input, "wrong track number found in entry points" );
636     }
637     p_vcd->b_valid_ep = true;
638     return 0;
639 }
640
641 /*****************************************************************************
642  * VCDSegments: Reads the information about the segments the disc.
643  *****************************************************************************/
644 static int
645 VCDSegments( input_thread_t * p_input )
646 {
647     thread_vcd_data_t * p_vcd;
648     unsigned int        i;
649     unsigned int        num_segments;
650
651
652     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
653     num_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
654
655 #define area p_input->stream.pp_areas
656
657     /* area 0 is reserved for segments. Set Absolute start offset
658          and size */
659     area[0]->i_plugin_data = 0;
660     input_DelArea( p_input, area[0] );
661     input_AddArea( p_input, 0, 0 );
662
663     area[0]->i_start = (off_t)p_vcd->p_sectors[0]
664       * (off_t)M2F2_SECTOR_SIZE;
665     area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
666       * (off_t)M2F2_SECTOR_SIZE;
667
668     /* Default Segment  */
669     area[0]->i_part = 0;
670
671     /* i_plugin_data is used to store which entry point is the first
672        of the track (area) */
673     area[0]->i_plugin_data = 0;
674
675     area[0]->i_part_nb = 0;
676
677     dbg_print( INPUT_DBG_MRL,
678                "area[0] id: %d, i_start: %lld, i_size: %lld",
679                area[0]->i_id, area[0]->i_start, area[0]->i_size );
680
681     if (num_segments == 0) return 0;
682
683     /* We have one additional segment allocated so we can get the size
684        by subtracting seg[i+1] - seg[i].
685      */
686     p_vcd->p_segments = malloc( sizeof( lba_t ) * (num_segments+1) );
687     if( p_vcd->p_segments == NULL )
688     {
689         LOG_ERR ("not enough memory for segment treatment" );
690         return -1;
691     }
692
693     /* Update the navigation variables without triggering a callback */
694     VCDUpdateVar( p_input, 0, VLC_VAR_SETVALUE, "title", _("Track"),
695                   "Setting track" );
696
697     var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
698
699     for( i = 0 ; i < num_segments ; i++ )
700     {
701       p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
702       area[0]->i_part_nb ++;
703       VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
704                     "chapter", _("Segment"), "Adding segment choice");
705     }
706
707 #undef area
708
709     p_vcd->p_segments[num_segments] = p_vcd->p_segments[num_segments-1]+
710       vcdinfo_get_seg_sector_count(p_vcd->vcd, num_segments-1);
711
712     return 0;
713 }
714
715 /*****************************************************************************
716  VCDTracks: initializes area information.
717  Before calling this track information should have been read in.
718  *****************************************************************************/
719 static void
720 VCDTracks( input_thread_t * p_input )
721 {
722     thread_vcd_data_t * p_vcd;
723     unsigned int        i;
724
725     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
726
727 #define area p_input->stream.pp_areas
728
729     /* We start area addressing for tracks at 1 since the default area 0
730        is reserved for segments */
731
732     for( i = 1 ; i < p_vcd->num_tracks ; i++ )
733     {
734         /* Tracks are Program Chains */
735         input_AddArea( p_input, i, i );
736
737         /* Absolute start byte offset and byte size */
738         area[i]->i_start = (off_t) p_vcd->p_sectors[i]
739                            * (off_t)M2F2_SECTOR_SIZE;
740         area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
741                            * (off_t)M2F2_SECTOR_SIZE;
742
743         /* Current entry being played in track */
744         area[i]->i_part = 0;
745
746         /* i_plugin_data is used to store which entry point is the first
747          * of the track (area) */
748         area[i]->i_plugin_data = 0;
749
750         dbg_print( INPUT_DBG_MRL,
751                    "area[%d] id: %d, i_start: %lld, i_size: %lld",
752                    i, area[i]->i_id, area[i]->i_start, area[i]->i_size );
753     }
754
755 #undef area
756
757     return ;
758 }
759
760 /*****************************************************************************
761   VCDLIDs: Reads the LIST IDs from the LOT.
762  *****************************************************************************/
763 static int
764 VCDLIDs( input_thread_t * p_input )
765 {
766     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
767
768     p_vcd->num_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
769     p_vcd->cur_lid  = VCDINFO_INVALID_ENTRY;
770
771     if (vcdinfo_read_psd (p_vcd->vcd)) {
772
773       vcdinfo_visit_lot (p_vcd->vcd, false);
774
775 #if FIXED
776     /*
777        We need to change libvcdinfo to be more robust when there are
778        problems reading the extended PSD. Given that area-highlighting and
779        selection features in the extended PSD haven't been implemented,
780        it's best then to not try to read this at all.
781      */
782       if (vcdinfo_get_psd_x_size(p_vcd->vcd))
783         vcdinfo_visit_lot (p_vcd->vcd, true);
784 #endif
785     }
786
787     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
788                "num LIDs=%d", p_vcd->num_lids);
789
790     return 0;
791 }
792
793 /*****************************************************************************
794  * VCDParse: parse command line
795  *****************************************************************************/
796 static char *
797 VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid,
798           /*out*/ bool *play_single_item )
799 {
800     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
801     char *             psz_parser;
802     char *             psz_source;
803     char *             psz_next;
804
805     if ( config_GetInt( p_input, MODULE_STRING "-PBC" ) ) {
806       p_itemid->type=VCDINFO_ITEM_TYPE_LID;
807       p_itemid->num=1;
808       *play_single_item=false;
809     } else {
810       p_itemid->type=VCDINFO_ITEM_TYPE_ENTRY;
811       p_itemid->num=0;
812     }
813
814 #ifdef WIN32
815     /* On Win32 we want the VCD access plugin to be explicitly requested,
816      * we end up with lots of problems otherwise */
817     if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
818 #endif
819
820     if( !p_input->psz_name )
821     {
822         return NULL;
823     }
824
825     psz_parser = psz_source = strdup( p_input->psz_name );
826
827     /* Parse input string :
828      * [device][@[type][title]] */
829     while( *psz_parser && *psz_parser != '@' )
830     {
831         psz_parser++;
832     }
833
834     if( *psz_parser == '@' )
835     {
836       /* Found the divide between the source name and the
837          type+entry number. */
838       unsigned int num;
839
840       *psz_parser = '\0';
841       ++psz_parser;
842       if( *psz_parser )
843         {
844           switch(*psz_parser) {
845           case 'E':
846             p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
847             ++psz_parser;
848             *play_single_item = true;
849             break;
850           case 'P':
851             p_itemid->type = VCDINFO_ITEM_TYPE_LID;
852             ++psz_parser;
853             *play_single_item = false;
854             break;
855           case 'S':
856             p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
857             ++psz_parser;
858             *play_single_item = true;
859             break;
860           case 'T':
861             p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
862             ++psz_parser;
863             *play_single_item = true;
864             break;
865           default: ;
866           }
867         }
868
869       num = strtol( psz_parser, &psz_next, 10 );
870       if ( *psz_parser != '\0' && *psz_next == '\0')
871         {
872           p_itemid->num = num;
873         }
874
875     } else {
876       *play_single_item = ( VCDINFO_ITEM_TYPE_LID == p_itemid->type );
877     }
878
879
880     if( !*psz_source ) {
881
882       /* No source specified, so figure it out. */
883       if( !p_input->psz_access ) return NULL;
884
885       psz_source = config_GetPsz( p_input, "vcd" );
886
887       if( !psz_source || 0==strlen(psz_source) ) {
888         /* Scan for a CD-ROM drive with a VCD in it. */
889         char **cd_drives = cdio_get_devices_with_cap(NULL,
890                             (CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD
891                              |CDIO_FS_ANAL_VIDEOCD|CDIO_FS_UNKNOWN),
892                                                      true);
893         if (NULL == cd_drives) return NULL;
894         if (cd_drives[0] == NULL) {
895           cdio_free_device_list(cd_drives);
896           return NULL;
897         }
898         psz_source = strdup(cd_drives[0]);
899         cdio_free_device_list(cd_drives);
900       }
901     }
902
903     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
904                "source=%s entry=%d type=%d",
905                psz_source, p_itemid->num, p_itemid->type);
906
907     return psz_source;
908 }
909
910 /*
911    Set's start origin subsequent seeks/reads
912 */
913 static void
914 VCDSetOrigin( input_thread_t *p_input, lsn_t origin_lsn,
915               lsn_t cur_lsn, lsn_t end_lsn, int cur_entry, track_t cur_track )
916 {
917   thread_vcd_data_t * p_vcd  = (thread_vcd_data_t *) p_input->p_access_data;
918
919   p_vcd->origin_lsn = origin_lsn;
920   p_vcd->cur_lsn    = cur_lsn;
921   p_vcd->end_lsn    = end_lsn;
922   p_vcd->cur_track  = cur_track;
923   p_vcd->play_item.num  = cur_entry;
924   p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
925
926   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
927              "origin: %lu, cur_lsn: %lu, end_lsn: %lu, entry: %d, track: %d",
928              (long unsigned int) origin_lsn, 
929              (long unsigned int) cur_lsn, 
930              (long unsigned int) end_lsn, cur_entry, cur_track );
931
932   p_input->stream.p_selected_area->i_tell =
933     (off_t) (p_vcd->cur_lsn - p_vcd->origin_lsn) * (off_t)M2F2_SECTOR_SIZE;
934
935   VCDUpdateVar( p_input, cur_entry, VLC_VAR_SETVALUE,
936                 "chapter", 
937                 p_vcd->play_item.type == VCDINFO_ITEM_TYPE_ENTRY ?
938                 _("Entry") : _("Segment"), 
939                 "Setting entry/segment");
940 }
941
942 /*****************************************************************************
943  * vcd_Open: Opens a VCD device or file and returns an opaque handle
944  *****************************************************************************/
945 static vcdinfo_obj_t *
946 vcd_Open( vlc_object_t *p_this, const char *psz_dev )
947 {
948     vcdinfo_obj_t *p_vcdobj;
949     char  *actual_dev;
950
951     if( !psz_dev ) return NULL;
952
953     actual_dev=strdup(psz_dev);
954     if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) !=
955          VCDINFO_OPEN_VCD) {
956       free(actual_dev);
957       return NULL;
958     }
959     free(actual_dev);
960
961     return p_vcdobj;
962 }
963
964 /****************************************************************************
965  * VCDReadSector: Read a sector (2324 bytes)
966  ****************************************************************************/
967 static int
968 VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
969                lsn_t cur_lsn, byte_t * p_buffer )
970 {
971   typedef struct {
972     uint8_t subheader   [8];
973     uint8_t data        [M2F2_SECTOR_SIZE];
974     uint8_t spare       [4];
975   } vcdsector_t;
976   vcdsector_t vcd_sector;
977
978   if (cdio_read_mode2_sector(vcdinfo_get_cd_image(p_vcd),
979                              &vcd_sector, cur_lsn, true)
980       != 0)
981     {
982       msg_Warn( p_this, "Could not read LSN %lu", 
983                 (long unsigned int) cur_lsn );
984       return -1;
985     }
986
987   memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
988
989   return( 0 );
990 }
991
992 /****************************************************************************
993  Update the "varname" variable to i_num without triggering a callback.
994 ****************************************************************************/
995 static void
996 VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
997               const char *p_varname, char *p_label, 
998               const char *p_debug_label)
999 {
1000   vlc_value_t val;
1001   val.i_int = i_num;
1002   if (NULL != p_vcd_input) {
1003     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1004     dbg_print( INPUT_DBG_PBC, "%s %d", p_debug_label, i_num );
1005   }
1006   if (p_label) {
1007     vlc_value_t text;
1008     text.psz_string = p_label;
1009     var_Change( p_input, p_varname, VLC_VAR_SETTEXT, &text, NULL );
1010   }
1011   var_Change( p_input, p_varname, i_action, &val, NULL );
1012 }
1013
1014
1015 static inline void
1016 MetaInfoAddStr(input_thread_t *p_input, char *p_cat,
1017                char *title, const char *str)
1018 {
1019   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1020   if ( str ) {
1021     dbg_print( INPUT_DBG_META, "field: %s: %s\n", title, str);
1022     input_Control( p_input, INPUT_ADD_INFO, p_cat, title, "%s", str);
1023   }
1024 }
1025
1026
1027 static inline void
1028 MetaInfoAddNum(input_thread_t *p_input, char *p_cat, char *title, int num)
1029 {
1030   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1031   dbg_print( INPUT_DBG_META, "field %s: %d\n", title, num);
1032   input_Control( p_input, INPUT_ADD_INFO, p_cat, title, "%d", num );
1033 }
1034
1035 #define addstr(title, str) \
1036   MetaInfoAddStr( p_input, p_cat, title, str );
1037
1038 #define addnum(title, num) \
1039   MetaInfoAddNum( p_input, p_cat, title, num );
1040
1041 static void InformationCreate( input_thread_t *p_input  )
1042 {
1043   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1044   unsigned int i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
1045   unsigned int last_entry = 0;
1046   char *p_cat;
1047   track_t i_track;
1048
1049   p_cat = _("General");
1050
1051   addstr( _("VCD Format"),  vcdinfo_get_format_version_str(p_vcd->vcd) );
1052   addstr( _("Album"),       vcdinfo_get_album_id(p_vcd->vcd));
1053   addstr( _("Application"), vcdinfo_get_application_id(p_vcd->vcd) );
1054   addstr( _("Preparer"),    vcdinfo_get_preparer_id(p_vcd->vcd) );
1055   addnum( _("Vol #"),       vcdinfo_get_volume_num(p_vcd->vcd) );
1056   addnum( _("Vol max #"),   vcdinfo_get_volume_count(p_vcd->vcd) );
1057   addstr( _("Volume Set"),  vcdinfo_get_volumeset_id(p_vcd->vcd) );
1058   addstr( _("Volume"),      vcdinfo_get_volume_id(p_vcd->vcd) );
1059   addstr( _("Publisher"),   vcdinfo_get_publisher_id(p_vcd->vcd) );
1060   addstr( _("System Id"),   vcdinfo_get_system_id(p_vcd->vcd) );
1061   addnum( "LIDs",           vcdinfo_get_num_LIDs(p_vcd->vcd) );
1062   addnum( _("Entries"),     vcdinfo_get_num_entries(p_vcd->vcd) );
1063   addnum( _("Segments"),    vcdinfo_get_num_segments(p_vcd->vcd) );
1064   addnum( _("Tracks"),      vcdinfo_get_num_tracks(p_vcd->vcd) );
1065
1066   /* Spit out track information. Could also include MSF info.
1067    */
1068
1069 #define TITLE_MAX 30
1070   for( i_track = 1 ; i_track < p_vcd->num_tracks ; i_track++ ) {
1071     char psz_track[TITLE_MAX];
1072     unsigned int audio_type = vcdinfo_get_track_audio_type(p_vcd->vcd, 
1073                                                            i_track);
1074     snprintf(psz_track, TITLE_MAX, "%s%02d", _("Track "), i_track);
1075     p_cat = psz_track;
1076
1077     if (p_vcd->b_svd) {
1078       addnum(_("Audio Channels"),  
1079              vcdinfo_audio_type_num_channels(p_vcd->vcd, audio_type) );
1080     }
1081
1082     addnum(_("First Entry Point"), last_entry );
1083     for ( ; last_entry < i_nb 
1084             && vcdinfo_get_track(p_vcd->vcd, last_entry) == i_track;
1085           last_entry++ ) ;
1086     addnum(_("Last Entry Point"), last_entry-1 );
1087   }
1088 }
1089
1090 #define add_format_str_info(val)                               \
1091   {                                                            \
1092     const char *str = val;                                     \
1093     unsigned int len;                                          \
1094     if (val != NULL) {                                         \
1095       len=strlen(str);                                         \
1096       if (len != 0) {                                          \
1097         strncat(tp, str, TEMP_STR_LEN-(tp-temp_str));          \
1098         tp += len;                                             \
1099       }                                                        \
1100       saw_control_prefix = false;                              \
1101     }                                                          \
1102   }
1103
1104 #define add_format_num_info(val, fmt)                          \
1105   {                                                            \
1106     char num_str[10];                                          \
1107     unsigned int len;                                          \
1108     sprintf(num_str, fmt, val);                                \
1109     len=strlen(num_str);                                       \
1110     if (len != 0) {                                            \
1111       strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str));        \
1112       tp += len;                                               \
1113     }                                                          \
1114     saw_control_prefix = false;                                \
1115   }
1116
1117 /*!
1118    Take a format string and expand escape sequences, that is sequences that
1119    begin with %, with information from the current VCD.
1120    The expanded string is returned. Here is a list of escape sequences:
1121
1122    %A : The album information
1123    %C : The VCD volume count - the number of CD's in the collection.
1124    %c : The VCD volume num - the number of the CD in the collection.
1125    %F : The VCD Format, e.g. VCD 1.0, VCD 1.1, VCD 2.0, or SVCD
1126    %I : The current entry/segment/playback type, e.g. ENTRY, TRACK, SEGMENT...
1127    %L : The playlist ID prefixed with " LID" if it exists
1128    %M : MRL
1129    %N : The current number of the %I - a decimal number
1130    %P : The publisher ID
1131    %p : The preparer ID
1132    %S : If we are in a segment (menu), the kind of segment
1133    %T : The track number
1134    %V : The volume set ID
1135    %v : The volume ID
1136        A number between 1 and the volume count.
1137    %% : a %
1138 */
1139 static char *
1140 VCDFormatStr(const input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1141              const char format_str[], const char *mrl,
1142              const vcdinfo_itemid_t *itemid)
1143 {
1144 #define TEMP_STR_SIZE 256
1145 #define TEMP_STR_LEN (TEMP_STR_SIZE-1)
1146   static char    temp_str[TEMP_STR_SIZE];
1147   size_t i;
1148   char * tp = temp_str;
1149   bool saw_control_prefix = false;
1150   size_t format_len = strlen(format_str);
1151
1152   memset(temp_str, 0, TEMP_STR_SIZE);
1153
1154   for (i=0; i<format_len; i++) {
1155
1156     if (!saw_control_prefix && format_str[i] != '%') {
1157       *tp++ = format_str[i];
1158       saw_control_prefix = false;
1159       continue;
1160     }
1161
1162     switch(format_str[i]) {
1163     case '%':
1164       if (saw_control_prefix) {
1165         *tp++ = '%';
1166       }
1167       saw_control_prefix = !saw_control_prefix;
1168       break;
1169     case 'A':
1170       add_format_str_info(vcdinfo_strip_trail(vcdinfo_get_album_id(p_vcd->vcd),
1171                                               MAX_ALBUM_LEN));
1172       break;
1173
1174     case 'c':
1175       add_format_num_info(vcdinfo_get_volume_num(p_vcd->vcd), "%d");
1176       break;
1177
1178     case 'C':
1179       add_format_num_info(vcdinfo_get_volume_count(p_vcd->vcd), "%d");
1180       break;
1181
1182     case 'F':
1183       add_format_str_info(vcdinfo_get_format_version_str(p_vcd->vcd));
1184       break;
1185
1186     case 'I':
1187       {
1188         switch (itemid->type) {
1189         case VCDINFO_ITEM_TYPE_TRACK:
1190           strncat(tp, _("Track"), TEMP_STR_LEN-(tp-temp_str));
1191           tp += strlen(_("Track"));
1192         break;
1193         case VCDINFO_ITEM_TYPE_ENTRY:
1194           strncat(tp, _("Entry"), TEMP_STR_LEN-(tp-temp_str));
1195           tp += strlen(_("Entry"));
1196           break;
1197         case VCDINFO_ITEM_TYPE_SEGMENT:
1198           strncat(tp, _("Segment"), TEMP_STR_LEN-(tp-temp_str));
1199           tp += strlen(_("Segment"));
1200           break;
1201         case VCDINFO_ITEM_TYPE_LID:
1202           strncat(tp, _("List ID"), TEMP_STR_LEN-(tp-temp_str));
1203           tp += strlen(_("List ID"));
1204           break;
1205         case VCDINFO_ITEM_TYPE_SPAREID2:
1206           strncat(tp, _("Navigation"), TEMP_STR_LEN-(tp-temp_str));
1207           tp += strlen(_("Navigation"));
1208           break;
1209         default:
1210           /* What to do? */
1211           ;
1212         }
1213         saw_control_prefix = false;
1214       }
1215       break;
1216
1217     case 'L':
1218       if (vcdplayer_pbc_is_on(p_vcd)) {
1219         char num_str[40];
1220         sprintf(num_str, "%s %d", _("List ID"), p_vcd->cur_lid);
1221         strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str));
1222         tp += strlen(num_str);
1223       }
1224       saw_control_prefix = false;
1225       break;
1226
1227     case 'M':
1228       add_format_str_info(mrl);
1229       break;
1230
1231     case 'N':
1232       add_format_num_info(itemid->num, "%d");
1233       break;
1234
1235     case 'p':
1236       add_format_str_info(vcdinfo_get_preparer_id(p_vcd->vcd));
1237       break;
1238
1239     case 'P':
1240       add_format_str_info(vcdinfo_get_publisher_id(p_vcd->vcd));
1241       break;
1242
1243     case 'S':
1244       if ( VCDINFO_ITEM_TYPE_SEGMENT==itemid->type ) {
1245         char seg_type_str[10];
1246
1247         sprintf(seg_type_str, " %s",
1248                 vcdinfo_video_type2str(p_vcd->vcd, itemid->num));
1249         strncat(tp, seg_type_str, TEMP_STR_LEN-(tp-temp_str));
1250         tp += strlen(seg_type_str);
1251       }
1252       saw_control_prefix = false;
1253       break;
1254
1255     case 'T':
1256       add_format_num_info(p_vcd->cur_track, "%d");
1257       break;
1258
1259     case 'V':
1260       add_format_str_info(vcdinfo_get_volumeset_id(p_vcd->vcd));
1261       break;
1262
1263     case 'v':
1264       add_format_str_info(vcdinfo_get_volume_id(p_vcd->vcd));
1265       break;
1266
1267     default:
1268       *tp++ = '%';
1269       *tp++ = format_str[i];
1270       saw_control_prefix = false;
1271     }
1272   }
1273   return strdup(temp_str);
1274 }
1275
1276 static void
1277 VCDCreatePlayListItem(const input_thread_t *p_input,
1278                       thread_vcd_data_t *p_vcd,
1279                       playlist_t *p_playlist,
1280                       const vcdinfo_itemid_t *itemid,
1281                       char *psz_mrl, int psz_mrl_max,
1282                       const char *psz_source, int playlist_operation,
1283                       int i_pos)
1284 {
1285   char *p_author;
1286   char *p_title;
1287   char c_type;
1288
1289   switch(itemid->type) {
1290   case VCDINFO_ITEM_TYPE_TRACK:
1291     c_type='T';
1292     break;
1293   case VCDINFO_ITEM_TYPE_SEGMENT:
1294     c_type='S';
1295     break;
1296   case VCDINFO_ITEM_TYPE_LID:
1297     c_type='P';
1298     break;
1299   case VCDINFO_ITEM_TYPE_ENTRY:
1300     c_type='E';
1301     break;
1302   default:
1303     c_type='?';
1304     break;
1305   }
1306
1307   snprintf(psz_mrl, psz_mrl_max, "%s%s@%c%u", VCD_MRL_PREFIX, psz_source,
1308            c_type, itemid->num);
1309
1310   p_title =
1311     VCDFormatStr( p_input, p_vcd,
1312                   config_GetPsz( p_input, MODULE_STRING "-title-format" ),
1313                   psz_mrl, itemid );
1314   
1315   playlist_Add( p_playlist, psz_mrl, p_title, playlist_operation, i_pos );
1316
1317   p_author =
1318     VCDFormatStr( p_input, p_vcd,
1319                   config_GetPsz( p_input, MODULE_STRING "-author-format" ),
1320                   psz_mrl, itemid );
1321
1322   if( i_pos == PLAYLIST_END ) i_pos = p_playlist->i_size - 1;
1323   playlist_AddInfo(p_playlist, i_pos, _("General"), _("Author"), "%s",
1324                    p_author);
1325 }
1326
1327 static int
1328 VCDFixupPlayList( input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1329                   const char *psz_source, vcdinfo_itemid_t *itemid,
1330                   bool play_single_item )
1331 {
1332   unsigned int i;
1333   playlist_t * p_playlist;
1334   char       * psz_mrl;
1335   unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX) + strlen(psz_source) +
1336     strlen("@T") + strlen("100") + 1;
1337
1338   psz_mrl = malloc( psz_mrl_max );
1339
1340   if( psz_mrl == NULL )
1341     {
1342       msg_Warn( p_input, "out of memory" );
1343       return -1;
1344     }
1345
1346   p_playlist = (playlist_t *) vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1347                                                FIND_ANYWHERE );
1348   if( !p_playlist )
1349     {
1350       msg_Warn( p_input, "can't find playlist" );
1351       free(psz_mrl);
1352       return -1;
1353     }
1354
1355   if ( play_single_item ) 
1356     {
1357     /* May fill out more information when the playlist user interface becomes
1358        more mature.
1359      */
1360     VCDCreatePlayListItem(p_input, p_vcd, p_playlist, itemid,
1361                           psz_mrl, psz_mrl_max, psz_source, PLAYLIST_REPLACE,
1362                           p_playlist->i_index);
1363
1364     } 
1365   else 
1366     {
1367     vcdinfo_itemid_t list_itemid;
1368     list_itemid.type=VCDINFO_ITEM_TYPE_ENTRY;
1369
1370     playlist_Delete( p_playlist, p_playlist->i_index);
1371
1372     for( i = 0 ; i < p_vcd->num_entries ; i++ )
1373       {
1374         list_itemid.num=i;
1375         VCDCreatePlayListItem(p_input, p_vcd, p_playlist, &list_itemid,
1376                               psz_mrl, psz_mrl_max, psz_source,
1377                               PLAYLIST_APPEND, PLAYLIST_END);
1378       }
1379
1380     playlist_Command( p_playlist, PLAYLIST_GOTO, 0 );
1381
1382   }
1383
1384   vlc_object_release( p_playlist );
1385   free(psz_mrl);
1386   return 0;
1387 }
1388
1389 /*****************************************************************************
1390  * Public routines.
1391  *****************************************************************************/
1392 int
1393 E_(DebugCallback)   ( vlc_object_t *p_this, const char *psz_name,
1394                       vlc_value_t oldval, vlc_value_t val, void *p_data )
1395 {
1396   thread_vcd_data_t *p_vcd;
1397
1398   if (NULL == p_vcd_input) return VLC_EGENERIC;
1399
1400   p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1401
1402   if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
1403     msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d",
1404              p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
1405   }
1406   p_vcd->i_debug = val.i_int;
1407   return VLC_SUCCESS;
1408 }
1409
1410
1411 /*
1412   The front-ends change spu-es - which sort of represents a symbolic
1413   name. Internally we use spu-channel which runs from 0..3.
1414
1415   So we add a callback to intercept changes to spu-es and change them
1416   to updates to spu-channel. Ugly.
1417
1418 */
1419
1420 static int 
1421 VCDSPUCallback( vlc_object_t *p_this, const char *psz_variable,
1422                 vlc_value_t old_val, vlc_value_t new_val, void *param)
1423 {
1424
1425   input_thread_t    *p_input = (input_thread_t *)p_this;
1426   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1427   vlc_value_t val;
1428   
1429   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EVENT), "old_val: %x, new_val %x", 
1430              old_val.i_int, new_val.i_int);
1431
1432   val.i_int =  new_val.i_int;
1433   var_Set( p_this, "spu-channel", val );
1434   
1435   return VLC_SUCCESS;
1436 }
1437
1438 /*****************************************************************************
1439   Open: open VCD.
1440   read in meta-information about VCD: the number of tracks, segments,
1441   entries, size and starting information. Then set up state variables so
1442   that we read/seek starting at the location specified.
1443
1444   On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
1445   and VLC_EGENERIC for some other error.
1446  *****************************************************************************/
1447 int
1448 E_(Open) ( vlc_object_t *p_this )
1449 {
1450     input_thread_t *        p_input = (input_thread_t *)p_this;
1451     thread_vcd_data_t *     p_vcd;
1452     char *                  psz_source;
1453     vcdinfo_itemid_t        itemid;
1454     bool                    b_play_ok;
1455     bool                    play_single_item = false;
1456
1457     p_input->pf_read        = VCDRead;
1458     p_input->pf_seek        = VCDSeek;
1459     p_input->pf_set_area    = VCDSetArea;
1460     p_input->pf_set_program = VCDSetProgram;
1461
1462     p_vcd = malloc( sizeof(thread_vcd_data_t) );
1463
1464     if( p_vcd == NULL )
1465     {
1466         LOG_ERR ("out of memory" );
1467         return VLC_ENOMEM;
1468     }
1469
1470     p_input->p_access_data = (void *)p_vcd;
1471     p_vcd->i_debug         = config_GetInt( p_this, MODULE_STRING "-debug" );
1472     p_vcd->in_still        = false;
1473     p_vcd->play_item.type  = VCDINFO_ITEM_TYPE_NOTFOUND;
1474
1475     /* Set where to log errors messages from libcdio. */
1476     p_vcd_input = (input_thread_t *)p_this;
1477     cdio_log_set_handler ( cdio_log_handler );
1478     vcd_log_set_handler ( vcd_log_handler );
1479
1480     psz_source = VCDParse( p_input, &itemid, &play_single_item );
1481
1482     if ( NULL == psz_source )
1483     {
1484       free( p_vcd );
1485       return( VLC_EGENERIC );
1486     }
1487
1488     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "source: %s: mrl: %s",
1489                psz_source, p_input->psz_name );
1490
1491     p_vcd->p_segments = NULL;
1492     p_vcd->p_entries  = NULL;
1493
1494     /* set up input  */
1495     p_input->i_mtu = VCD_DATA_ONCE;
1496
1497     vlc_mutex_lock( &p_input->stream.stream_lock );
1498
1499     /* If we are here we can control the pace... */
1500     p_input->stream.b_pace_control = 1;
1501
1502     p_input->stream.b_seekable = 1;
1503     p_input->stream.p_selected_area->i_size = 0;
1504     p_input->stream.p_selected_area->i_tell = 0;
1505
1506     vlc_mutex_unlock( &p_input->stream.stream_lock );
1507
1508     if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
1509     {
1510         msg_Warn( p_input, "could not open %s", psz_source );
1511         goto err_exit;
1512     }
1513
1514     p_vcd->b_svd= vcdinfo_get_tracksSVD(p_vcd->vcd);;
1515     
1516     /* Get track information. */
1517     p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
1518                                             vcdinfo_get_cd_image(p_vcd->vcd),
1519                                             &p_vcd->p_sectors );
1520     if( p_vcd->num_tracks < 0 )
1521         LOG_ERR ("unable to count tracks" );
1522     else if( p_vcd->num_tracks <= 1 )
1523         LOG_ERR ("no movie tracks found" );
1524     if( p_vcd->num_tracks <= 1)
1525     {
1526         vcdinfo_close( p_vcd->vcd );
1527         goto err_exit;
1528     }
1529
1530     /* Set stream and area data */
1531     vlc_mutex_lock( &p_input->stream.stream_lock );
1532
1533     /* Initialize ES structures */
1534     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
1535
1536     /* disc input method */
1537     p_input->stream.i_method = INPUT_METHOD_VCD;
1538
1539     p_input->stream.i_area_nb = 1;
1540
1541
1542     /* Initialize segment information. */
1543     VCDSegments( p_input );
1544
1545     /* Initialize track area information. */
1546     VCDTracks( p_input );
1547
1548     if( VCDEntryPoints( p_input ) < 0 )
1549     {
1550         msg_Warn( p_input, "could not read entry points, will not use them" );
1551         p_vcd->b_valid_ep = false;
1552     }
1553
1554     if( VCDLIDs( p_input ) < 0 )
1555     {
1556         msg_Warn( p_input, "could not read entry LIDs" );
1557     }
1558
1559     b_play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
1560
1561     vlc_mutex_unlock( &p_input->stream.stream_lock );
1562
1563     if ( ! b_play_ok ) {
1564       vcdinfo_close( p_vcd->vcd );
1565       goto err_exit;
1566     }
1567
1568     if( !p_input->psz_demux || !*p_input->psz_demux )
1569     {
1570 #if FIXED
1571       p_input->psz_demux = "vcdx";
1572 #else
1573       p_input->psz_demux = "ps";
1574 #endif
1575     }
1576
1577     p_vcd->p_intf = intf_Create( p_input, "vcdx" );
1578     p_vcd->p_intf->b_block = VLC_FALSE;
1579     intf_RunThread( p_vcd->p_intf );
1580
1581     InformationCreate( p_input );
1582
1583     if (play_single_item)
1584       VCDFixupPlayList( p_input, p_vcd, psz_source, &itemid, 
1585                         play_single_item );
1586
1587     free( psz_source );
1588
1589     var_AddCallback( p_this, "spu-es", VCDSPUCallback, NULL );
1590
1591
1592     return VLC_SUCCESS;
1593  err_exit:
1594     free( psz_source );
1595     free( p_vcd );
1596     return VLC_EGENERIC;
1597 }
1598
1599 /*****************************************************************************
1600  * Close: closes VCD releasing allocated memory.
1601  *****************************************************************************/
1602 void
1603 E_(Close) ( vlc_object_t *p_this )
1604 {
1605     input_thread_t *   p_input = (input_thread_t *)p_this;
1606     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1607
1608     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
1609
1610     var_DelCallback( p_this, "spu-es", VCDSPUCallback, NULL );
1611     vcdinfo_close( p_vcd->vcd );
1612
1613     free( p_vcd->p_entries );
1614     free( p_vcd->p_segments );
1615     free( p_vcd->p_sectors );
1616
1617     /* For reasons that are a mystery to me we don't have to deal with
1618        stopping, and destroying the p_vcd->p_intf thread. And if we do
1619        it causes problems upstream.
1620      */
1621     if( p_vcd->p_intf != NULL )
1622     {
1623         p_vcd->p_intf = NULL;
1624     }
1625
1626     free( p_vcd );
1627     p_input->p_access_data = NULL;
1628     p_vcd_input = NULL;
1629 }