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