oleaut32: Support VT_SAFEARRAY in the typelib marshaler.
[wine] / dlls / ntdll / resource.c
1 /*
2  * PE file resources
3  *
4  * Copyright 1995 Thomas Sandford
5  * Copyright 1996 Martin von Loewis
6  * Copyright 2003 Alexandre Julliard
7  *
8  * Based on the Win16 resource handling code in loader/resource.c
9  * Copyright 1993 Robert J. Amstadt
10  * Copyright 1995 Alexandre Julliard
11  * Copyright 1997 Marcus Meissner
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public
15  * License as published by the Free Software Foundation; either
16  * version 2.1 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public
24  * License along with this library; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27
28 #include "config.h"
29 #include "wine/port.h"
30
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include <sys/types.h>
34
35 #define NONAMELESSUNION
36 #define NONAMELESSSTRUCT
37 #include "ntstatus.h"
38 #define WIN32_NO_STATUS
39 #include "windef.h"
40 #include "winbase.h"
41 #include "winnls.h"
42 #include "winnt.h"
43 #include "winternl.h"
44 #include "excpt.h"
45 #include "wine/exception.h"
46 #include "wine/unicode.h"
47 #include "wine/debug.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(resource);
50
51 static LCID user_lcid, system_lcid;
52 static LANGID user_ui_language, system_ui_language;
53
54 /**********************************************************************
55  *  is_data_file_module
56  *
57  * Check if a module handle is for a LOAD_LIBRARY_AS_DATAFILE module.
58  */
59 inline static int is_data_file_module( HMODULE hmod )
60 {
61     return (ULONG_PTR)hmod & 1;
62 }
63
64
65 /**********************************************************************
66  *  push_language
67  *
68  * push a language in the list of languages to try
69  */
70 static inline int push_language( WORD *list, int pos, WORD lang )
71 {
72     int i;
73     for (i = 0; i < pos; i++) if (list[i] == lang) return pos;
74     list[pos++] = lang;
75     return pos;
76 }
77
78
79 /**********************************************************************
80  *  find_first_entry
81  *
82  * Find the first suitable entry in a resource directory
83  */
84 static const IMAGE_RESOURCE_DIRECTORY *find_first_entry( const IMAGE_RESOURCE_DIRECTORY *dir,
85                                                          const void *root, int want_dir )
86 {
87     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
88     int pos;
89
90     for (pos = 0; pos < dir->NumberOfNamedEntries + dir->NumberOfIdEntries; pos++)
91     {
92         if (!entry[pos].u2.s3.DataIsDirectory == !want_dir)
93             return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry[pos].u2.s3.OffsetToDirectory);
94     }
95     return NULL;
96 }
97
98
99 /**********************************************************************
100  *  find_entry_by_id
101  *
102  * Find an entry by id in a resource directory
103  */
104 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
105                                                          WORD id, const void *root, int want_dir )
106 {
107     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
108     int min, max, pos;
109
110     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
111     min = dir->NumberOfNamedEntries;
112     max = min + dir->NumberOfIdEntries - 1;
113     while (min <= max)
114     {
115         pos = (min + max) / 2;
116         if (entry[pos].u1.s2.Id == id)
117         {
118             if (!entry[pos].u2.s3.DataIsDirectory == !want_dir)
119             {
120                 TRACE("root %p dir %p id %04x ret %p\n",
121                       root, dir, id, (const char*)root + entry[pos].u2.s3.OffsetToDirectory);
122                 return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry[pos].u2.s3.OffsetToDirectory);
123             }
124             break;
125         }
126         if (entry[pos].u1.s2.Id > id) max = pos - 1;
127         else min = pos + 1;
128     }
129     TRACE("root %p dir %p id %04x not found\n", root, dir, id );
130     return NULL;
131 }
132
133
134 /**********************************************************************
135  *  find_entry_by_name
136  *
137  * Find an entry by name in a resource directory
138  */
139 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_name( const IMAGE_RESOURCE_DIRECTORY *dir,
140                                                            LPCWSTR name, const void *root,
141                                                            int want_dir )
142 {
143     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
144     const IMAGE_RESOURCE_DIR_STRING_U *str;
145     int min, max, res, pos, namelen;
146
147     if (!HIWORD(name)) return find_entry_by_id( dir, LOWORD(name), root, want_dir );
148     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
149     namelen = strlenW(name);
150     min = 0;
151     max = dir->NumberOfNamedEntries - 1;
152     while (min <= max)
153     {
154         pos = (min + max) / 2;
155         str = (const IMAGE_RESOURCE_DIR_STRING_U *)((const char *)root + entry[pos].u1.s1.NameOffset);
156         res = strncmpW( name, str->NameString, str->Length );
157         if (!res && namelen == str->Length)
158         {
159             if (!entry[pos].u2.s3.DataIsDirectory == !want_dir)
160             {
161                 TRACE("root %p dir %p name %s ret %p\n",
162                       root, dir, debugstr_w(name), (const char*)root + entry[pos].u2.s3.OffsetToDirectory);
163                 return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry[pos].u2.s3.OffsetToDirectory);
164             }
165             break;
166         }
167         if (res < 0) max = pos - 1;
168         else min = pos + 1;
169     }
170     TRACE("root %p dir %p name %s not found\n", root, dir, debugstr_w(name) );
171     return NULL;
172 }
173
174
175 /**********************************************************************
176  *  find_entry
177  *
178  * Find a resource entry
179  */
180 static NTSTATUS find_entry( HMODULE hmod, const LDR_RESOURCE_INFO *info,
181                             ULONG level, const void **ret, int want_dir )
182 {
183     ULONG size;
184     const void *root;
185     const IMAGE_RESOURCE_DIRECTORY *resdirptr;
186     WORD list[9];  /* list of languages to try */
187     int i, pos = 0;
188
189     root = RtlImageDirectoryEntryToData( hmod, TRUE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &size );
190     if (!root) return STATUS_RESOURCE_DATA_NOT_FOUND;
191     resdirptr = root;
192
193     if (!level--) goto done;
194     if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Type, root, want_dir || level )))
195         return STATUS_RESOURCE_TYPE_NOT_FOUND;
196     if (!level--) return STATUS_SUCCESS;
197
198     resdirptr = *ret;
199     if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Name, root, want_dir || level )))
200         return STATUS_RESOURCE_NAME_NOT_FOUND;
201     if (!level--) return STATUS_SUCCESS;
202     if (level) return STATUS_INVALID_PARAMETER;  /* level > 3 */
203
204     /* 1. specified language */
205     pos = push_language( list, pos, info->Language );
206
207     /* 2. specified language with neutral sublanguage */
208     pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(info->Language), SUBLANG_NEUTRAL ) );
209
210     /* 3. neutral language with neutral sublanguage */
211     pos = push_language( list, pos, MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ) );
212
213     /* if no explicitly specified language, try some defaults */
214     if (PRIMARYLANGID(info->Language) == LANG_NEUTRAL)
215     {
216         /* user defaults, unless SYS_DEFAULT sublanguage specified  */
217         if (SUBLANGID(info->Language) != SUBLANG_SYS_DEFAULT)
218         {
219             /* 4. current thread locale language */
220             pos = push_language( list, pos, LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale) );
221
222             /* 5. user locale language */
223             pos = push_language( list, pos, LANGIDFROMLCID(user_lcid) );
224
225             /* 6. user locale language with neutral sublanguage  */
226             pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(user_lcid), SUBLANG_NEUTRAL ) );
227         }
228
229         /* now system defaults */
230
231         /* 7. system locale language */
232         pos = push_language( list, pos, LANGIDFROMLCID( system_lcid ) );
233
234         /* 8. system locale language with neutral sublanguage */
235         pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(system_lcid), SUBLANG_NEUTRAL ) );
236
237         /* 9. English */
238         pos = push_language( list, pos, MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ) );
239     }
240
241     resdirptr = *ret;
242     for (i = 0; i < pos; i++)
243         if ((*ret = find_entry_by_id( resdirptr, list[i], root, want_dir ))) return STATUS_SUCCESS;
244
245     /* if no explicitly specified language, return the first entry */
246     if (PRIMARYLANGID(info->Language) == LANG_NEUTRAL)
247     {
248         if ((*ret = find_first_entry( resdirptr, root, want_dir ))) return STATUS_SUCCESS;
249     }
250     return STATUS_RESOURCE_LANG_NOT_FOUND;
251
252 done:
253     *ret = resdirptr;
254     return STATUS_SUCCESS;
255 }
256
257
258 /**********************************************************************
259  *      LdrFindResourceDirectory_U  (NTDLL.@)
260  */
261 NTSTATUS WINAPI LdrFindResourceDirectory_U( HMODULE hmod, const LDR_RESOURCE_INFO *info,
262                                             ULONG level, const IMAGE_RESOURCE_DIRECTORY **dir )
263 {
264     const void *res;
265     NTSTATUS status;
266
267     __TRY
268     {
269         if (info) TRACE( "module %p type %s name %s lang %04lx level %ld\n",
270                      hmod, debugstr_w((LPCWSTR)info->Type),
271                      level > 1 ? debugstr_w((LPCWSTR)info->Name) : "",
272                      level > 2 ? info->Language : 0, level );
273
274         status = find_entry( hmod, info, level, &res, TRUE );
275         if (status == STATUS_SUCCESS) *dir = res;
276     }
277     __EXCEPT_PAGE_FAULT
278     {
279         return GetExceptionCode();
280     }
281     __ENDTRY;
282     return status;
283 }
284
285
286 /**********************************************************************
287  *      LdrFindResource_U  (NTDLL.@)
288  */
289 NTSTATUS WINAPI LdrFindResource_U( HMODULE hmod, const LDR_RESOURCE_INFO *info,
290                                    ULONG level, const IMAGE_RESOURCE_DATA_ENTRY **entry )
291 {
292     const void *res;
293     NTSTATUS status;
294
295     __TRY
296     {
297         if (info) TRACE( "module %p type %s name %s lang %04lx level %ld\n",
298                      hmod, debugstr_w((LPCWSTR)info->Type),
299                      level > 1 ? debugstr_w((LPCWSTR)info->Name) : "",
300                      level > 2 ? info->Language : 0, level );
301
302         status = find_entry( hmod, info, level, &res, FALSE );
303         if (status == STATUS_SUCCESS) *entry = res;
304     }
305     __EXCEPT_PAGE_FAULT
306     {
307         return GetExceptionCode();
308     }
309     __ENDTRY;
310     return status;
311 }
312
313
314 /* don't penalize other platforms stuff needed on i386 for compatibility */
315 #ifdef __i386__
316 NTSTATUS WINAPI access_resource( HMODULE hmod, const IMAGE_RESOURCE_DATA_ENTRY *entry,
317                                  void **ptr, ULONG *size )
318 #else
319 static inline NTSTATUS access_resource( HMODULE hmod, const IMAGE_RESOURCE_DATA_ENTRY *entry,
320                                         void **ptr, ULONG *size )
321 #endif
322 {
323     NTSTATUS status;
324
325     __TRY
326     {
327         ULONG dirsize;
328
329         if (!RtlImageDirectoryEntryToData( hmod, TRUE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &dirsize ))
330             status = STATUS_RESOURCE_DATA_NOT_FOUND;
331         else
332         {
333             if (ptr)
334             {
335                 if (is_data_file_module(hmod))
336                 {
337                     HMODULE mod = (HMODULE)((ULONG_PTR)hmod & ~1);
338                     *ptr = RtlImageRvaToVa( RtlImageNtHeader(mod), mod, entry->OffsetToData, NULL );
339                 }
340                 else *ptr = (char *)hmod + entry->OffsetToData;
341             }
342             if (size) *size = entry->Size;
343             status = STATUS_SUCCESS;
344         }
345     }
346     __EXCEPT_PAGE_FAULT
347     {
348         return GetExceptionCode();
349     }
350     __ENDTRY;
351     return status;
352 }
353
354 /**********************************************************************
355  *      LdrAccessResource  (NTDLL.@)
356  *
357  * NOTE
358  * On x86, Shrinker, an executable compressor, depends on the
359  * "call access_resource" instruction being there.
360  */
361 #ifdef __i386__
362 __ASM_GLOBAL_FUNC( LdrAccessResource,
363     "pushl %ebp\n\t"
364     "movl %esp, %ebp\n\t"
365     "subl $4,%esp\n\t"
366     "pushl 24(%ebp)\n\t"
367     "pushl 20(%ebp)\n\t"
368     "pushl 16(%ebp)\n\t"
369     "pushl 12(%ebp)\n\t"
370     "pushl 8(%ebp)\n\t"
371     "call " __ASM_NAME("access_resource") "\n\t"
372     "leave\n\t"
373     "ret $16"
374 );
375 #else
376 NTSTATUS WINAPI LdrAccessResource( HMODULE hmod, const IMAGE_RESOURCE_DATA_ENTRY *entry,
377                                    void **ptr, ULONG *size )
378 {
379     return access_resource( hmod, entry, ptr, size );
380 }
381 #endif
382
383 /**********************************************************************
384  *      RtlFindMessage  (NTDLL.@)
385  */
386 NTSTATUS WINAPI RtlFindMessage( HMODULE hmod, ULONG type, ULONG lang,
387                                 ULONG msg_id, const MESSAGE_RESOURCE_ENTRY **ret )
388 {
389     const MESSAGE_RESOURCE_DATA *data;
390     const MESSAGE_RESOURCE_BLOCK *block;
391     const IMAGE_RESOURCE_DATA_ENTRY *rsrc;
392     LDR_RESOURCE_INFO info;
393     NTSTATUS status;
394     void *ptr;
395     unsigned int i;
396
397     info.Type     = type;
398     info.Name     = 1;
399     info.Language = lang;
400
401     if ((status = LdrFindResource_U( hmod, &info, 3, &rsrc )) != STATUS_SUCCESS)
402         return status;
403     if ((status = LdrAccessResource( hmod, rsrc, &ptr, NULL )) != STATUS_SUCCESS)
404         return status;
405
406     data = ptr;
407     block = data->Blocks;
408     for (i = 0; i < data->NumberOfBlocks; i++, block++)
409     {
410         if (msg_id >= block->LowId && msg_id <= block->HighId)
411         {
412             const MESSAGE_RESOURCE_ENTRY *entry;
413
414             entry = (const MESSAGE_RESOURCE_ENTRY *)((const char *)data + block->OffsetToEntries);
415             for (i = msg_id - block->LowId; i > 0; i--)
416                 entry = (const MESSAGE_RESOURCE_ENTRY *)((const char *)entry + entry->Length);
417             *ret = entry;
418             return STATUS_SUCCESS;
419         }
420     }
421     return STATUS_MESSAGE_NOT_FOUND;
422 }
423
424 /**********************************************************************
425  *      RtlFormatMessage  (NTDLL.@)
426  *
427  * Formats a message (similar to sprintf).
428  *
429  * PARAMS
430  *   Message          [I] Message to format.
431  *   MaxWidth         [I] Maximum width in characters of each output line.
432  *   IgnoreInserts    [I] Whether to copy the message without processing inserts.
433  *   Ansi             [I] Whether Arguments may have ANSI strings.
434  *   ArgumentsIsArray [I] Whether Arguments is actually an array rather than a va_list *.
435  *   Buffer           [O] Buffer to store processed message in.
436  *   BufferSize       [I] Size of Buffer (in bytes?).
437  *
438  * RETURNS
439  *      NTSTATUS code.
440  */
441 NTSTATUS WINAPI RtlFormatMessage( LPWSTR Message, UCHAR MaxWidth,
442                                   BOOLEAN IgnoreInserts, BOOLEAN Ansi,
443                                   BOOLEAN ArgumentIsArray, va_list * Arguments,
444                                   LPWSTR Buffer, ULONG BufferSize )
445 {
446     FIXME("(%s, %u, %s, %s, %s, %p, %p, %ld)\n", debugstr_w(Message),
447         MaxWidth, IgnoreInserts ? "TRUE" : "FALSE", Ansi ? "TRUE" : "FALSE",
448         ArgumentIsArray ? "TRUE" : "FALSE", Arguments, Buffer, BufferSize);
449     return STATUS_SUCCESS;
450 }
451
452
453 /**********************************************************************
454  *      NtQueryDefaultLocale  (NTDLL.@)
455  */
456 NTSTATUS WINAPI NtQueryDefaultLocale( BOOLEAN user, LCID *lcid )
457 {
458     *lcid = user ? user_lcid : system_lcid;
459     return STATUS_SUCCESS;
460 }
461
462
463 /**********************************************************************
464  *      NtSetDefaultLocale  (NTDLL.@)
465  */
466 NTSTATUS WINAPI NtSetDefaultLocale( BOOLEAN user, LCID lcid )
467 {
468     if (user) user_lcid = lcid;
469     else
470     {
471         system_lcid = lcid;
472         system_ui_language = LANGIDFROMLCID(lcid); /* there is no separate call to set it */
473     }
474     return STATUS_SUCCESS;
475 }
476
477
478 /**********************************************************************
479  *      NtQueryDefaultUILanguage  (NTDLL.@)
480  */
481 NTSTATUS WINAPI NtQueryDefaultUILanguage( LANGID *lang )
482 {
483     *lang = user_ui_language;
484     return STATUS_SUCCESS;
485 }
486
487
488 /**********************************************************************
489  *      NtSetDefaultUILanguage  (NTDLL.@)
490  */
491 NTSTATUS WINAPI NtSetDefaultUILanguage( LANGID lang )
492 {
493     user_ui_language = lang;
494     return STATUS_SUCCESS;
495 }
496
497
498 /**********************************************************************
499  *      NtQueryInstallUILanguage  (NTDLL.@)
500  */
501 NTSTATUS WINAPI NtQueryInstallUILanguage( LANGID *lang )
502 {
503     *lang = system_ui_language;
504     return STATUS_SUCCESS;
505 }