ntdll: Add support for saving/restoring CFA frame state.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 "winnt.h"
42 #include "winternl.h"
43 #include "wine/exception.h"
44 #include "wine/unicode.h"
45 #include "wine/debug.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(resource);
48
49 static LCID user_lcid, system_lcid;
50 static LANGID user_ui_language, system_ui_language;
51
52 #define IS_INTRESOURCE(x)       (((ULONG_PTR)(x) >> 16) == 0)
53
54 /**********************************************************************
55  *  is_data_file_module
56  *
57  * Check if a module handle is for a LOAD_LIBRARY_AS_DATAFILE module.
58  */
59 static inline 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 (IS_INTRESOURCE(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     if (size < sizeof(*resdirptr)) return STATUS_RESOURCE_DATA_NOT_FOUND;
192     resdirptr = root;
193
194     if (!level--) goto done;
195     if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Type, root, want_dir || level )))
196         return STATUS_RESOURCE_TYPE_NOT_FOUND;
197     if (!level--) return STATUS_SUCCESS;
198
199     resdirptr = *ret;
200     if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Name, root, want_dir || level )))
201         return STATUS_RESOURCE_NAME_NOT_FOUND;
202     if (!level--) return STATUS_SUCCESS;
203     if (level) return STATUS_INVALID_PARAMETER;  /* level > 3 */
204
205     /* 1. specified language */
206     pos = push_language( list, pos, info->Language );
207
208     /* 2. specified language with neutral sublanguage */
209     pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(info->Language), SUBLANG_NEUTRAL ) );
210
211     /* 3. neutral language with neutral sublanguage */
212     pos = push_language( list, pos, MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ) );
213
214     /* if no explicitly specified language, try some defaults */
215     if (PRIMARYLANGID(info->Language) == LANG_NEUTRAL)
216     {
217         /* user defaults, unless SYS_DEFAULT sublanguage specified  */
218         if (SUBLANGID(info->Language) != SUBLANG_SYS_DEFAULT)
219         {
220             /* 4. current thread locale language */
221             pos = push_language( list, pos, LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale) );
222
223             /* 5. user locale language */
224             pos = push_language( list, pos, LANGIDFROMLCID(user_lcid) );
225
226             /* 6. user locale language with neutral sublanguage  */
227             pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(user_lcid), SUBLANG_NEUTRAL ) );
228         }
229
230         /* now system defaults */
231
232         /* 7. system locale language */
233         pos = push_language( list, pos, LANGIDFROMLCID( system_lcid ) );
234
235         /* 8. system locale language with neutral sublanguage */
236         pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(system_lcid), SUBLANG_NEUTRAL ) );
237
238         /* 9. English */
239         pos = push_language( list, pos, MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ) );
240     }
241
242     resdirptr = *ret;
243     for (i = 0; i < pos; i++)
244         if ((*ret = find_entry_by_id( resdirptr, list[i], root, want_dir ))) return STATUS_SUCCESS;
245
246     /* if no explicitly specified language, return the first entry */
247     if (PRIMARYLANGID(info->Language) == LANG_NEUTRAL)
248     {
249         if ((*ret = find_first_entry( resdirptr, root, want_dir ))) return STATUS_SUCCESS;
250     }
251     return STATUS_RESOURCE_LANG_NOT_FOUND;
252
253 done:
254     *ret = resdirptr;
255     return STATUS_SUCCESS;
256 }
257
258
259 /**********************************************************************
260  *      LdrFindResourceDirectory_U  (NTDLL.@)
261  */
262 NTSTATUS WINAPI LdrFindResourceDirectory_U( HMODULE hmod, const LDR_RESOURCE_INFO *info,
263                                             ULONG level, const IMAGE_RESOURCE_DIRECTORY **dir )
264 {
265     const void *res;
266     NTSTATUS status;
267
268     __TRY
269     {
270         if (info) TRACE( "module %p type %s name %s lang %04x level %d\n",
271                      hmod, debugstr_w((LPCWSTR)info->Type),
272                      level > 1 ? debugstr_w((LPCWSTR)info->Name) : "",
273                      level > 2 ? info->Language : 0, level );
274
275         status = find_entry( hmod, info, level, &res, TRUE );
276         if (status == STATUS_SUCCESS) *dir = res;
277     }
278     __EXCEPT_PAGE_FAULT
279     {
280         return GetExceptionCode();
281     }
282     __ENDTRY;
283     return status;
284 }
285
286
287 /**********************************************************************
288  *      LdrFindResource_U  (NTDLL.@)
289  */
290 NTSTATUS WINAPI LdrFindResource_U( HMODULE hmod, const LDR_RESOURCE_INFO *info,
291                                    ULONG level, const IMAGE_RESOURCE_DATA_ENTRY **entry )
292 {
293     const void *res;
294     NTSTATUS status;
295
296     __TRY
297     {
298         if (info) TRACE( "module %p type %s name %s lang %04x level %d\n",
299                      hmod, debugstr_w((LPCWSTR)info->Type),
300                      level > 1 ? debugstr_w((LPCWSTR)info->Name) : "",
301                      level > 2 ? info->Language : 0, level );
302
303         status = find_entry( hmod, info, level, &res, FALSE );
304         if (status == STATUS_SUCCESS) *entry = res;
305     }
306     __EXCEPT_PAGE_FAULT
307     {
308         return GetExceptionCode();
309     }
310     __ENDTRY;
311     return status;
312 }
313
314
315 /* don't penalize other platforms stuff needed on i386 for compatibility */
316 #ifdef __i386__
317 NTSTATUS WINAPI access_resource( HMODULE hmod, const IMAGE_RESOURCE_DATA_ENTRY *entry,
318                                  void **ptr, ULONG *size )
319 #else
320 static inline NTSTATUS access_resource( HMODULE hmod, const IMAGE_RESOURCE_DATA_ENTRY *entry,
321                                         void **ptr, ULONG *size )
322 #endif
323 {
324     NTSTATUS status;
325
326     __TRY
327     {
328         ULONG dirsize;
329
330         if (!RtlImageDirectoryEntryToData( hmod, TRUE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &dirsize ))
331             status = STATUS_RESOURCE_DATA_NOT_FOUND;
332         else
333         {
334             if (ptr)
335             {
336                 if (is_data_file_module(hmod))
337                 {
338                     HMODULE mod = (HMODULE)((ULONG_PTR)hmod & ~1);
339                     *ptr = RtlImageRvaToVa( RtlImageNtHeader(mod), mod, entry->OffsetToData, NULL );
340                 }
341                 else *ptr = (char *)hmod + entry->OffsetToData;
342             }
343             if (size) *size = entry->Size;
344             status = STATUS_SUCCESS;
345         }
346     }
347     __EXCEPT_PAGE_FAULT
348     {
349         return GetExceptionCode();
350     }
351     __ENDTRY;
352     return status;
353 }
354
355 /**********************************************************************
356  *      LdrAccessResource  (NTDLL.@)
357  *
358  * NOTE
359  * On x86, Shrinker, an executable compressor, depends on the
360  * "call access_resource" instruction being there.
361  */
362 #ifdef __i386__
363 __ASM_STDCALL_FUNC( LdrAccessResource, 16,
364     "pushl %ebp\n\t"
365     "movl %esp, %ebp\n\t"
366     "subl $4,%esp\n\t"
367     "pushl 24(%ebp)\n\t"
368     "pushl 20(%ebp)\n\t"
369     "pushl 16(%ebp)\n\t"
370     "pushl 12(%ebp)\n\t"
371     "pushl 8(%ebp)\n\t"
372     "call " __ASM_NAME("access_resource") "\n\t"
373     "leave\n\t"
374     "ret $16"
375 )
376 #else
377 NTSTATUS WINAPI LdrAccessResource( HMODULE hmod, const IMAGE_RESOURCE_DATA_ENTRY *entry,
378                                    void **ptr, ULONG *size )
379 {
380     return access_resource( hmod, entry, ptr, size );
381 }
382 #endif
383
384 /**********************************************************************
385  *      RtlFindMessage  (NTDLL.@)
386  */
387 NTSTATUS WINAPI RtlFindMessage( HMODULE hmod, ULONG type, ULONG lang,
388                                 ULONG msg_id, const MESSAGE_RESOURCE_ENTRY **ret )
389 {
390     const MESSAGE_RESOURCE_DATA *data;
391     const MESSAGE_RESOURCE_BLOCK *block;
392     const IMAGE_RESOURCE_DATA_ENTRY *rsrc;
393     LDR_RESOURCE_INFO info;
394     NTSTATUS status;
395     void *ptr;
396     unsigned int i;
397
398     info.Type     = type;
399     info.Name     = 1;
400     info.Language = lang;
401
402     if ((status = LdrFindResource_U( hmod, &info, 3, &rsrc )) != STATUS_SUCCESS)
403         return status;
404     if ((status = LdrAccessResource( hmod, rsrc, &ptr, NULL )) != STATUS_SUCCESS)
405         return status;
406
407     data = ptr;
408     block = data->Blocks;
409     for (i = 0; i < data->NumberOfBlocks; i++, block++)
410     {
411         if (msg_id >= block->LowId && msg_id <= block->HighId)
412         {
413             const MESSAGE_RESOURCE_ENTRY *entry;
414
415             entry = (const MESSAGE_RESOURCE_ENTRY *)((const char *)data + block->OffsetToEntries);
416             for (i = msg_id - block->LowId; i > 0; i--)
417                 entry = (const MESSAGE_RESOURCE_ENTRY *)((const char *)entry + entry->Length);
418             *ret = entry;
419             return STATUS_SUCCESS;
420         }
421     }
422     return STATUS_MESSAGE_NOT_FOUND;
423 }
424
425 /**********************************************************************
426  *      RtlFormatMessage  (NTDLL.@)
427  *
428  * Formats a message (similar to sprintf).
429  *
430  * PARAMS
431  *   Message          [I] Message to format.
432  *   MaxWidth         [I] Maximum width in characters of each output line.
433  *   IgnoreInserts    [I] Whether to copy the message without processing inserts.
434  *   Ansi             [I] Whether Arguments may have ANSI strings.
435  *   ArgumentsIsArray [I] Whether Arguments is actually an array rather than a va_list *.
436  *   Buffer           [O] Buffer to store processed message in.
437  *   BufferSize       [I] Size of Buffer (in bytes?).
438  *
439  * RETURNS
440  *      NTSTATUS code.
441  */
442 NTSTATUS WINAPI RtlFormatMessage( LPWSTR Message, UCHAR MaxWidth,
443                                   BOOLEAN IgnoreInserts, BOOLEAN Ansi,
444                                   BOOLEAN ArgumentIsArray, __ms_va_list * Arguments,
445                                   LPWSTR Buffer, ULONG BufferSize )
446 {
447     FIXME("(%s, %u, %s, %s, %s, %p, %p, %d)\n", debugstr_w(Message),
448         MaxWidth, IgnoreInserts ? "TRUE" : "FALSE", Ansi ? "TRUE" : "FALSE",
449         ArgumentIsArray ? "TRUE" : "FALSE", Arguments, Buffer, BufferSize);
450     return STATUS_SUCCESS;
451 }
452
453
454 /**********************************************************************
455  *      NtQueryDefaultLocale  (NTDLL.@)
456  */
457 NTSTATUS WINAPI NtQueryDefaultLocale( BOOLEAN user, LCID *lcid )
458 {
459     *lcid = user ? user_lcid : system_lcid;
460     return STATUS_SUCCESS;
461 }
462
463
464 /**********************************************************************
465  *      NtSetDefaultLocale  (NTDLL.@)
466  */
467 NTSTATUS WINAPI NtSetDefaultLocale( BOOLEAN user, LCID lcid )
468 {
469     if (user) user_lcid = lcid;
470     else
471     {
472         system_lcid = lcid;
473         system_ui_language = LANGIDFROMLCID(lcid); /* there is no separate call to set it */
474     }
475     return STATUS_SUCCESS;
476 }
477
478
479 /**********************************************************************
480  *      NtQueryDefaultUILanguage  (NTDLL.@)
481  */
482 NTSTATUS WINAPI NtQueryDefaultUILanguage( LANGID *lang )
483 {
484     *lang = user_ui_language;
485     return STATUS_SUCCESS;
486 }
487
488
489 /**********************************************************************
490  *      NtSetDefaultUILanguage  (NTDLL.@)
491  */
492 NTSTATUS WINAPI NtSetDefaultUILanguage( LANGID lang )
493 {
494     user_ui_language = lang;
495     return STATUS_SUCCESS;
496 }
497
498
499 /**********************************************************************
500  *      NtQueryInstallUILanguage  (NTDLL.@)
501  */
502 NTSTATUS WINAPI NtQueryInstallUILanguage( LANGID *lang )
503 {
504     *lang = system_ui_language;
505     return STATUS_SUCCESS;
506 }