- Fix memory leaks.
[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
13 #include <stdlib.h>
14 #include <sys/types.h>
15
16 #include "config.h"
17
18 #include "wine/unicode.h"
19 #include "windef.h"
20 #include "winnls.h"
21 #include "winerror.h"
22 #include "module.h"
23 #include "heap.h"
24 #include "stackframe.h"
25 #include "debugtools.h"
26
27 DEFAULT_DEBUG_CHANNEL(resource);
28
29
30 /**********************************************************************
31  *  get_module_base
32  *
33  * Get the base address of a module
34  */
35 static const void *get_module_base( HMODULE hmod )
36 {
37     if (!hmod) hmod = GetModuleHandleA( NULL );
38     else if (!HIWORD(hmod))
39     {
40         FIXME("Enumeration of 16-bit resources is not supported\n");
41         SetLastError(ERROR_INVALID_HANDLE);
42         return NULL;
43     }
44
45     /* clear low order bit in case of LOAD_LIBRARY_AS_DATAFILE module */
46     return (void *)((ULONG_PTR)hmod & ~1);
47 }
48
49
50 /**********************************************************************
51  *  get_resdir
52  *
53  * Get the resource directory of a PE module
54  */
55 static const IMAGE_RESOURCE_DIRECTORY* get_resdir( HMODULE hmod )
56 {
57     const IMAGE_DATA_DIRECTORY *dir;
58     const IMAGE_RESOURCE_DIRECTORY *ret = NULL;
59     const void *base = get_module_base( hmod );
60
61     if (base)
62     {
63         dir = &PE_HEADER(base)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
64         if (dir->Size && dir->VirtualAddress)
65             ret = (IMAGE_RESOURCE_DIRECTORY *)((char *)base + dir->VirtualAddress);
66     }
67     return ret;
68 }
69
70
71 /**********************************************************************
72  *  find_entry_by_id
73  *
74  * Find an entry by id in a resource directory
75  */
76 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
77                                                          WORD id, const void *root )
78 {
79     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
80     int min, max, pos;
81
82     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
83     min = dir->NumberOfNamedEntries;
84     max = min + dir->NumberOfIdEntries - 1;
85     while (min <= max)
86     {
87         pos = (min + max) / 2;
88         if (entry[pos].u1.s2.Id == id)
89             return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s3.OffsetToDirectory);
90         if (entry[pos].u1.s2.Id > id) max = pos - 1;
91         else min = pos + 1;
92     }
93     return NULL;
94 }
95
96
97 /**********************************************************************
98  *  find_entry_by_nameW
99  *
100  * Find an entry by name in a resource directory
101  */
102 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_nameW( const IMAGE_RESOURCE_DIRECTORY *dir,
103                                                             LPCWSTR name, const void *root )
104 {
105     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
106     const IMAGE_RESOURCE_DIR_STRING_U *str;
107     int min, max, res, pos, namelen;
108
109     if (!HIWORD(name)) return find_entry_by_id( dir, LOWORD(name), root );
110     if (name[0] == '#')
111     {
112         char buf[16];
113         if (!WideCharToMultiByte( CP_ACP, 0, name+1, -1, buf, sizeof(buf), NULL, NULL ))
114             return NULL;
115         return find_entry_by_id( dir, atoi(buf), root );
116     }
117
118     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
119     namelen = strlenW(name);
120     min = 0;
121     max = dir->NumberOfNamedEntries - 1;
122     while (min <= max)
123     {
124         pos = (min + max) / 2;
125         str = (IMAGE_RESOURCE_DIR_STRING_U *)((char *)root + entry[pos].u1.s1.NameOffset);
126         res = strncmpiW( name, str->NameString, str->Length );
127         if (!res && namelen == str->Length)
128             return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s3.OffsetToDirectory);
129         if (res < 0) max = pos - 1;
130         else min = pos + 1;
131     }
132     return NULL;
133 }
134
135
136 /**********************************************************************
137  *  find_entry_by_nameA
138  *
139  * Find an entry by name in a resource directory
140  */
141 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_nameA( const IMAGE_RESOURCE_DIRECTORY *dir,
142                                                             LPCSTR name, const void *root )
143 {
144     const IMAGE_RESOURCE_DIRECTORY *ret = NULL;
145     LPWSTR nameW;
146
147     if (!HIWORD(name)) return find_entry_by_id( dir, LOWORD(name), root );
148     if (name[0] == '#')
149     {
150         return find_entry_by_id( dir, atoi(name+1), root );
151     }
152
153     if ((nameW = HEAP_strdupAtoW( GetProcessHeap(), 0, name )))
154     {
155         ret = find_entry_by_nameW( dir, nameW, root );
156         HeapFree( GetProcessHeap(), 0, nameW );
157     }
158     return ret;
159 }
160
161
162 /**********************************************************************
163  *  find_entry_default
164  *
165  * Find a default entry in a resource directory
166  */
167 static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir,
168                                                            const void *root )
169 {
170     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
171
172     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
173     return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry->u2.s3.OffsetToDirectory);
174 }
175
176
177 /**********************************************************************
178  *          PE_FindResourceExW
179  *
180  * FindResourceExA/W does search in the following order:
181  * 1. Exact specified language
182  * 2. Language with neutral sublanguage
183  * 3. Neutral language with neutral sublanguage
184  * 4. Neutral language with default sublanguage
185  */
186 HRSRC PE_FindResourceExW( HMODULE hmod, LPCWSTR name, LPCWSTR type, WORD lang )
187 {
188     const IMAGE_RESOURCE_DIRECTORY *resdirptr = get_resdir(hmod);
189     const void *root;
190     HRSRC result;
191
192     if (!resdirptr) return 0;
193
194     root = resdirptr;
195     if (!(resdirptr = find_entry_by_nameW(resdirptr, type, root))) return 0;
196     if (!(resdirptr = find_entry_by_nameW(resdirptr, name, root))) return 0;
197
198     /* 1. Exact specified language */
199     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
200
201     /* 2. Language with neutral sublanguage */
202     lang = MAKELANGID(PRIMARYLANGID(lang), SUBLANG_NEUTRAL);
203     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
204
205     /* 3. Neutral language with neutral sublanguage */
206     lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
207     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
208
209     /* 4. Neutral language with default sublanguage */
210     lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
211     result = (HRSRC)find_entry_by_id( resdirptr, lang, root );
212
213  found:
214     return result;
215 }
216
217 /**********************************************************************
218  *          PE_FindResourceW
219  *
220  * Load[String]/[Icon]/[Menu]/[etc.] does use FindResourceA/W.
221  * FindResourceA/W does search in the following order:
222  * 1. Neutral language with neutral sublanguage
223  * 2. Neutral language with default sublanguage
224  * 3. Current locale lang id
225  * 4. Current locale lang id with neutral sublanguage
226  * 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT
227  * 6. Return first in the list
228  */
229 HRSRC PE_FindResourceW( HMODULE hmod, LPCWSTR name, LPCWSTR type )
230 {
231     const IMAGE_RESOURCE_DIRECTORY *resdirptr = get_resdir(hmod);
232     const void *root;
233     HRSRC result;
234     WORD lang;
235
236     if (!resdirptr) return 0;
237
238     root = resdirptr;
239     if (!(resdirptr = find_entry_by_nameW(resdirptr, type, root))) return 0;
240     if (!(resdirptr = find_entry_by_nameW(resdirptr, name, root))) return 0;
241
242     /* 1. Neutral language with neutral sublanguage */
243     lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
244     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
245
246     /* 2. Neutral language with default sublanguage */
247     lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
248     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
249
250     /* 3. Current locale lang id */
251     lang = LANGIDFROMLCID(GetUserDefaultLCID());
252     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
253
254     /* 4. Current locale lang id with neutral sublanguage */
255     lang = MAKELANGID(PRIMARYLANGID(lang), SUBLANG_NEUTRAL);
256     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
257
258     /* 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT */
259     lang = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
260     if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
261
262     /* 6. Return first in the list */
263     result = (HRSRC)find_entry_default( resdirptr, root );
264
265  found:
266     return result;
267 }
268
269
270 /**********************************************************************
271  *          PE_LoadResource
272  */
273 HGLOBAL PE_LoadResource( HMODULE hmod, HRSRC hRsrc )
274 {
275     const void *base = get_module_base( hmod );
276     if (!hRsrc) return 0;
277     return (HANDLE)((char *)base + ((PIMAGE_RESOURCE_DATA_ENTRY)hRsrc)->OffsetToData);
278 }
279
280
281 /**********************************************************************
282  *          PE_SizeofResource
283  */
284 DWORD PE_SizeofResource( HRSRC hRsrc )
285 {
286     if (!hRsrc) return 0;
287     return ((PIMAGE_RESOURCE_DATA_ENTRY)hRsrc)->Size;
288 }
289
290
291 /**********************************************************************
292  *      EnumResourceTypesA      (KERNEL32.@)
293  */
294 BOOL WINAPI EnumResourceTypesA( HMODULE hmod, ENUMRESTYPEPROCA lpfun, LONG lparam)
295 {
296     int         i;
297     const IMAGE_RESOURCE_DIRECTORY *resdir = get_resdir(hmod);
298     const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
299     BOOL        ret;
300
301     if (!resdir) return FALSE;
302
303     et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
304     ret = FALSE;
305     for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
306         LPSTR type;
307
308         if (et[i].u1.s1.NameIsString)
309         {
310             PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) resdir + et[i].u1.s1.NameOffset);
311             DWORD len = WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
312                                              NULL, 0, NULL, NULL);
313             if (!(type = HeapAlloc(GetProcessHeap(), 0, len + 1)))
314                 return FALSE;
315             WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
316                                  type, len, NULL, NULL);
317             type[len] = '\0';
318             ret = lpfun(hmod,type,lparam);
319             HeapFree(GetProcessHeap(), 0, type);
320         }
321         else
322         {
323             type = (LPSTR)(int)et[i].u1.s2.Id;
324             ret = lpfun(hmod,type,lparam);
325         }
326         if (!ret)
327                 break;
328     }
329     return ret;
330 }
331
332
333 /**********************************************************************
334  *      EnumResourceTypesW      (KERNEL32.@)
335  */
336 BOOL WINAPI EnumResourceTypesW( HMODULE hmod, ENUMRESTYPEPROCW lpfun, LONG lparam)
337 {
338     int         i;
339     const IMAGE_RESOURCE_DIRECTORY *resdir = get_resdir(hmod);
340     const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
341     BOOL        ret;
342
343     if (!resdir) return FALSE;
344
345     et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
346     ret = FALSE;
347     for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
348         LPWSTR  type;
349
350         if (et[i].u1.s1.NameIsString)
351         {
352             PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) resdir + et[i].u1.s1.NameOffset);
353             if (!(type = HeapAlloc(GetProcessHeap(), 0, (pResString->Length+1) * sizeof (WCHAR))))
354                 return FALSE;
355             memcpy(type, pResString->NameString, pResString->Length * sizeof (WCHAR));
356             type[pResString->Length] = '\0';
357             ret = lpfun(hmod,type,lparam);
358             HeapFree(GetProcessHeap(), 0, type);
359         }
360         else
361         {
362             type = (LPWSTR)(int)et[i].u1.s2.Id;
363             ret = lpfun(hmod,type,lparam);
364         }
365         if (!ret)
366                 break;
367     }
368     return ret;
369 }
370
371
372 /**********************************************************************
373  *      EnumResourceNamesA      (KERNEL32.@)
374  */
375 BOOL WINAPI EnumResourceNamesA( HMODULE hmod, LPCSTR type, ENUMRESNAMEPROCA lpfun, LONG lparam )
376 {
377     int         i;
378     const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
379     const IMAGE_RESOURCE_DIRECTORY *resdir;
380     const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
381     BOOL        ret;
382
383     if (!basedir) return FALSE;
384
385     if (!(resdir = find_entry_by_nameA( basedir, type, basedir ))) return FALSE;
386
387     et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
388     ret = FALSE;
389     for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
390         LPSTR name;
391
392         if (et[i].u1.s1.NameIsString)
393         {
394             PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) basedir + et[i].u1.s1.NameOffset);
395             DWORD len = WideCharToMultiByte(CP_ACP, 0, pResString->NameString, pResString->Length,
396                                             NULL, 0, NULL, NULL);
397             if (!(name = HeapAlloc(GetProcessHeap(), 0, len + 1 )))
398                 return FALSE;
399             WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
400                                  name, len, NULL, NULL );
401             name[len] = '\0';
402             ret = lpfun(hmod,type,name,lparam);
403             HeapFree( GetProcessHeap(), 0, name );
404         }
405         else
406         {
407             name = (LPSTR)(int)et[i].u1.s2.Id;
408             ret = lpfun(hmod,type,name,lparam);
409         }
410         if (!ret)
411                 break;
412     }
413     return ret;
414 }
415
416
417 /**********************************************************************
418  *      EnumResourceNamesW      (KERNEL32.@)
419  */
420 BOOL WINAPI EnumResourceNamesW( HMODULE hmod, LPCWSTR type, ENUMRESNAMEPROCW lpfun, LONG lparam )
421 {
422     int         i;
423     const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
424     const IMAGE_RESOURCE_DIRECTORY *resdir;
425     const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
426     BOOL        ret;
427
428     if (!basedir) return FALSE;
429
430     if (!(resdir = find_entry_by_nameW( basedir, type, basedir ))) return FALSE;
431
432     et = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
433     ret = FALSE;
434     for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
435         LPWSTR name;
436
437         if (et[i].u1.s1.NameIsString)
438         {
439             PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) basedir + et[i].u1.s1.NameOffset);
440             if (!(name = HeapAlloc(GetProcessHeap(), 0, (pResString->Length + 1) * sizeof (WCHAR))))
441                 return FALSE;
442             memcpy(name, pResString->NameString, pResString->Length * sizeof (WCHAR));
443             name[pResString->Length] = '\0';
444             ret = lpfun(hmod,type,name,lparam);
445             HeapFree(GetProcessHeap(), 0, name);
446         }
447         else
448         {
449             name = (LPWSTR)(int)et[i].u1.s2.Id;
450             ret = lpfun(hmod,type,name,lparam);
451         }
452         if (!ret)
453                 break;
454     }
455     return ret;
456 }
457
458
459 /**********************************************************************
460  *      EnumResourceLanguagesA  (KERNEL32.@)
461  */
462 BOOL WINAPI EnumResourceLanguagesA( HMODULE hmod, LPCSTR type, LPCSTR name,
463                                     ENUMRESLANGPROCA lpfun, LONG lparam )
464 {
465     int         i;
466     const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
467     const IMAGE_RESOURCE_DIRECTORY *resdir;
468     const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
469     BOOL        ret;
470
471     if (!basedir) return FALSE;
472     if (!(resdir = find_entry_by_nameA( basedir, type, basedir ))) return FALSE;
473     if (!(resdir = find_entry_by_nameA( resdir, name, basedir ))) return FALSE;
474
475     et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
476     ret = FALSE;
477     for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
478         /* languages are just ids... I hope */
479         ret = lpfun(hmod,type,name,et[i].u1.s2.Id,lparam);
480         if (!ret)
481                 break;
482     }
483     return ret;
484 }
485
486
487 /**********************************************************************
488  *      EnumResourceLanguagesW  (KERNEL32.@)
489  */
490 BOOL WINAPI EnumResourceLanguagesW( HMODULE hmod, LPCWSTR type, LPCWSTR name,
491                                     ENUMRESLANGPROCW lpfun, LONG lparam )
492 {
493     int         i;
494     const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
495     const IMAGE_RESOURCE_DIRECTORY *resdir;
496     const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
497     BOOL        ret;
498
499     if (!basedir) return FALSE;
500
501     if (!(resdir = find_entry_by_nameW( basedir, type, basedir ))) return FALSE;
502     if (!(resdir = find_entry_by_nameW( resdir, name, basedir ))) return FALSE;
503
504     et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
505     ret = FALSE;
506     for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
507         ret = lpfun(hmod,type,name,et[i].u1.s2.Id,lparam);
508         if (!ret)
509                 break;
510     }
511     return ret;
512 }