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