]> git.sesse.net Git - vlc/blob - src/osd/osd_parser.c
Make PIX_FMT_RGBA dependend on ffmpeg with swscale. Patch by Remi Duraffort <ivoire...
[vlc] / src / osd / osd_parser.c
1 /*****************************************************************************
2  * osd_parser.c - The OSD Menu  parser core code.
3  *****************************************************************************
4  * Copyright (C) 2005 M2X
5  * $Id$
6  *
7  * Authors: Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #include <vlc/vlc.h>
29 #include <vlc_vout.h>
30 #include <vlc_config.h>
31
32 #include <vlc_keys.h>
33 #include <vlc_image.h>
34 #include <vlc_osd.h>
35 #include <vlc_charset.h>
36
37
38 #undef OSD_MENU_DEBUG
39
40 /*****************************************************************************
41  * Local prototypes
42  *****************************************************************************/
43 static const char *ppsz_button_states[] = { "unselect", "select", "pressed" };
44
45 /* OSD Menu structure support routines */
46 static osd_menu_t   *osd_MenuNew( osd_menu_t *, const char *, int, int );
47 static osd_button_t *osd_ButtonNew( const char *, int, int );
48 static osd_state_t  *osd_StateNew( vlc_object_t *, const char *, const char * );
49
50 static void osd_MenuFree  ( vlc_object_t *, osd_menu_t * );
51 static void osd_ButtonFree( vlc_object_t *, osd_button_t * );
52 static void osd_StatesFree( vlc_object_t *, osd_state_t * );
53
54 static picture_t *osd_LoadImage( vlc_object_t *, const char *);
55
56 /*****************************************************************************
57  * osd_LoadImage: loads the logo image into memory
58  *****************************************************************************/
59 static picture_t *osd_LoadImage( vlc_object_t *p_this, const char *psz_filename )
60 {
61     picture_t *p_pic = NULL;
62     image_handler_t *p_image;
63     video_format_t fmt_in, fmt_out;
64
65     memset( &fmt_in, 0, sizeof(video_format_t) );
66     memset( &fmt_out, 0, sizeof(video_format_t) );
67
68     fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
69     p_image = image_HandlerCreate( p_this );
70     if( p_image )
71     {
72         p_pic = image_ReadUrl( p_image, psz_filename, &fmt_in, &fmt_out );
73         image_HandlerDelete( p_image );
74     }
75     else msg_Err( p_this, "unable to handle this chroma" );
76
77     return p_pic;
78 }
79
80 /*****************************************************************************
81  * Create a new Menu structure
82  *****************************************************************************/
83 static osd_menu_t *osd_MenuNew( osd_menu_t *p_menu, const char *psz_path, int i_x, int i_y )
84 {
85     if( !p_menu ) return NULL;
86
87     p_menu->p_state = (osd_menu_state_t *) malloc( sizeof( osd_menu_state_t ) );
88     if( !p_menu->p_state )
89     {
90         msg_Err( p_menu, "Memory allocation for OSD Menu state failed" );
91         return NULL;
92     }
93
94     memset(p_menu->p_state, 0, sizeof(osd_menu_state_t));
95     if( psz_path != NULL )
96         p_menu->psz_path = strdup( psz_path );
97     else
98         p_menu->psz_path = NULL;
99     p_menu->i_x = i_x;
100     p_menu->i_y = i_y;
101     p_menu->i_style = OSD_MENU_STYLE_SIMPLE;
102
103     return p_menu;
104 }
105
106 /*****************************************************************************
107  * Free the menu
108  *****************************************************************************/
109 static void osd_MenuFree( vlc_object_t *p_this, osd_menu_t *p_menu )
110 {
111     msg_Dbg( p_this, "freeing menu" );
112     osd_ButtonFree( p_this, p_menu->p_button );
113     p_menu->p_button = NULL;
114     p_menu->p_last_button = NULL;
115     if( p_menu->psz_path ) free( p_menu->psz_path );
116     p_menu->psz_path = NULL;
117     if( p_menu->p_state ) free( p_menu->p_state );
118     p_menu->p_state = NULL;
119 }
120
121 /*****************************************************************************
122  * Create a new button
123  *****************************************************************************/
124 static osd_button_t *osd_ButtonNew( const char *psz_action, int i_x, int i_y )
125 {
126     osd_button_t *p_button = NULL;
127     p_button = (osd_button_t*) malloc( sizeof(osd_button_t) );
128     if( !p_button )
129         return NULL;
130
131     memset( p_button, 0, sizeof(osd_button_t) );
132     p_button->psz_action = strdup(psz_action);
133     p_button->psz_action_down = NULL;
134     p_button->p_feedback = NULL;
135     p_button->i_x = i_x;
136     p_button->i_y = i_y;
137
138     return p_button;
139 }
140
141 /*****************************************************************************
142  * Free a button
143  *****************************************************************************/
144 static void osd_ButtonFree( vlc_object_t *p_this, osd_button_t *p_button )
145 {
146     osd_button_t *p_current = p_button;
147     osd_button_t *p_next = NULL;
148     osd_button_t *p_prev = NULL;
149
150     /* First walk to the end. */
151     while( p_current->p_next )
152     {
153         p_next = p_current->p_next;
154         p_current = p_next;
155     }
156     /* Then free end first and walk to the start. */
157     while( p_current->p_prev )
158     {
159         msg_Dbg( p_this, "+ freeing button %s [%p]", p_current->psz_action, p_current );
160         p_prev = p_current->p_prev;
161         p_current = p_prev;
162         if( p_current->p_next )
163         {
164             if( p_current->p_next->psz_name )
165                 free( p_current->p_next->psz_name );
166             if( p_current->p_next->psz_action )
167                 free( p_current->p_next->psz_action );
168             if( p_current->p_next->psz_action_down )
169                 free( p_current->p_next->psz_action_down );
170             if( p_current->p_feedback && p_current->p_feedback->p_data_orig )
171                 free( p_current->p_feedback->p_data_orig );
172             if( p_current->p_feedback )
173                 free( p_current->p_feedback );
174
175             p_current->p_next->psz_action_down = NULL;
176             p_current->p_next->psz_action = NULL;
177             p_current->p_next->psz_name = NULL;
178             p_current->p_feedback = NULL;
179
180             /* Free all states first */
181             if( p_current->p_next->p_states )
182                 osd_StatesFree( p_this, p_current->p_next->p_states );
183             p_current->p_next->p_states = NULL;
184             if( p_current->p_next) free( p_current->p_next );
185             p_current->p_next = NULL;
186         }
187
188         if( p_current->p_up )
189         {
190             if( p_current->p_up->psz_name )
191                 free( p_current->p_up->psz_name );
192             if( p_current->p_up->psz_action )
193                 free( p_current->p_up->psz_action );
194             if( p_current->p_up->psz_action_down )
195                 free( p_current->p_up->psz_action_down );
196             if( p_current->p_feedback && p_current->p_feedback->p_data_orig )
197                 free( p_current->p_feedback->p_data_orig );
198             if( p_current->p_feedback )
199                 free( p_current->p_feedback );
200
201             p_current->p_up->psz_action_down = NULL;
202             p_current->p_up->psz_action = NULL;
203             p_current->p_up->psz_name = NULL;
204             p_current->p_feedback = NULL;
205
206             /* Free all states first */
207             if( p_current->p_up->p_states )
208                 osd_StatesFree( p_this, p_current->p_up->p_states );
209             p_current->p_up->p_states = NULL;
210             if( p_current->p_up ) free( p_current->p_up );
211             p_current->p_up = NULL;
212         }
213     }
214     /* Free the last one. */
215     if( p_button )
216     {
217         msg_Dbg( p_this, "+ freeing button %s [%p]", p_button->psz_action, p_button );
218         if( p_button->psz_name ) free( p_button->psz_name );
219         if( p_button->psz_action ) free( p_button->psz_action );
220         if( p_button->psz_action_down ) free( p_button->psz_action_down );
221         if( p_current->p_feedback && p_current->p_feedback->p_data_orig )
222             free( p_current->p_feedback->p_data_orig );
223         if( p_current->p_feedback )
224             free( p_current->p_feedback );
225
226         p_button->psz_name = NULL;
227         p_button->psz_action = NULL;
228         p_button->psz_action_down = NULL;
229         p_current->p_feedback = NULL;
230
231         if( p_button->p_states )
232             osd_StatesFree( p_this, p_button->p_states );
233         p_button->p_states = NULL;
234         free( p_button );
235         p_button = NULL;
236     }
237 }
238
239 /*****************************************************************************
240  * Create a new state image
241  *****************************************************************************/
242 static osd_state_t *osd_StateNew( vlc_object_t *p_this, const char *psz_file, const char *psz_state )
243 {
244     osd_state_t *p_state = NULL;
245     p_state = (osd_state_t*) malloc( sizeof(osd_state_t) );
246     if( !p_state )
247         return NULL;
248
249     memset( p_state, 0, sizeof(osd_state_t) );
250     p_state->p_pic = osd_LoadImage( p_this, psz_file );
251
252     if( psz_state )
253     {
254         p_state->psz_state = strdup( psz_state );
255         if( strncmp( ppsz_button_states[0], psz_state, strlen(ppsz_button_states[0]) ) == 0 )
256             p_state->i_state = OSD_BUTTON_UNSELECT;
257         else if( strncmp( ppsz_button_states[1], psz_state, strlen(ppsz_button_states[1]) ) == 0 )
258             p_state->i_state = OSD_BUTTON_SELECT;
259         else if( strncmp( ppsz_button_states[2], psz_state, strlen(ppsz_button_states[2]) ) == 0 )
260             p_state->i_state = OSD_BUTTON_PRESSED;
261     }
262     return p_state;
263 }
264
265 /*****************************************************************************
266  * Free state images
267  *****************************************************************************/
268 static void osd_StatesFree( vlc_object_t *p_this, osd_state_t *p_states )
269 {
270     osd_state_t *p_state = p_states;
271     osd_state_t *p_next = NULL;
272     osd_state_t *p_prev = NULL;
273
274     while( p_state->p_next )
275     {
276         p_next = p_state->p_next;
277         p_state = p_next;
278     }
279     /* Then free end first and walk to the start. */
280     while( p_state->p_prev )
281     {
282         msg_Dbg( p_this, " |- freeing state %s [%p]", p_state->psz_state, p_state );
283         p_prev = p_state->p_prev;
284         p_state = p_prev;
285         if( p_state->p_next )
286         {
287             if( p_state->p_next->p_pic && p_state->p_next->p_pic->p_data_orig )
288                 free( p_state->p_next->p_pic->p_data_orig );
289             if( p_state->p_next->p_pic ) free( p_state->p_next->p_pic );
290             p_state->p_next->p_pic = NULL;
291             if( p_state->p_next->psz_state ) free( p_state->p_next->psz_state );
292             p_state->p_next->psz_state = NULL;
293             free( p_state->p_next );
294             p_state->p_next = NULL;
295         }
296     }
297     /* Free the last one. */
298     if( p_states )
299     {
300         msg_Dbg( p_this, " |- freeing state %s [%p]", p_state->psz_state, p_states );
301         if( p_states->p_pic && p_states->p_pic->p_data_orig )
302             free( p_states->p_pic->p_data_orig );
303         if( p_states->p_pic ) free( p_states->p_pic );
304         p_states->p_pic = NULL;
305         if( p_state->psz_state ) free( p_state->psz_state );
306         p_state->psz_state = NULL;
307         free( p_states );
308         p_states = NULL;
309     }
310 }
311
312 /*****************************************************************************
313  * osd_ConfigLoader: Load and parse osd text configurationfile
314  *****************************************************************************/
315 int osd_ConfigLoader( vlc_object_t *p_this, const char *psz_file,
316     osd_menu_t **p_menu )
317 {
318     osd_button_t   *p_current = NULL; /* button currently processed */
319     osd_button_t   *p_prev = NULL;    /* previous processed button */
320
321 #define MAX_FILE_PATH 256
322     FILE       *fd = NULL;
323     int        result = 0;
324
325     msg_Dbg( p_this, "opening osdmenu definition file %s", psz_file );
326     fd = utf8_fopen( psz_file, "r" );
327     if( !fd )
328     {
329         msg_Err( p_this, "failed to open osdmenu definition file %s", psz_file );
330         return VLC_EGENERIC;
331     }
332
333     /* Read first line */
334     if( !feof( fd ) )
335     {
336         char action[25] = "";
337         char cmd[25] = "";
338         char path[MAX_FILE_PATH] = "";
339         char *psz_path = NULL;
340         size_t i_len = 0;
341         long pos = 0;
342
343         result = fscanf(fd, "%24s %255s", &action[0], &path[0] );
344
345         /* override images path ? */
346         psz_path = config_GetPsz( p_this, "osdmenu-file-path" );
347         if( psz_path )
348         {
349             /* psz_path is not null and therefor &path[0] cannot be NULL
350              * it might be null terminated.
351              */
352             strncpy( &path[0], psz_path, MAX_FILE_PATH );
353             free( psz_path );
354             psz_path = NULL;
355         }
356         /* NULL terminate before asking the length of path[] */
357         path[MAX_FILE_PATH-1] = '\0';
358         i_len = strlen(&path[0]);
359         if( i_len == MAX_FILE_PATH )
360             i_len--; /* truncate to prevent buffer overflow */
361 #if defined(WIN32) || defined(UNDER_CE)
362         if( (i_len > 0) && path[i_len] != '\\' )
363             path[i_len] = '\\';
364 #else
365         if( (i_len > 0) && path[i_len] != '/' )
366             path[i_len] = '/';
367 #endif
368         path[i_len+1] = '\0';
369         if( result == 0 || result == EOF )
370             goto error;
371         msg_Dbg( p_this, "osdmenu dir %s", &path[0] );
372
373         if( i_len == 0 )
374             *p_menu = osd_MenuNew( *p_menu, NULL, 0, 0 );
375         else
376             *p_menu = osd_MenuNew( *p_menu, &path[0], 0, 0 );
377
378         /* Peek for 'style' argument */
379         pos = ftell( fd );
380         if( pos < 0 )
381                 goto error;
382
383         result = fscanf(fd, "%24s %24s", &cmd[0], &action[0] );
384         if( result == 0 || result == EOF )
385             goto error;
386
387         msg_Dbg( p_this, "osdmenu %s %s", &cmd[0], &action[0] );
388         if( strncmp( &cmd[0], "style", 5 ) == 0 )
389         {
390             if( strncmp( &action[0], "default", 7) == 0 )
391             {
392                 (*p_menu)->i_style = OSD_MENU_STYLE_SIMPLE;
393             }
394             else if( strncmp( &action[0], "concat", 6) == 0 )
395             {
396                 (*p_menu)->i_style = OSD_MENU_STYLE_CONCAT;
397             }
398         }
399         else
400         {
401             result = fseek( fd, pos, SEEK_SET );
402             if( result < 0 )
403                 goto error;
404         }
405     }
406
407     if( !*p_menu )
408         goto error;
409
410     /* read successive lines */
411     while( !feof( fd ) )
412     {
413         osd_state_t   *p_state_current = NULL; /* button state currently processed */
414         osd_state_t   *p_state_prev = NULL;    /* previous state processed button */
415
416         char cmd[25] = "";
417         char action[25] = "";
418         char state[25]  = "";
419         char file[256]  = "";
420         char path[512]  = "";
421         int  i_x = 0;
422         int  i_y = 0;
423
424         result = fscanf( fd, "%24s %24s (%d,%d)", &cmd[0], &action[0], &i_x, &i_y );
425         if( result == 0 )
426             goto error;
427         if( strncmp( &cmd[0], "action", 6 ) != 0 )
428             break;
429         msg_Dbg( p_this, " + %s hotkey=%s (%d,%d)", &cmd[0], &action[0], i_x, i_y );
430
431         p_prev = p_current;
432         p_current = osd_ButtonNew( &action[0], i_x, i_y );
433         if( !p_current )
434             goto error;
435
436         if( p_prev )
437             p_prev->p_next = p_current;
438         else
439             (*p_menu)->p_button = p_current;
440         p_current->p_prev = p_prev;
441
442         /* parse all states */
443         while( !feof( fd ) )
444         {
445             char type[25] = "";
446
447             result = fscanf( fd, "\t%24s", &state[0] );
448             if( result == 0 )
449                 goto error;
450
451             /* FIXME: We only parse one level deep now */
452             if( strncmp( &state[0], "action", 6 ) == 0 )
453             {
454                 osd_button_t   *p_up = NULL;
455
456                 result = fscanf( fd, "%24s (%d,%d)", &action[0], &i_x, &i_y );
457                 if( result == 0 )
458                     goto error;
459                 /* create new button */
460                 p_up = osd_ButtonNew( &action[0], i_x, i_y );
461                 if( !p_up )
462                     goto error;
463                 /* Link to list */
464                 p_up->p_down = p_current;
465                 p_current->p_up = p_up;
466                 msg_Dbg( p_this, " + (menu up) hotkey=%s (%d,%d)", &action[0], i_x, i_y );
467                 /* Parse type state */
468                 result = fscanf( fd, "\t%24s %24s", &cmd[0], &type[0] );
469                 if( result == 0 )
470                     goto error;
471                 if( strncmp( &cmd[0], "type", 4 ) == 0 )
472                 {
473                     if( strncmp( &type[0], "volume", 6 ) == 0 )
474                     {
475                         (*p_menu)->p_state->p_volume = p_up;
476                         msg_Dbg( p_this, " + type=%s", &type[0] );
477                     }
478                 }
479                 /* Parse range state */
480                 result = fscanf( fd, "\t%24s", &state[0] );
481                 if( result == 0 )
482                     goto error;
483                 /* Parse the range state */
484                 if( strncmp( &state[0], "range", 5 ) == 0 )
485                 {
486                     osd_state_t   *p_range_current = NULL; /* range state currently processed */
487                     osd_state_t   *p_range_prev = NULL;    /* previous state processed range */
488                     int i_index = 0;
489
490                     p_up->b_range = VLC_TRUE;
491
492                     result = fscanf( fd, "\t%24s", &action[0] );
493                     if( result == 0 )
494                         goto error;
495
496                     result = fscanf( fd, "\t%d", &i_index );
497                     if( result == 0 )
498                         goto error;
499
500                     msg_Dbg( p_this, " + (menu up) hotkey down %s, file=%s%s", &action[0], (*p_menu)->psz_path, &file[0] );
501
502                     if( p_up->psz_action_down ) free( p_up->psz_action_down );
503                     p_up->psz_action_down = strdup( &action[0] );
504
505                     /* Parse range contstruction :
506                      * range <hotkey>
507                      *      <state1> <file1>
508                      *
509                      *      <stateN> <fileN>
510                      * end
511                      */
512                     while( !feof( fd ) )
513                     {
514                         result = fscanf( fd, "\t%255s", &file[0] );
515                         if( result == 0 )
516                             goto error;
517                         if( strncmp( &file[0], "end", 3 ) == 0 )
518                             break;
519
520                         p_range_prev = p_range_current;
521
522                         if( (*p_menu)->psz_path )
523                         {
524                             size_t i_path_size = strlen( (*p_menu)->psz_path );
525                             size_t i_file_size = strlen( &file[0] );
526
527                             strncpy( &path[0], (*p_menu)->psz_path, i_path_size );
528                             strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );
529                             path[ i_path_size + i_file_size ] = '\0';
530
531                             p_range_current = osd_StateNew( p_this, &path[0], "pressed" );
532                         }
533                         else /* absolute paths are used. */
534                             p_range_current = osd_StateNew( p_this, &file[0], "pressed" );
535
536                         if( !p_range_current || !p_range_current->p_pic )
537                             goto error;
538
539                         /* increment the number of ranges for this button */
540                         p_up->i_ranges++;
541
542                         if( p_range_prev )
543                             p_range_prev->p_next = p_range_current;
544                         else
545                             p_up->p_states = p_range_current;
546                         p_range_current->p_prev = p_range_prev;
547
548                         msg_Dbg( p_this, "  |- range=%d, file=%s%s",
549                                 p_up->i_ranges,
550                                 (*p_menu)->psz_path, &file[0] );
551                     }
552                     if( i_index > 0 )
553                     {
554                         osd_state_t *p_range = NULL;
555
556                         /* Find the default index for state range */
557                         p_range = p_up->p_states;
558                         while( (--i_index > 0) && p_range->p_next )
559                         {
560                             osd_state_t *p_temp = NULL;
561                             p_temp = p_range->p_next;
562                             p_range = p_temp;
563                         }
564                         p_up->p_current_state = p_range;
565                     }
566                     else p_up->p_current_state = p_up->p_states;
567
568                 }
569                 result = fscanf( fd, "\t%24s", &state[0] );
570                 if( result == 0 )
571                     goto error;
572                 if( strncmp( &state[0], "end", 3 ) != 0 )
573                     goto error;
574
575                 /* Continue at the beginning of the while() */
576                 continue;
577             }
578
579             /* Parse the range state */
580             if( strncmp( &state[0], "range", 5 ) == 0 )
581             {
582                 osd_state_t   *p_range_current = NULL; /* range state currently processed */
583                 osd_state_t   *p_range_prev = NULL;    /* previous state processed range */
584                 int i_index = 0;
585
586                 p_current->b_range = VLC_TRUE;
587
588                 result = fscanf( fd, "\t%24s", &action[0] );
589                 if( result == 0 )
590                     goto error;
591
592                 result = fscanf( fd, "\t%d", &i_index );
593                 if( result == 0 )
594                     goto error;
595
596                 msg_Dbg( p_this, " + hotkey down %s, file=%s%s", &action[0], (*p_menu)->psz_path, &file[0] );
597                 if( p_current->psz_action_down ) free( p_current->psz_action_down );
598                 p_current->psz_action_down = strdup( &action[0] );
599
600                 /* Parse range contstruction :
601                  * range <hotkey>
602                  *      <state1> <file1>
603                  *
604                  *      <stateN> <fileN>
605                  * end
606                  */
607                 while( !feof( fd ) )
608                 {
609                     result = fscanf( fd, "\t%255s", &file[0] );
610                     if( result == 0 )
611                         goto error;
612                     if( strncmp( &file[0], "end", 3 ) == 0 )
613                         break;
614
615                     p_range_prev = p_range_current;
616
617                     if( (*p_menu)->psz_path )
618                     {
619                         size_t i_path_size = strlen( (*p_menu)->psz_path );
620                         size_t i_file_size = strlen( &file[0] );
621
622                         strncpy( &path[0], (*p_menu)->psz_path, i_path_size );
623                         strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );
624                         path[ i_path_size + i_file_size ] = '\0';
625
626                         p_range_current = osd_StateNew( p_this, &path[0], "pressed" );
627                     }
628                     else /* absolute paths are used. */
629                         p_range_current = osd_StateNew( p_this, &file[0], "pressed" );
630
631                     if( !p_range_current || !p_range_current->p_pic )
632                         goto error;
633
634                     /* increment the number of ranges for this button */
635                     p_current->i_ranges++;
636
637                     if( p_range_prev )
638                         p_range_prev->p_next = p_range_current;
639                     else
640                         p_current->p_states = p_range_current;
641                     p_range_current->p_prev = p_range_prev;
642
643                     msg_Dbg( p_this, "  |- range=%d, file=%s%s",
644                             p_current->i_ranges,
645                             (*p_menu)->psz_path, &file[0] );
646                 }
647                 if( i_index > 0 )
648                 {
649                     osd_state_t *p_range = NULL;
650
651                     /* Find the default index for state range */
652                     p_range = p_current->p_states;
653                     while( (--i_index > 0) && p_range->p_next )
654                     {
655                         osd_state_t *p_temp = NULL;
656                         p_temp = p_range->p_next;
657                         p_range = p_temp;
658                     }
659                     p_current->p_current_state = p_range;
660                 }
661                 else p_current->p_current_state = p_current->p_states;
662                 /* Continue at the beginning of the while() */
663                 continue;
664             }
665             if( strncmp( &state[0], "end", 3 ) == 0 )
666                 break;
667
668             result = fscanf( fd, "\t%255s", &file[0] );
669             if( result == 0 )
670                 goto error;
671
672             p_state_prev = p_state_current;
673
674             if( ( strncmp( ppsz_button_states[0], &state[0], strlen(ppsz_button_states[0]) ) != 0 ) &&
675                 ( strncmp( ppsz_button_states[1], &state[0], strlen(ppsz_button_states[1]) ) != 0 ) &&
676                 ( strncmp( ppsz_button_states[2], &state[0], strlen(ppsz_button_states[2]) ) != 0 ) )
677             {
678                 msg_Err( p_this, "invalid button state %s for button %s "
679                          "expected %u: unselect, select or pressed)",
680                          &state[0], &action[0], (unsigned)strlen(&state[0]));
681                 goto error;
682             }
683
684             if( (*p_menu)->psz_path )
685             {
686                 size_t i_path_size = strlen( (*p_menu)->psz_path );
687                 size_t i_file_size = strlen( &file[0] );
688
689                 strncpy( &path[0], (*p_menu)->psz_path, i_path_size );
690                 strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );
691                 path[ i_path_size + i_file_size ] = '\0';
692
693                 p_state_current = osd_StateNew( p_this, &path[0], &state[0] );
694             }
695             else /* absolute paths are used. */
696                 p_state_current = osd_StateNew( p_this, &file[0], &state[0] );
697
698             if( !p_state_current || !p_state_current->p_pic )
699                 goto error;
700
701             if( p_state_prev )
702                 p_state_prev->p_next = p_state_current;
703             else
704                 p_current->p_states = p_state_current;
705             p_state_current->p_prev = p_state_prev;
706
707             msg_Dbg( p_this, " |- state=%s, file=%s%s", &state[0], (*p_menu)->psz_path, &file[0] );
708         }
709         p_current->p_current_state = p_current->p_states;
710     }
711
712     /* Find the last button and store its pointer.
713      * The OSD menu behaves like a roundrobin list.
714      */
715     p_current = (*p_menu)->p_button;
716     while( p_current && p_current->p_next )
717     {
718         osd_button_t *p_temp = NULL;
719         p_temp = p_current->p_next;
720         p_current = p_temp;
721     }
722     (*p_menu)->p_last_button = p_current;
723     fclose( fd );
724     return 0;
725
726 #undef MAX_FILE_PATH
727 error:
728     msg_Err( p_this, "parsing file failed (returned %d)", result );
729     osd_MenuFree( p_this, *p_menu );
730     fclose( fd );
731     return 1;
732 }
733
734 /*****************************************************************************
735  * osd_ConfigUnload: Load and parse osd text configurationfile
736  *****************************************************************************/
737 void osd_ConfigUnload( vlc_object_t *p_this, osd_menu_t **p_osd)
738 {
739     msg_Dbg( p_this, "unloading OSD menu structure" );
740     osd_MenuFree( p_this, *p_osd );
741 }