No need to call GetModuleFileName16 now that GetModuleFileNameA
[wine] / loader / pe_resource.c
1 /*
2  * PE (Portable Execute) File Resources
3  *
4  * Copyright 1995 Thomas Sandford
5  * Copyright 1996 Martin von Loewis
6  *
7  * Based on the Win16 resource handling code in loader/resource.c
8  * Copyright 1993 Robert J. Amstadt
9  * Copyright 1995 Alexandre Julliard
10  * Copyright 1997 Marcus Meissner
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public
14  * License as published by the Free Software Foundation; either
15  * version 2.1 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with this library; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25  */
26
27 #include "config.h"
28
29 #include <stdlib.h>
30 #include <sys/types.h>
31
32 #include "wine/unicode.h"
33 #include "windef.h"
34 #include "winnls.h"
35 #include "winerror.h"
36 #include "module.h"
37 #include "stackframe.h"
38 #include "wine/debug.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(resource);
41
42
43 /**********************************************************************
44  *  get_module_base
45  *
46  * Get the base address of a module
47  */
48 static const void *get_module_base( HMODULE hmod )
49 {
50     if (!hmod) hmod = GetModuleHandleA( NULL );
51     else if (!HIWORD(hmod))
52     {
53         FIXME("Enumeration of 16-bit resources is not supported\n");
54         SetLastError(ERROR_INVALID_HANDLE);
55         return NULL;
56     }
57
58     /* clear low order bit in case of LOAD_LIBRARY_AS_DATAFILE module */
59     return (void *)((ULONG_PTR)hmod & ~1);
60 }
61
62
63 /**********************************************************************
64  *  is_data_file_module
65  *
66  * Check if a module handle is for a LOAD_LIBRARY_AS_DATAFILE module.
67  */
68 inline static int is_data_file_module( HMODULE hmod )
69 {
70     return (ULONG_PTR)hmod & 1;
71 }
72
73
74 /**********************************************************************
75  *  get_data_file_ptr
76  *
77  * Get a pointer to a given offset in a file mapped as data file.
78  */
79 static const void *get_data_file_ptr( const void *base, DWORD offset )
80 {
81     const IMAGE_NT_HEADERS *nt = PE_HEADER(base);
82     const IMAGE_SECTION_HEADER *sec = (IMAGE_SECTION_HEADER *)((char *)&nt->OptionalHeader +
83                                                                nt->FileHeader.SizeOfOptionalHeader);
84     int i;
85
86     /* find the section containing the virtual address */
87     for (i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
88     {
89         if ((sec->VirtualAddress <= offset) && (sec->VirtualAddress + sec->SizeOfRawData > offset))
90             return (char *)base + sec->PointerToRawData + (offset - sec->VirtualAddress);
91     }
92     return NULL;
93 }
94
95
96 /**********************************************************************
97  *  get_resdir
98  *
99  * Get the resource directory of a PE module
100  */
101 static const IMAGE_RESOURCE_DIRECTORY* get_resdir( HMODULE hmod )
102 {
103     const IMAGE_DATA_DIRECTORY *dir;
104     const IMAGE_RESOURCE_DIRECTORY *ret = NULL;
105     const void *base = get_module_base( hmod );
106
107     if (base)
108     {
109         dir = &PE_HEADER(base)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
110         if (dir->Size && dir->VirtualAddress)
111         {
112             if (is_data_file_module(hmod)) ret = get_data_file_ptr( base, dir->VirtualAddress );
113             else ret = (IMAGE_RESOURCE_DIRECTORY *)((char *)base + dir->VirtualAddress);
114         }
115     }
116     return ret;
117 }
118
119
120 /**********************************************************************
121  *  find_entry_by_id
122  *
123  * Find an entry by id in a resource directory
124  */
125 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
126                                                          WORD id, const void *root )
127 {
128     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
129     int min, max, pos;
130
131     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
132     min = dir->NumberOfNamedEntries;
133     max = min + dir->NumberOfIdEntries - 1;
134     while (min <= max)
135     {
136         pos = (min + max) / 2;
137         if (entry[pos].u1.s2.Id == id)
138             return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s3.OffsetToDirectory);
139         if (entry[pos].u1.s2.Id > id) max = pos - 1;
140         else min = pos + 1;
141     }
142     return NULL;
143 }
144
145
146 /**********************************************************************
147  *  find_entry_by_nameW
148  *
149  * Find an entry by name in a resource directory
150  */
151 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_nameW( const IMAGE_RESOURCE_DIRECTORY *dir,
152                                                             LPCWSTR name, const void *root )
153 {
154     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
155     const IMAGE_RESOURCE_DIR_STRING_U *str;
156     int min, max, res, pos, namelen;
157
158     if (!HIWORD(name)) return find_entry_by_id( dir, LOWORD(name), root );
159     if (name[0] == '#')
160     {
161         char buf[16];
162         if (!WideCharToMultiByte( CP_ACP, 0, name+1, -1, buf, sizeof(buf), NULL, NULL ))
163             return NULL;
164         return find_entry_by_id( dir, atoi(buf), root );
165     }
166
167     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
168     namelen = strlenW(name);
169     min = 0;
170     max = dir->NumberOfNamedEntries - 1;
171     while (min <= max)
172     {
173         pos = (min + max) / 2;
174         str = (IMAGE_RESOURCE_DIR_STRING_U *)((char *)root + entry[pos].u1.s1.NameOffset);
175         res = strncmpiW( name, str->NameString, str->Length );
176         if (!res && namelen == str->Length)
177             return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s3.OffsetToDirectory);
178         if (res < 0) max = pos - 1;
179         else min = pos + 1;
180     }
181     return NULL;
182 }
183
184
185 /**********************************************************************
186  *  find_entry_by_nameA
187  *
188  * Find an entry by name in a resource directory
189  */
190 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_nameA( const IMAGE_RESOURCE_DIRECTORY *dir,
191                                                             LPCSTR name, const void *root )
192 {
193     const IMAGE_RESOURCE_DIRECTORY *ret = NULL;
194     LPWSTR nameW;
195     INT len;
196
197     if (!HIWORD(name)) return find_entry_by_id( dir, LOWORD(name), root );
198     if (name[0] == '#')
199     {
200         return find_entry_by_id( dir, atoi(name+1), root );
201     }
202
203     len = MultiByteToWideChar( CP_ACP, 0, name, -1, NULL, 0 );
204     if ((nameW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
205     {
206         MultiByteToWideChar( CP_ACP, 0, name, -1, nameW, len );
207         ret = find_entry_by_nameW( dir, nameW, root );
208         HeapFree( GetProcessHeap(), 0, nameW );
209     }
210     return ret;
211 }
212
213
214 /**********************************************************************
215  *  find_entry_default
216  *
217  * Find a default entry in a resource directory
218  */
219 static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir,
220                                                            const void *root )
221 {
222     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
223
224     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
225     return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry->u2.s3.OffsetToDirectory);
226 }
227
228
229 /**********************************************************************
230  *          PE_FindResourceExW
231  *
232  * FindResourceExA/W does search in the following order:
233  * 1. Exact specified language
234  * 2. Language with neutral sublanguage
235  * 3. Neutral language with neutral sublanguage
236  * 4. Neutral language with default sublanguage
237  */
238 HRSRC PE_FindResourceExW( HMODULE hmod, LPCWSTR name, LPCWSTR type, WORD lang )
239 {
240     const IMAGE_RESOURCE_DIRECTORY *resdirptr = get_resdir(hmod);
241     const void *root;
242     HRSRC result;
243
244     if (!resdirptr) return 0;
245
246     root = resdirptr;
247     if (!(resdirptr = find_entry_by_nameW(resdirptr, type, root))) return 0;
248     if (!(resdirptr = find_entry_by_nameW(resdirptr, name, root))) return 0;
249
250     /* 1. Exact specified language */
251     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
252
253     /* 2. Language with neutral sublanguage */
254     lang = MAKELANGID(PRIMARYLANGID(lang), SUBLANG_NEUTRAL);
255     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
256
257     /* 3. Neutral language with neutral sublanguage */
258     lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
259     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
260
261     /* 4. Neutral language with default sublanguage */
262     lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
263     result = (HRSRC)find_entry_by_id( resdirptr, lang, root );
264
265  found:
266     return result;
267 }
268
269 /**********************************************************************
270  *          PE_FindResourceW
271  *
272  * Load[String]/[Icon]/[Menu]/[etc.] does use FindResourceA/W.
273  * FindResourceA/W does search in the following order:
274  * 1. Neutral language with neutral sublanguage
275  * 2. Neutral language with default sublanguage
276  * 3. Current locale lang id
277  * 4. Current locale lang id with neutral sublanguage
278  * 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT
279  * 6. Return first in the list
280  */
281 HRSRC PE_FindResourceW( HMODULE hmod, LPCWSTR name, LPCWSTR type )
282 {
283     const IMAGE_RESOURCE_DIRECTORY *resdirptr = get_resdir(hmod);
284     const void *root;
285     HRSRC result;
286     WORD lang;
287
288     if (!resdirptr) return 0;
289
290     root = resdirptr;
291     if (!(resdirptr = find_entry_by_nameW(resdirptr, type, root))) return 0;
292     if (!(resdirptr = find_entry_by_nameW(resdirptr, name, root))) return 0;
293
294     /* 1. Neutral language with neutral sublanguage */
295     lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
296     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
297
298     /* 2. Neutral language with default sublanguage */
299     lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
300     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
301
302     /* 3. Current locale lang id */
303     lang = LANGIDFROMLCID(GetUserDefaultLCID());
304     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
305
306     /* 4. Current locale lang id with neutral sublanguage */
307     lang = MAKELANGID(PRIMARYLANGID(lang), SUBLANG_NEUTRAL);
308     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
309
310     /* 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT */
311     lang = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
312     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
313
314     /* 6. Return first in the list */
315     result = (HRSRC)find_entry_default( resdirptr, root );
316
317  found:
318     return result;
319 }
320
321
322 /**********************************************************************
323  *          PE_LoadResource
324  */
325 HGLOBAL PE_LoadResource( HMODULE hmod, HRSRC hRsrc )
326 {
327     DWORD offset;
328     const void *base = get_module_base( hmod );
329
330     if (!hRsrc || !base) return 0;
331
332     offset = ((PIMAGE_RESOURCE_DATA_ENTRY)hRsrc)->OffsetToData;
333
334     if (is_data_file_module(hmod))
335         return (HANDLE)get_data_file_ptr( base, offset );
336     else
337         return (HANDLE)((char *)base + offset);
338 }
339
340
341 /**********************************************************************
342  *          PE_SizeofResource
343  */
344 DWORD PE_SizeofResource( HRSRC hRsrc )
345 {
346     if (!hRsrc) return 0;
347     return ((PIMAGE_RESOURCE_DATA_ENTRY)hRsrc)->Size;
348 }
349
350
351 /**********************************************************************
352  *      EnumResourceTypesA      (KERNEL32.@)
353  */
354 BOOL WINAPI EnumResourceTypesA( HMODULE hmod, ENUMRESTYPEPROCA lpfun, LONG lparam)
355 {
356     int         i;
357     const IMAGE_RESOURCE_DIRECTORY *resdir = get_resdir(hmod);
358     const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
359     BOOL        ret;
360
361     if (!resdir) return FALSE;
362
363     et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
364     ret = FALSE;
365     for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
366         LPSTR type;
367
368         if (et[i].u1.s1.NameIsString)
369         {
370             PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) resdir + et[i].u1.s1.NameOffset);
371             DWORD len = WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
372                                              NULL, 0, NULL, NULL);
373             if (!(type = HeapAlloc(GetProcessHeap(), 0, len + 1)))
374                 return FALSE;
375             WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
376                                  type, len, NULL, NULL);
377             type[len] = '\0';
378             ret = lpfun(hmod,type,lparam);
379             HeapFree(GetProcessHeap(), 0, type);
380         }
381         else
382         {
383             type = (LPSTR)(int)et[i].u1.s2.Id;
384             ret = lpfun(hmod,type,lparam);
385         }
386         if (!ret)
387                 break;
388     }
389     return ret;
390 }
391
392
393 /**********************************************************************
394  *      EnumResourceTypesW      (KERNEL32.@)
395  */
396 BOOL WINAPI EnumResourceTypesW( HMODULE hmod, ENUMRESTYPEPROCW lpfun, LONG lparam)
397 {
398     int         i;
399     const IMAGE_RESOURCE_DIRECTORY *resdir = get_resdir(hmod);
400     const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
401     BOOL        ret;
402
403     if (!resdir) return FALSE;
404
405     et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
406     ret = FALSE;
407     for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
408         LPWSTR  type;
409
410         if (et[i].u1.s1.NameIsString)
411         {
412             PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) resdir + et[i].u1.s1.NameOffset);
413             if (!(type = HeapAlloc(GetProcessHeap(), 0, (pResString->Length+1) * sizeof (WCHAR))))
414                 return FALSE;
415             memcpy(type, pResString->NameString, pResString->Length * sizeof (WCHAR));
416             type[pResString->Length] = '\0';
417             ret = lpfun(hmod,type,lparam);
418             HeapFree(GetProcessHeap(), 0, type);
419         }
420         else
421         {
422             type = (LPWSTR)(int)et[i].u1.s2.Id;
423             ret = lpfun(hmod,type,lparam);
424         }
425         if (!ret)
426                 break;
427     }
428     return ret;
429 }
430
431
432 /**********************************************************************
433  *      EnumResourceNamesA      (KERNEL32.@)
434  */
435 BOOL WINAPI EnumResourceNamesA( HMODULE hmod, LPCSTR type, ENUMRESNAMEPROCA lpfun, LONG lparam )
436 {
437     int         i;
438     const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
439     const IMAGE_RESOURCE_DIRECTORY *resdir;
440     const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
441     BOOL        ret;
442
443     if (!basedir) return FALSE;
444
445     if (!(resdir = find_entry_by_nameA( basedir, type, basedir ))) return FALSE;
446
447     et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
448     ret = FALSE;
449     for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
450         LPSTR name;
451
452         if (et[i].u1.s1.NameIsString)
453         {
454             PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) basedir + et[i].u1.s1.NameOffset);
455             DWORD len = WideCharToMultiByte(CP_ACP, 0, pResString->NameString, pResString->Length,
456                                             NULL, 0, NULL, NULL);
457             if (!(name = HeapAlloc(GetProcessHeap(), 0, len + 1 )))
458                 return FALSE;
459             WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
460                                  name, len, NULL, NULL );
461             name[len] = '\0';
462             ret = lpfun(hmod,type,name,lparam);
463             HeapFree( GetProcessHeap(), 0, name );
464         }
465         else
466         {
467             name = (LPSTR)(int)et[i].u1.s2.Id;
468             ret = lpfun(hmod,type,name,lparam);
469         }
470         if (!ret)
471                 break;
472     }
473     return ret;
474 }
475
476
477 /**********************************************************************
478  *      EnumResourceNamesW      (KERNEL32.@)
479  */
480 BOOL WINAPI EnumResourceNamesW( HMODULE hmod, LPCWSTR type, ENUMRESNAMEPROCW lpfun, LONG lparam )
481 {
482     int         i;
483     const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
484     const IMAGE_RESOURCE_DIRECTORY *resdir;
485     const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
486     BOOL        ret;
487
488     if (!basedir) return FALSE;
489
490     if (!(resdir = find_entry_by_nameW( basedir, type, basedir ))) return FALSE;
491
492     et = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
493     ret = FALSE;
494     for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
495         LPWSTR name;
496
497         if (et[i].u1.s1.NameIsString)
498         {
499             PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) basedir + et[i].u1.s1.NameOffset);
500             if (!(name = HeapAlloc(GetProcessHeap(), 0, (pResString->Length + 1) * sizeof (WCHAR))))
501                 return FALSE;
502             memcpy(name, pResString->NameString, pResString->Length * sizeof (WCHAR));
503             name[pResString->Length] = '\0';
504             ret = lpfun(hmod,type,name,lparam);
505             HeapFree(GetProcessHeap(), 0, name);
506         }
507         else
508         {
509             name = (LPWSTR)(int)et[i].u1.s2.Id;
510             ret = lpfun(hmod,type,name,lparam);
511         }
512         if (!ret)
513                 break;
514     }
515     return ret;
516 }
517
518
519 /**********************************************************************
520  *      EnumResourceLanguagesA  (KERNEL32.@)
521  */
522 BOOL WINAPI EnumResourceLanguagesA( HMODULE hmod, LPCSTR type, LPCSTR name,
523                                     ENUMRESLANGPROCA lpfun, LONG lparam )
524 {
525     int         i;
526     const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
527     const IMAGE_RESOURCE_DIRECTORY *resdir;
528     const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
529     BOOL        ret;
530
531     if (!basedir) return FALSE;
532     if (!(resdir = find_entry_by_nameA( basedir, type, basedir ))) return FALSE;
533     if (!(resdir = find_entry_by_nameA( resdir, name, basedir ))) return FALSE;
534
535     et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
536     ret = FALSE;
537     for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
538         /* languages are just ids... I hope */
539         ret = lpfun(hmod,type,name,et[i].u1.s2.Id,lparam);
540         if (!ret)
541                 break;
542     }
543     return ret;
544 }
545
546
547 /**********************************************************************
548  *      EnumResourceLanguagesW  (KERNEL32.@)
549  */
550 BOOL WINAPI EnumResourceLanguagesW( HMODULE hmod, LPCWSTR type, LPCWSTR name,
551                                     ENUMRESLANGPROCW lpfun, LONG lparam )
552 {
553     int         i;
554     const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
555     const IMAGE_RESOURCE_DIRECTORY *resdir;
556     const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
557     BOOL        ret;
558
559     if (!basedir) return FALSE;
560
561     if (!(resdir = find_entry_by_nameW( basedir, type, basedir ))) return FALSE;
562     if (!(resdir = find_entry_by_nameW( resdir, name, basedir ))) return FALSE;
563
564     et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
565     ret = FALSE;
566     for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
567         ret = lpfun(hmod,type,name,et[i].u1.s2.Id,lparam);
568         if (!ret)
569                 break;
570     }
571     return ret;
572 }