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