]> git.sesse.net Git - vlc/blob - src/misc/rsc_files.c
(previous commit foired)
[vlc] / src / misc / rsc_files.c
1 /*****************************************************************************
2  * rsc_files.c: resources files manipulation functions
3  * This library describe a general format used to store 'resources'. Resources
4  * can be anything, including pictures, audio streams, and so on.
5  *****************************************************************************
6  * Copyright (C) 1999, 2000 VideoLAN
7  *
8  * Authors: Vincent Seguin <seguin@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Format of a resource file:
27  *  offset      type        meaning
28  *  0           char[2]     "RF" (magic number)
29  *  2           char[2]     "VL" (minor magic number, ignored)
30  *  4           u16         i_type: resource file type. This is to allow
31  *                          different versions of the resources types constants.
32  *  6           u16         i_size: number of entries in the resources table.
33  * {
34  *  +0          char[32]    resource name (ASCIIZ or ASCII[32])
35  *  +32         u16         resource type
36  *  +34         u64         data offset in bytes, from beginning of file
37  *  +42         u64         data size in bytes
38  * } * i_size
39  *****************************************************************************/
40
41 /*****************************************************************************
42  * Preamble
43  *****************************************************************************/
44 #include "defs.h"
45
46 #include <errno.h>                                                  /* errno */
47 #include <fcntl.h>                                                 /* open() */
48 #include <stdlib.h>                                                /* free() */
49 #include <string.h>                                /* strerror(), strncopy() */
50 #include <unistd.h>                     /* read(), close(), lseek(), write() */
51
52 #include "config.h"
53 #include "common.h"
54 #include "mtime.h"
55
56 #include "rsc_files.h"
57
58 #include "intf_msg.h"
59
60 /*****************************************************************************
61  * CreateResourceFile: create a new resource file
62  *****************************************************************************
63  * Creates a new resource file. The file is opened read/write and erased if
64  * it already exists.
65  *****************************************************************************
66  * Messages type: rsc, major code 101
67  *****************************************************************************/
68 resource_file_t *CreateResourceFile( char *psz_filename, int i_type, int i_size,
69                                      int i_mode )
70 {
71     resource_file_t *   p_file;                            /* new descriptor */
72     int                 i_index;                           /* resource index */
73
74     /* Create descriptor and tables */
75     p_file = malloc( sizeof(resource_file_t) );
76     if( p_file == NULL )
77     {
78         intf_ErrMsg("rsc error 101-1: %s", strerror(errno));
79         return( NULL );
80     }
81     p_file->p_resource = malloc( sizeof(resource_descriptor_t) * i_size );
82     if( p_file->p_resource == NULL )
83     {
84         intf_ErrMsg("rsc error 101-2: %s", strerror(errno));
85         free( p_file );
86         return( NULL );
87     }
88
89     /* Open file */
90     p_file->i_file = open( psz_filename, O_CREAT | O_RDWR, i_mode );
91     if( p_file->i_file == -1 )                                      /* error */
92     {
93         intf_ErrMsg("rsc error 101-3: %s: %s", psz_filename, strerror(errno) );
94         free( p_file->p_resource );
95         free( p_file );
96     }
97
98     /* Initialize tables */
99     p_file->i_type = i_type;
100     p_file->i_size = i_size;
101     p_file->b_read_only = 0;
102     for( i_index = 0; i_index < i_size; i_index++ )
103     {
104         p_file->p_resource[i_index].i_type = EMPTY_RESOURCE;
105     }
106
107     return( p_file );
108 }
109
110 /*****************************************************************************
111  * OpenResourceFile: open an existing resource file read-only
112  *****************************************************************************
113  * Open an existing resource file. i_flags should be O_RDONLY or O_RDWR. An
114  * error will occurs if the file does not exists.
115  *****************************************************************************
116  * Messages type: rsc, major code 102
117  *****************************************************************************/
118 resource_file_t *OpenResourceFile( char *psz_filename, int i_type, int i_flags )
119 {
120     resource_file_t *   p_file;                            /* new descriptor */
121     int                 i_index;                           /* resource index */
122     byte_t              p_buffer[50];                              /* buffer */
123
124     /* Create descriptor and tables */
125     p_file = malloc( sizeof(resource_file_t) );
126     if( p_file == NULL )
127     {
128         intf_ErrMsg("rsc error 102-1: %s", strerror(errno));
129         return( NULL );
130     }
131
132     /* Open file */
133     p_file->i_file = open( psz_filename, i_flags );
134     if( p_file->i_file == -1 )                                      /* error */
135     {
136         intf_ErrMsg("rsc error 102-2: %s: %s", psz_filename, strerror(errno) );
137         free( p_file );
138         return( NULL );
139     }
140
141     /* Read header */
142     if( read( p_file->i_file, p_buffer, 8 ) != 8)
143     {
144         intf_ErrMsg("rsc error 102-3: %s: unexpected end of file (not a resource file ?)");
145         close( p_file->i_file );
146         free( p_file);
147         return( NULL );
148     }
149     if( (p_buffer[0] != 'R') || (p_buffer[0] != 'F') || (*(u16 *)(p_buffer + 4) != i_type) )
150     {
151         intf_ErrMsg("rsc error 102-4: %s is not a valid resource file or has incorrect type", psz_filename);
152         close( p_file->i_file );
153         free( p_file );
154         return( NULL );
155     }
156     p_file->i_type = i_type;
157     p_file->i_size = *(u16 *)(p_buffer + 6);
158     intf_DbgMsg("rsc debug 102-1: %s opened, %d resources", psz_filename, p_file->i_size);
159
160     /* Allocate tables */
161     p_file->p_resource = malloc( sizeof(resource_descriptor_t) * p_file->i_size );
162     if( p_file->p_resource == NULL )
163     {
164         intf_ErrMsg("rsc error 102-5: %s", strerror(errno));
165         close( p_file->i_file );
166         free( p_file );
167         return( NULL );
168     }
169
170     /* Initialize table */
171     p_file->b_up_to_date = 1;
172     p_file->b_read_only = ( i_flags & O_RDWR ) ? 0 : 1;
173     for( i_index = 0; i_index < p_file->i_size; i_index++ )
174     {
175         if( read( p_file->i_file, p_buffer, 50 ) != 50 )
176         {
177             intf_ErrMsg("rsc error 102-6: %s: unexpected end of file", psz_filename);
178             close( p_file->i_file );
179             free( p_file->p_resource );
180             free( p_file );
181             return( NULL );
182         }
183         memcpy( p_file->p_resource[i_index].psz_name, p_buffer, 32 );
184         p_file->p_resource[i_index].psz_name[RESOURCE_MAX_NAME] = '\0';
185         p_file->p_resource[i_index].i_type =    *(u16 *)(p_buffer + 32 );
186         p_file->p_resource[i_index].i_offset =  *(u64 *)(p_buffer + 34 );
187         p_file->p_resource[i_index].i_size =    *(u64 *)(p_buffer + 42 );
188     }
189
190     return( p_file );
191 }
192
193 /*****************************************************************************
194  * UpdateResourceFile: write the resources table in a resource file
195  *****************************************************************************
196  * This function writes resources table in the resource file. This is
197  * automatically done when the file is closed, but can also be done manually.
198  *****************************************************************************
199  * Messages type: rsc, major code 103
200  *****************************************************************************/
201 int UpdateResourceFile( resource_file_t *p_file )
202 {
203     byte_t      p_buffer[50];                                      /* buffer */
204     int         i_index;                                   /* resource index */
205
206 #ifdef DEBUG
207     if( p_file->b_read_only )
208     {
209         intf_DbgMsg("rsc debug 103-1: can't update a read-only file");
210         return( -1 );
211     }
212 #endif
213
214     /* Seek beginning of file */
215     if( lseek( p_file->i_file, 0, SEEK_SET ) )
216     {
217         intf_ErrMsg("rsc error 103-1: %s", strerror(errno));
218         return( -2 );
219     }
220
221     /* Write header */
222     p_buffer[0] =               'R';
223     p_buffer[1] =               'F';
224     p_buffer[2] =               'V';
225     p_buffer[3] =               'L';
226     *(u16 *)(p_buffer + 4) =    p_file->i_type;
227     *(u16 *)(p_buffer + 6) =    p_file->i_size;
228     if( write( p_file->i_file, p_buffer, 8 ) != 8 )
229     {
230         intf_ErrMsg("rsc error 103-2: %s", strerror(errno));
231         return( -3 );
232     }
233
234     /* Write resources table */
235     for( i_index = 0; i_index < p_file->i_size; i_index++ )
236     {
237         memcpy( p_buffer, p_file->p_resource[i_index].psz_name, 32 );
238         *(u16 *)(p_buffer + 32) =   p_file->p_resource[i_index].i_type;
239         *(u64 *)(p_buffer + 34) =   p_file->p_resource[i_index].i_offset;
240         *(u64 *)(p_buffer + 42) =   p_file->p_resource[i_index].i_size;
241         if( write( p_file->i_file, p_buffer, 8 ) != 8 )
242         {
243             intf_ErrMsg("rsc error 103-3: %s", strerror(errno));
244             return( -4 );
245         }
246     }
247
248     /* Mark file as up to date */
249     p_file->b_up_to_date = 1;
250
251     return( 0 );
252 }
253
254 /*****************************************************************************
255  * CloseResourceFile: close a resource file
256  *****************************************************************************
257  * Updates the resources table if required, and close the file. It returns non
258  * 0 in case of error.
259  *****************************************************************************
260  * Messages type: rsc, major code 104
261  *****************************************************************************/
262 int CloseResourceFile( resource_file_t *p_file )
263 {
264     /* Update table */
265     if( !p_file->b_up_to_date && ( UpdateResourceFile( p_file ) < 0 ) )
266     {
267         return( -1 );
268     }
269
270     /* Close and destroy descriptor */
271     if( close( p_file->i_file ) )
272     {
273         intf_ErrMsg("rsc error 104-1: %s", strerror(errno));
274         return( -2 );
275     }
276     free( p_file->p_resource );
277     free( p_file );
278     return( 0 );
279 }
280
281 /*****************************************************************************
282  * SeekResource: seek a resource in a resource file
283  *****************************************************************************
284  * Look for a resource in file and place the "reading head" at the beginning of
285  * the resource data.
286  * In case of success, the resource number is returned. Else, a negative number
287  * is returned.
288  *****************************************************************************
289  * Messages type: rsc, major code 105
290  *****************************************************************************/
291 int SeekResource( resource_file_t *p_file, char *psz_name, int i_type )
292 {
293     int     i_index;                                       /* resource index */
294
295     /* Look for resource in table */
296     for( i_index = 0;
297          (i_index < p_file->i_size)
298              && ((i_type != p_file->p_resource[i_index].i_type )
299                  || strcmp(psz_name, p_file->p_resource[i_index].psz_name));
300          i_index++ )
301     {
302         ;
303     }
304     if( i_index == p_file->i_size )
305     {
306         intf_ErrMsg("rsc error 105-1: unknown resource %s.%d", psz_name, i_type);
307         return( -1 );
308     }
309
310     /* Seek */
311     if( lseek( p_file->i_file, p_file->p_resource[i_index].i_offset, SEEK_SET ) )
312     {
313         intf_ErrMsg("rsc error 105-2: can not reach %s.%d: %s", psz_name,
314                     i_type, strerror(errno));
315         return( -2 );
316     }
317
318     return( i_index );
319 }
320
321 /*****************************************************************************
322  * ReadResource: read a resource
323  *****************************************************************************
324  * Read a resource from a file. The function returns a negative value in case
325  * of error, and 0 in case of success.
326  *****************************************************************************
327  * Messages type: rsc, major code 106
328  *****************************************************************************/
329 int ReadResource( resource_file_t *p_file, char *psz_name, int i_type,
330                   size_t max_size, byte_t *p_data )
331 {
332     int i_index;                                           /* resource index */
333
334     /* Seek resource */
335     i_index = SeekResource( p_file, psz_name, i_type );
336     if( i_index < 0 )
337     {
338         return( -1 );
339     }
340
341     /* Check if buffer is large enough */
342     if( max_size < p_file->p_resource[i_index].i_size )
343     {
344         intf_ErrMsg("rsc error 106-1: buffer is too small");
345         return( -2 );
346     }
347
348     /* Read data */
349     if( read( p_file->i_file, p_data, p_file->p_resource[i_index].i_size )
350         != p_file->p_resource[i_index].i_size )
351     {
352         intf_ErrMsg("rsc error 106-2: can not read %s.%d: %s",
353                     p_file->p_resource[i_index].psz_name,
354                     p_file->p_resource[i_index].i_type,
355                     strerror(errno));
356         return( -3 );
357     }
358
359     return( 0 );
360 }
361
362 /*****************************************************************************
363  * WriteResource: write a resource
364  *****************************************************************************
365  * Append a resource at the end of the file. It returns non 0 on error.
366  *****************************************************************************
367  * Messages type: rsc, major code 107
368  *****************************************************************************/
369 int WriteResource( resource_file_t *p_file, char *psz_name, int i_type,
370                    size_t size, byte_t *p_data )
371 {
372     int i_index;                                           /* resource index */
373     int i_tmp_index;                             /* temporary resource index */
374     u64 i_offset;                                                  /* offset */
375
376 #ifdef DEBUG
377     if( p_file->b_read_only )
378     {
379         intf_DbgMsg("rsc debug 107-1: can not write to a read-only resource file");
380         return( -1 );
381     }
382 #endif
383
384     /* Look for an empty place in the resources table */
385     i_index = -1;
386     i_offset = p_file->i_size * 50 + 8;
387     for( i_tmp_index = 0; i_tmp_index < p_file->i_size; i_tmp_index++ )
388     {
389         if( p_file->p_resource[i_tmp_index].i_type != EMPTY_RESOURCE)
390         {
391             i_offset = MAX( i_offset, p_file->p_resource[i_tmp_index].i_offset
392                             + p_file->p_resource[i_tmp_index].i_size );
393         }
394         else if( i_index == -1 )
395         {
396             i_index = i_tmp_index;
397         }
398     }
399     if( i_index == -1 )
400     {
401         intf_ErrMsg("rsc error 107-1: resources table is full");
402         return( -1 );
403     }
404
405     /* Seek end of file */
406     if( lseek( p_file->i_file, i_offset, SEEK_SET ) )
407     {
408         intf_ErrMsg("rsc error 107-2: %s", strerror(errno));
409         return( -2 );
410     }
411
412     /* Write data */
413     if( write( p_file->i_file, p_data, size ) != size )
414     {
415         intf_ErrMsg("rsc error 107-3: %s", strerror(errno));
416         return( -3 );
417     }
418
419     /* Update table */
420     strncpy( p_file->p_resource[i_index].psz_name, psz_name, RESOURCE_MAX_NAME );
421     p_file->p_resource[i_index].psz_name[RESOURCE_MAX_NAME] = '\0';
422     p_file->p_resource[i_index].i_type = i_type;
423     p_file->p_resource[i_index].i_offset = i_offset;
424     p_file->p_resource[i_index].i_size = size;
425     p_file->b_up_to_date = 0;
426
427     return( 0 );
428 }