Got rid of the Wine internal lstrcpy* functions and of winestring.h.
[wine] / dlls / version / resource.c
1 /* 
2  * Implementation of VERSION.DLL - Resource Access routines
3  * 
4  * Copyright 1996,1997 Marcus Meissner
5  * Copyright 1997 David Cuthbert
6  * Copyright 1999 Ulrich Weigand
7  */
8
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13
14 #include "winbase.h"
15 #include "winnls.h"
16 #include "wine/unicode.h"
17
18 #include "neexe.h"
19 #include "module.h"
20 #include "winver.h"
21 #include "lzexpand.h"
22 #include "debugtools.h"
23
24 DEFAULT_DEBUG_CHANNEL(ver);
25
26
27 /**********************************************************************
28  *  find_entry_by_id
29  *
30  * Find an entry by id in a resource directory
31  * Copied from loader/pe_resource.c
32  */
33 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
34                                                          WORD id, const void *root )
35 {
36     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
37     int min, max, pos;
38
39     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
40     min = dir->NumberOfNamedEntries;
41     max = min + dir->NumberOfIdEntries - 1;
42     while (min <= max)
43     {
44         pos = (min + max) / 2;
45         if (entry[pos].u1.Id == id)
46             return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s2.OffsetToDirectory);
47         if (entry[pos].u1.Id > id) max = pos - 1;
48         else min = pos + 1;
49     }
50     return NULL;
51 }
52
53
54 /**********************************************************************
55  *  find_entry_default
56  *
57  * Find a default entry in a resource directory
58  * Copied from loader/pe_resource.c
59  */
60 static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir,
61                                                            const void *root )
62 {
63     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
64
65     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
66     return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry->u2.s2.OffsetToDirectory);
67 }
68
69
70 /**********************************************************************
71  *  find_entry_by_name
72  *
73  * Find an entry by name in a resource directory
74  * Copied from loader/pe_resource.c
75  */
76 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_name( const IMAGE_RESOURCE_DIRECTORY *dir,
77                                                            LPCSTR name, const void *root )
78 {
79     const IMAGE_RESOURCE_DIRECTORY *ret = NULL;
80     LPWSTR nameW;
81     DWORD namelen;
82
83     if (!HIWORD(name)) return find_entry_by_id( dir, LOWORD(name), root );
84     if (name[0] == '#')
85     {
86         return find_entry_by_id( dir, atoi(name+1), root );
87     }
88
89     namelen = MultiByteToWideChar( CP_ACP, 0, name, -1, NULL, 0 );
90     if ((nameW = HeapAlloc( GetProcessHeap(), 0, namelen * sizeof(WCHAR) )))
91     {
92         const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
93         const IMAGE_RESOURCE_DIR_STRING_U *str;
94         int min, max, res, pos;
95
96         MultiByteToWideChar( CP_ACP, 0, name, -1, nameW, namelen );
97         namelen--;  /* remove terminating null */
98         entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
99         min = 0;
100         max = dir->NumberOfNamedEntries - 1;
101         while (min <= max)
102         {
103             pos = (min + max) / 2;
104             str = (IMAGE_RESOURCE_DIR_STRING_U *)((char *)root + entry[pos].u1.s1.NameOffset);
105             res = strncmpiW( nameW, str->NameString, str->Length );
106             if (!res && namelen == str->Length)
107             {
108                 ret = (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s2.OffsetToDirectory);
109                 break;
110             }
111             if (res < 0) max = pos - 1;
112             else min = pos + 1;
113         }
114         HeapFree( GetProcessHeap(), 0, nameW );
115     }
116     return ret;
117 }
118
119
120 /***********************************************************************
121  *           read_xx_header         [internal]
122  */
123 static int read_xx_header( HFILE lzfd )
124 {
125     IMAGE_DOS_HEADER mzh;
126     char magic[3];
127
128     LZSeek( lzfd, 0, SEEK_SET );
129     if ( sizeof(mzh) != LZRead( lzfd, &mzh, sizeof(mzh) ) )
130         return 0;
131     if ( mzh.e_magic != IMAGE_DOS_SIGNATURE )
132         return 0;
133
134     LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
135     if ( 2 != LZRead( lzfd, magic, 2 ) )
136         return 0;
137
138     LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
139
140     if ( magic[0] == 'N' && magic[1] == 'E' )
141         return IMAGE_OS2_SIGNATURE;
142     if ( magic[0] == 'P' && magic[1] == 'E' )
143         return IMAGE_NT_SIGNATURE;
144
145     magic[2] = '\0';
146     WARN("Can't handle %s files.\n", magic );
147     return 0;
148 }
149
150 /***********************************************************************
151  *           load_ne_resource         [internal]
152  */
153 static BOOL find_ne_resource( HFILE lzfd, LPCSTR typeid, LPCSTR resid,
154                                 DWORD *resLen, DWORD *resOff )
155 {
156     IMAGE_OS2_HEADER nehd;
157     NE_TYPEINFO *typeInfo;
158     NE_NAMEINFO *nameInfo;
159     DWORD nehdoffset;
160     LPBYTE resTab;
161     DWORD resTabSize;
162     int count;
163
164     /* Read in NE header */ 
165     nehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
166     if ( sizeof(nehd) != LZRead( lzfd, &nehd, sizeof(nehd) ) ) return 0;
167
168     resTabSize = nehd.ne_restab - nehd.ne_rsrctab; 
169     if ( !resTabSize )
170     {
171         TRACE("No resources in NE dll\n" );
172         return FALSE;
173     }
174
175     /* Read in resource table */ 
176     resTab = HeapAlloc( GetProcessHeap(), 0, resTabSize );
177     if ( !resTab ) return FALSE;
178
179     LZSeek( lzfd, nehd.ne_rsrctab + nehdoffset, SEEK_SET );
180     if ( resTabSize != LZRead( lzfd, resTab, resTabSize ) )
181     {
182         HeapFree( GetProcessHeap(), 0, resTab );
183         return FALSE;
184     }
185
186     /* Find resource */
187     typeInfo = (NE_TYPEINFO *)(resTab + 2);
188
189     if (HIWORD(typeid) != 0)  /* named type */
190     {
191         BYTE len = strlen( typeid );
192         while (typeInfo->type_id)
193         {
194             if (!(typeInfo->type_id & 0x8000))
195             {
196                 BYTE *p = resTab + typeInfo->type_id;
197                 if ((*p == len) && !strncasecmp( p+1, typeid, len )) goto found_type;
198             }
199             typeInfo = (NE_TYPEINFO *)((char *)(typeInfo + 1) +
200                                        typeInfo->count * sizeof(NE_NAMEINFO));
201         }
202     }
203     else  /* numeric type id */
204     {
205         WORD id = LOWORD(typeid) | 0x8000;
206         while (typeInfo->type_id)
207         {
208             if (typeInfo->type_id == id) goto found_type;
209             typeInfo = (NE_TYPEINFO *)((char *)(typeInfo + 1) +
210                                        typeInfo->count * sizeof(NE_NAMEINFO));
211         }
212     }
213     TRACE("No typeid entry found for %p\n", typeid );
214     HeapFree( GetProcessHeap(), 0, resTab );
215     return FALSE;
216
217  found_type:
218     nameInfo = (NE_NAMEINFO *)(typeInfo + 1);
219
220     if (HIWORD(resid) != 0)  /* named resource */
221     {
222         BYTE len = strlen( resid );
223         for (count = typeInfo->count; count > 0; count--, nameInfo++)
224         {
225             BYTE *p = resTab + nameInfo->id;
226             if (nameInfo->id & 0x8000) continue;
227             if ((*p == len) && !strncasecmp( p+1, resid, len )) goto found_name;
228         }
229     }
230     else  /* numeric resource id */
231     {
232         WORD id = LOWORD(resid) | 0x8000;
233         for (count = typeInfo->count; count > 0; count--, nameInfo++)
234             if (nameInfo->id == id) goto found_name;
235     }
236     TRACE("No resid entry found for %p\n", typeid );
237     HeapFree( GetProcessHeap(), 0, resTab );
238     return FALSE;
239
240  found_name:
241     /* Return resource data */
242     if ( resLen ) *resLen = nameInfo->length << *(WORD *)resTab;
243     if ( resOff ) *resOff = nameInfo->offset << *(WORD *)resTab;
244
245     HeapFree( GetProcessHeap(), 0, resTab );
246     return TRUE;
247 }
248
249 /***********************************************************************
250  *           load_pe_resource         [internal]
251  */
252 static BOOL find_pe_resource( HFILE lzfd, LPCSTR typeid, LPCSTR resid,
253                                 DWORD *resLen, DWORD *resOff )
254 {
255     IMAGE_NT_HEADERS pehd;
256     DWORD pehdoffset;
257     PIMAGE_DATA_DIRECTORY resDataDir;
258     PIMAGE_SECTION_HEADER sections;
259     LPBYTE resSection;
260     DWORD resSectionSize;
261     const void *resDir;
262     const IMAGE_RESOURCE_DIRECTORY *resPtr;
263     const IMAGE_RESOURCE_DATA_ENTRY *resData;
264     int i, nSections;
265     BOOL ret = FALSE;
266
267     /* Read in PE header */
268     pehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
269     if ( sizeof(pehd) != LZRead( lzfd, &pehd, sizeof(pehd) ) ) return 0;
270
271     resDataDir = pehd.OptionalHeader.DataDirectory+IMAGE_FILE_RESOURCE_DIRECTORY;
272     if ( !resDataDir->Size )
273     {
274         TRACE("No resources in PE dll\n" );
275         return FALSE;
276     }
277
278     /* Read in section table */
279     nSections = pehd.FileHeader.NumberOfSections; 
280     sections = HeapAlloc( GetProcessHeap(), 0, 
281                           nSections * sizeof(IMAGE_SECTION_HEADER) );
282     if ( !sections ) return FALSE;
283
284     LZSeek( lzfd, pehdoffset +
285                     sizeof(DWORD) + /* Signature */
286                     sizeof(IMAGE_FILE_HEADER) +
287                     pehd.FileHeader.SizeOfOptionalHeader, SEEK_SET );
288
289     if ( nSections * sizeof(IMAGE_SECTION_HEADER) !=
290          LZRead( lzfd, sections, nSections * sizeof(IMAGE_SECTION_HEADER) ) )
291     {
292         HeapFree( GetProcessHeap(), 0, sections );
293         return FALSE;
294     }
295
296     /* Find resource section */
297     for ( i = 0; i < nSections; i++ )
298         if (    resDataDir->VirtualAddress >= sections[i].VirtualAddress
299              && resDataDir->VirtualAddress <  sections[i].VirtualAddress +
300                                               sections[i].SizeOfRawData )
301             break;
302
303     if ( i == nSections )
304     {
305         HeapFree( GetProcessHeap(), 0, sections );
306         TRACE("Couldn't find resource section\n" );
307         return FALSE;
308     }
309
310     /* Read in resource section */
311     resSectionSize = sections[i].SizeOfRawData; 
312     resSection = HeapAlloc( GetProcessHeap(), 0, resSectionSize );
313     if ( !resSection ) 
314     {
315         HeapFree( GetProcessHeap(), 0, sections );
316         return FALSE;
317     }
318
319     LZSeek( lzfd, sections[i].PointerToRawData, SEEK_SET );
320     if ( resSectionSize != LZRead( lzfd, resSection, resSectionSize ) ) goto done;
321
322     /* Find resource */
323     resDir = resSection + (resDataDir->VirtualAddress - sections[i].VirtualAddress);
324
325     resPtr = (PIMAGE_RESOURCE_DIRECTORY)resDir;
326     resPtr = find_entry_by_name( resPtr, typeid, resDir );
327     if ( !resPtr )
328     {
329         TRACE("No typeid entry found for %p\n", typeid );
330         goto done;
331     }
332     resPtr = find_entry_by_name( resPtr, resid, resDir );
333     if ( !resPtr )
334     {
335         TRACE("No resid entry found for %p\n", resid );
336         goto done;
337     }
338     resPtr = find_entry_default( resPtr, resDir );
339     if ( !resPtr )
340     {
341         TRACE("No default language entry found for %p\n", resid );
342         goto done;
343     }
344
345     /* Find resource data section */
346     resData = (PIMAGE_RESOURCE_DATA_ENTRY)resPtr;
347     for ( i = 0; i < nSections; i++ )
348         if (    resData->OffsetToData >= sections[i].VirtualAddress
349              && resData->OffsetToData <  sections[i].VirtualAddress +
350                                          sections[i].SizeOfRawData )
351             break;
352
353     if ( i == nSections )
354     {
355         TRACE("Couldn't find resource data section\n" );
356         goto done;
357     }
358
359     /* Return resource data */
360     if ( resLen ) *resLen = resData->Size;
361     if ( resOff ) *resOff = resData->OffsetToData - sections[i].VirtualAddress
362                             + sections[i].PointerToRawData;
363     ret = TRUE;
364
365  done:
366     HeapFree( GetProcessHeap(), 0, resSection );
367     HeapFree( GetProcessHeap(), 0, sections );
368     return ret;
369 }
370
371
372 /*************************************************************************
373  * GetFileResourceSize16                     [VER.2]
374  */
375 DWORD WINAPI GetFileResourceSize16( LPCSTR lpszFileName, LPCSTR lpszResType,
376                                     LPCSTR lpszResId, LPDWORD lpdwFileOffset )
377 {
378     BOOL retv = FALSE;
379     HFILE lzfd;
380     OFSTRUCT ofs;
381     DWORD reslen;
382
383     TRACE("(%s,type=0x%lx,id=0x%lx,off=%p)\n",
384                 debugstr_a(lpszFileName), (LONG)lpszResType, (LONG)lpszResId, 
385                 lpszResId );
386
387     lzfd = LZOpenFileA( lpszFileName, &ofs, OF_READ );
388     if ( !lzfd ) return 0;
389
390     switch ( read_xx_header( lzfd ) )
391     {
392     case IMAGE_OS2_SIGNATURE:
393         retv = find_ne_resource( lzfd, lpszResType, lpszResId, 
394                                  &reslen, lpdwFileOffset );
395         break;
396
397     case IMAGE_NT_SIGNATURE:
398         retv = find_pe_resource( lzfd, lpszResType, lpszResId, 
399                                  &reslen, lpdwFileOffset );
400         break;
401     }
402
403     LZClose( lzfd );
404     return retv? reslen : 0;
405 }
406
407
408 /*************************************************************************
409  * GetFileResource16                         [VER.3]
410  */
411 DWORD WINAPI GetFileResource16( LPCSTR lpszFileName, LPCSTR lpszResType,
412                                 LPCSTR lpszResId, DWORD dwFileOffset,
413                                 DWORD dwResLen, LPVOID lpvData )
414 {
415     BOOL retv = FALSE;
416     HFILE lzfd;
417     OFSTRUCT ofs;
418     DWORD reslen = dwResLen;
419
420     TRACE("(%s,type=0x%lx,id=0x%lx,off=%ld,len=%ld,data=%p)\n",
421                 debugstr_a(lpszFileName), (LONG)lpszResType, (LONG)lpszResId, 
422                 dwFileOffset, dwResLen, lpvData );
423
424     lzfd = LZOpenFileA( lpszFileName, &ofs, OF_READ );
425     if ( lzfd == 0 ) return 0;
426
427     if ( !dwFileOffset )
428     {
429         switch ( read_xx_header( lzfd ) ) 
430         {
431         case IMAGE_OS2_SIGNATURE:
432             retv = find_ne_resource( lzfd, lpszResType, lpszResId, 
433                                      &reslen, &dwFileOffset );
434             break;
435
436         case IMAGE_NT_SIGNATURE:
437             retv = find_pe_resource( lzfd, lpszResType, lpszResId, 
438                                      &reslen, &dwFileOffset );
439             break;
440         }
441
442         if ( !retv ) 
443         {
444             LZClose( lzfd );
445             return 0;
446         }
447     }
448
449     LZSeek( lzfd, dwFileOffset, SEEK_SET );
450     reslen = LZRead( lzfd, lpvData, min( reslen, dwResLen ) );
451     LZClose( lzfd );
452
453     return reslen;
454 }
455