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