d3478b3e8e1f1a473ff0af5c7abbc7e93c74b488
[reactos.git] / reactos / lib / ntdll / ldr / res.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: lib/ntdll/ldr/res.c
6 * PURPOSE: Resource access for PE executables
7 * PROGRAMMERS: Jean Michault
8 * Rex Jolliff (rex@lvcablemodem.com)
9 * Robert Dickenson (robd@mok.lvcm.com)
10 * NOTES: Parts based on Wine code
11 * Copyright 1995 Thomas Sandford
12 * Copyright 1996 Martin von Loewis
13 * Copyright 2003 Alexandre Julliard
14 * Copyright 1993 Robert J. Amstadt
15 * Copyright 1995 Alexandre Julliard
16 * Copyright 1997 Marcus Meissner
17 */
18
19 /*
20 * TODO:
21 * - any comments ??
22 */
23
24 /* INCLUDES *****************************************************************/
25
26 #include <ntdll.h>
27 #define NDEBUG
28 #include <debug.h>
29
30 /* PROTOTYPES ****************************************************************/
31
32
33
34 /* FUNCTIONS *****************************************************************/
35
36 static PIMAGE_RESOURCE_DIRECTORY_ENTRY FASTCALL
37 FindEntryById(PIMAGE_RESOURCE_DIRECTORY ResDir,
38 ULONG Id)
39 {
40 PIMAGE_RESOURCE_DIRECTORY_ENTRY ResEntry;
41 LONG low, high, mid, result;
42
43 /* We use ID number instead of string */
44 ResEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(ResDir + 1) + ResDir->NumberOfNamedEntries;
45 DPRINT("ResEntry %x - Resource ID number instead of string\n", (ULONG)ResEntry);
46 DPRINT("EntryCount %d\n", (ULONG)ResDir->NumberOfIdEntries);
47
48 low = 0;
49 high = ResDir->NumberOfIdEntries - 1;
50 mid = high/2;
51 while( low <= high ) {
52 result = Id - ResEntry[mid].Name;
53 if(result == 0)
54 return ResEntry + mid;
55 if(result < 0)
56 high = mid - 1;
57 else
58 low = mid + 1;
59
60 mid = (low + high)/2;
61 }
62
63 return NULL;
64 }
65
66 static int FASTCALL
67 PushLanguage(WORD *list, int pos, WORD lang)
68 {
69 int i;
70
71 for (i = 0; i < pos; i++) {
72 if (list[i] == lang) {
73 return pos;
74 }
75 }
76
77 list[pos++] = lang;
78
79 return pos;
80 }
81
82 /*
83 Status = LdrFindResource_U (hModule,
84 &ResourceInfo,
85 RESOURCE_DATA_LEVEL,
86 &ResourceDataEntry);
87 */
88 NTSTATUS NTAPI
89 LdrFindResource_U(PVOID BaseAddress,
90 PLDR_RESOURCE_INFO ResourceInfo,
91 ULONG Level,
92 PIMAGE_RESOURCE_DATA_ENTRY* ResourceDataEntry)
93 {
94 PIMAGE_RESOURCE_DIRECTORY ResDir;
95 PIMAGE_RESOURCE_DIRECTORY ResBase;
96 PIMAGE_RESOURCE_DIRECTORY_ENTRY ResEntry;
97 NTSTATUS Status = STATUS_SUCCESS;
98 PWCHAR ws;
99 ULONG i;
100 ULONG Id;
101 LONG low, high, mid, result;
102 WORD list[9]; /* list of languages to try */
103 int j, pos = 0;
104 LCID UserLCID, SystemLCID;
105 LANGID UserLangID, SystemLangID;
106 BOOLEAN MappedAsDataFile;
107
108 MappedAsDataFile = LdrMappedAsDataFile(&BaseAddress);
109 DPRINT("LdrFindResource_U(%08x, %08x, %d, %08x)\n", BaseAddress, ResourceInfo, Level, ResourceDataEntry);
110
111 /* Get the pointer to the resource directory */
112 ResDir = (PIMAGE_RESOURCE_DIRECTORY)RtlImageDirectoryEntryToData(BaseAddress,
113 ! MappedAsDataFile, IMAGE_DIRECTORY_ENTRY_RESOURCE, &i);
114 if (ResDir == NULL) {
115 return STATUS_RESOURCE_DATA_NOT_FOUND;
116 }
117
118 DPRINT("ResourceDirectory: %x Size: %d\n", (ULONG)ResDir, (int)i);
119
120 ResBase = ResDir;
121
122 /* Let's go into resource tree */
123 for (i = 0; i < (2 < Level ? 2 : Level); i++) {
124 DPRINT("ResDir: %x Level: %d\n", (ULONG)ResDir, i);
125
126 Id = ((PULONG)ResourceInfo)[i];
127
128 if (Id & 0xFFFF0000) {
129 /* Resource name is a unicode string */
130 ResEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(ResDir + 1);
131 DPRINT("ResEntry %x - Resource name is a unicode string\n", (ULONG)ResEntry);
132 DPRINT("EntryCount %d\n", (ULONG)ResDir->NumberOfNamedEntries);
133
134 low = 0;
135 high = ResDir->NumberOfNamedEntries - 1;
136 mid = high/2;
137 while( low <= high ) {
138 /* Does we need check if it's named entry, think not */
139 ws = (PWCHAR)((ULONG)ResBase + (ResEntry[mid].Name & 0x7FFFFFFF));
140 result = _wcsnicmp((PWCHAR)Id, ws + 1, *ws);
141 /* Need double check for lexical & length */
142 if(result == 0) {
143 result = (wcslen((PWCHAR)Id) - (int)*ws);
144 if(result == 0) {
145 ResEntry += mid;
146 goto found;
147 }
148 }
149 if(result < 0)
150 high = mid - 1;
151 else
152 low = mid + 1;
153
154 mid = (low + high)/2;
155 }
156 } else {
157 /* We use ID number instead of string */
158 ResEntry = FindEntryById(ResDir, Id);
159 if (NULL != ResEntry) goto found;
160 }
161
162 switch (i) {
163 case 0:
164 DPRINT("Error %lu - STATUS_RESOURCE_TYPE_NOT_FOUND\n", i);
165 return STATUS_RESOURCE_TYPE_NOT_FOUND;
166 case 1:
167 DPRINT("Error %lu - STATUS_RESOURCE_NAME_NOT_FOUND\n", i);
168 return STATUS_RESOURCE_NAME_NOT_FOUND;
169 case 2:
170 if (ResDir->NumberOfNamedEntries || ResDir->NumberOfIdEntries) {
171 /* Use the first available language */
172 ResEntry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(ResDir + 1);
173 break;
174 }
175 DPRINT("Error %lu - STATUS_RESOURCE_LANG_NOT_FOUND\n", i);
176 return STATUS_RESOURCE_LANG_NOT_FOUND;
177 case 3:
178 DPRINT("Error %lu - STATUS_RESOURCE_DATA_NOT_FOUND\n", i);
179 return STATUS_RESOURCE_DATA_NOT_FOUND;
180 default:
181 DPRINT("Error %lu - STATUS_INVALID_PARAMETER\n", i);
182 return STATUS_INVALID_PARAMETER;
183 }
184 found:;
185 ResDir = (PIMAGE_RESOURCE_DIRECTORY)((ULONG)ResBase +
186 (ResEntry->OffsetToData & 0x7FFFFFFF));
187 }
188
189 if (3 <= Level) {
190 /* 1. specified language */
191 pos = PushLanguage(list, pos, ResourceInfo->Language );
192
193 /* 2. specified language with neutral sublanguage */
194 pos = PushLanguage(list, pos, MAKELANGID(PRIMARYLANGID(ResourceInfo->Language), SUBLANG_NEUTRAL));
195
196 /* 3. neutral language with neutral sublanguage */
197 pos = PushLanguage(list, pos, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
198
199 /* if no explicitly specified language, try some defaults */
200 if (LANG_NEUTRAL == PRIMARYLANGID(ResourceInfo->Language)) {
201 /* user defaults, unless SYS_DEFAULT sublanguage specified */
202 if (SUBLANG_SYS_DEFAULT != SUBLANGID(ResourceInfo->Language)) {
203 NtQueryDefaultLocale(TRUE, &UserLCID);
204 UserLangID = LANGIDFROMLCID(UserLCID);
205
206 /* 4. current thread locale language */
207 pos = PushLanguage(list, pos, LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale));
208
209 /* 5. user locale language */
210 pos = PushLanguage(list, pos, UserLangID);
211
212 /* 6. user locale language with neutral sublanguage */
213 pos = PushLanguage(list, pos, MAKELANGID(PRIMARYLANGID(UserLangID),
214 SUBLANG_NEUTRAL));
215 }
216
217 /* now system defaults */
218 NtQueryDefaultLocale(FALSE, &SystemLCID);
219 SystemLangID = LANGIDFROMLCID(SystemLCID);
220
221 /* 7. system locale language */
222 pos = PushLanguage(list, pos, SystemLangID);
223
224 /* 8. system locale language with neutral sublanguage */
225 pos = PushLanguage(list, pos, MAKELANGID(PRIMARYLANGID(SystemLangID),
226 SUBLANG_NEUTRAL));
227
228 /* 9. English */
229 pos = PushLanguage(list, pos, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT));
230 }
231
232 ResEntry = NULL;
233 for (j = 0; NULL == ResEntry && j < pos; j++)
234 ResEntry = FindEntryById(ResDir, list[j]);
235 if (NULL == ResEntry) {
236 if (ResDir->NumberOfNamedEntries || ResDir->NumberOfIdEntries) {
237 /* Use the first available language */
238 ResEntry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(ResDir + 1);
239 } else {
240 DPRINT("Error - STATUS_RESOURCE_LANG_NOT_FOUND\n", i);
241 return STATUS_RESOURCE_LANG_NOT_FOUND;
242 }
243 }
244 ResDir = (PIMAGE_RESOURCE_DIRECTORY)((ULONG)ResBase +
245 (ResEntry->OffsetToData & 0x7FFFFFFF));
246 if (3 < Level) {
247 DPRINT("Error - STATUS_INVALID_PARAMETER\n", i);
248 return STATUS_INVALID_PARAMETER;
249 }
250 }
251 DPRINT("ResourceDataEntry: %x\n", (ULONG)ResDir);
252
253 if (ResourceDataEntry) {
254 *ResourceDataEntry = (PVOID)ResDir;
255 }
256 return Status;
257 }
258
259
260 /*
261 * @implemented
262 */
263 NTSTATUS NTAPI
264 LdrAccessResource(IN PVOID BaseAddress,
265 IN PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry,
266 OUT PVOID* Resource OPTIONAL,
267 OUT PULONG Size OPTIONAL)
268 {
269 PIMAGE_SECTION_HEADER Section;
270 PIMAGE_NT_HEADERS NtHeader;
271 ULONG SectionRva;
272 ULONG SectionVa;
273 ULONG DataSize;
274 ULONG Offset = 0;
275 ULONG Data;
276 BOOLEAN MappedAsDataFile;
277
278 if(!ResourceDataEntry)
279 return STATUS_RESOURCE_DATA_NOT_FOUND;
280
281 MappedAsDataFile = LdrMappedAsDataFile(&BaseAddress);
282 Data = (ULONG)RtlImageDirectoryEntryToData(BaseAddress,
283 TRUE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &DataSize);
284 if (Data == 0) {
285 return STATUS_RESOURCE_DATA_NOT_FOUND;
286 }
287 if (MappedAsDataFile) {
288 /* loaded as ordinary file */
289 NtHeader = RtlImageNtHeader(BaseAddress);
290 Offset = (ULONG)BaseAddress - Data + NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
291 Section = RtlImageRvaToSection(NtHeader, BaseAddress, NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
292 if (Section == NULL) {
293 return STATUS_RESOURCE_DATA_NOT_FOUND;
294 }
295 if (Section->Misc.VirtualSize < ResourceDataEntry->OffsetToData) {
296 SectionRva = RtlImageRvaToSection (NtHeader, BaseAddress, ResourceDataEntry->OffsetToData)->VirtualAddress;
297 SectionVa = RtlImageRvaToVa(NtHeader, BaseAddress, SectionRva, NULL);
298 Offset = SectionRva - SectionVa + Data - Section->VirtualAddress;
299 }
300 }
301 if (Resource) {
302 *Resource = (PVOID)(ResourceDataEntry->OffsetToData - Offset + (ULONG)BaseAddress);
303 }
304 if (Size) {
305 *Size = ResourceDataEntry->Size;
306 }
307 return STATUS_SUCCESS;
308 }
309
310
311 /*
312 * @implemented
313 */
314 NTSTATUS NTAPI
315 LdrFindResourceDirectory_U(IN PVOID BaseAddress,
316 IN PLDR_RESOURCE_INFO info,
317 IN ULONG level,
318 OUT PIMAGE_RESOURCE_DIRECTORY* addr)
319 {
320 PIMAGE_RESOURCE_DIRECTORY ResDir;
321 PIMAGE_RESOURCE_DIRECTORY_ENTRY ResEntry;
322 ULONG EntryCount;
323 ULONG i;
324 NTSTATUS Status = STATUS_SUCCESS;
325 WCHAR* ws;
326 PWCHAR* name = (PWCHAR*) info;
327
328 /* Get the pointer to the resource directory */
329 ResDir = (PIMAGE_RESOURCE_DIRECTORY)
330 RtlImageDirectoryEntryToData(BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &i);
331 if (ResDir == NULL) {
332 return STATUS_RESOURCE_DATA_NOT_FOUND;
333 }
334
335 /* Let's go into resource tree */
336 for (i = 0; i < level; i++, name++) {
337 EntryCount = ResDir->NumberOfNamedEntries;
338 ResEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(ResDir + 1);
339 if ((ULONG)(*name) & 0xFFFF0000) {
340 /* Resource name is a unicode string */
341 for (; EntryCount--; ResEntry++) {
342 /* Scan entries for equal name */
343 if (ResEntry->Name & 0x80000000) {
344 ws = (WCHAR*)((ULONG)ResDir + (ResEntry->Name & 0x7FFFFFFF));
345 if (!wcsncmp(*name, ws + 1, *ws) && wcslen(*name) == (int)*ws) {
346 goto found;
347 }
348 }
349 }
350 } else {
351 /* We use ID number instead of string */
352 ResEntry += EntryCount;
353 EntryCount = ResDir->NumberOfIdEntries;
354 for (; EntryCount--; ResEntry++) {
355 /* Scan entries for equal name */
356 if (ResEntry->Name == (ULONG)(*name))
357 goto found;
358 }
359 }
360 switch (i) {
361 case 0:
362 return STATUS_RESOURCE_TYPE_NOT_FOUND;
363 case 1:
364 return STATUS_RESOURCE_NAME_NOT_FOUND;
365 case 2:
366 Status = STATUS_RESOURCE_LANG_NOT_FOUND;
367 /* Just use first language entry */
368 if (ResDir->NumberOfNamedEntries || ResDir->NumberOfIdEntries) {
369 ResEntry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(ResDir + 1);
370 break;
371 }
372 return Status;
373 case 3:
374 return STATUS_RESOURCE_DATA_NOT_FOUND;
375 default:
376 return STATUS_INVALID_PARAMETER;
377 }
378 found:;
379 ResDir = (PIMAGE_RESOURCE_DIRECTORY)((ULONG)ResDir + ResEntry->OffsetToData);
380 }
381 if (addr) {
382 *addr = (PVOID)ResDir;
383 }
384 return Status;
385 }
386
387 /* EOF */