Rev 433 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
402 | daniel-mar | 1 | /* |
2 | 32/64 bit "standalone" ASCII Resource Modification methods for Win9x compatibility |
||
415 | daniel-mar | 3 | includes many fixes (marked with "Fix by Daniel Marschall" comment) |
402 | daniel-mar | 4 | Copyright (C) 2021 Daniel Marschall, ViaThinkSoft |
5 | based on the code of the Wine Project |
||
6 | Copyright 1993 Robert J. Amstadt |
||
7 | Copyright 1995, 2003 Alexandre Julliard |
||
8 | Copyright 2006 Mike McCormack |
||
9 | and its linked list support |
||
10 | Copyright (C) 2002 Alexandre Julliard |
||
11 | |||
12 | This program is free software; you can redistribute it and/or modify |
||
13 | it under the terms of the GNU General Public License as published by |
||
14 | the Free Software Foundation; either version 2 of the License, or |
||
15 | (at your option) any later version. |
||
16 | |||
17 | This program is distributed in the hope that it will be useful, |
||
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
20 | GNU General Public License for more details. |
||
21 | |||
22 | You should have received a copy of the GNU General Public License |
||
23 | along with this program; if not, write to the Free Software |
||
24 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||
25 | */ |
||
26 | |||
27 | /* |
||
28 | |||
29 | TODO / Things that don't work correctly: |
||
30 | - Resources cannot be deleted correctly: see https://bugs.winehq.org/show_bug.cgi?id=52046 |
||
31 | - Errors, e.g. in EndUpdateResource cannot be retrieved by GetLastError |
||
415 | daniel-mar | 32 | - The field "Size of initialized data" of an OpenWatcom compiled image got changed by these procedures. |
33 | Not sure if this would break something! We should be very careful with that field, since it affects code rather than resources?! |
||
402 | daniel-mar | 34 | |
35 | */ |
||
36 | |||
37 | #define NONAMELESSUNION |
||
38 | #define NONAMELESSSTRUCT |
||
39 | |||
40 | #ifndef DUMMYUNIONNAME |
||
41 | #if defined(NONAMELESSUNION) || !defined(_MSC_EXTENSIONS) |
||
42 | #define DUMMYUNIONNAME u |
||
43 | #define DUMMYUNIONNAME2 u2 |
||
44 | #define DUMMYUNIONNAME3 u3 |
||
45 | #define DUMMYUNIONNAME4 u4 |
||
46 | #define DUMMYUNIONNAME5 u5 |
||
47 | #define DUMMYUNIONNAME6 u6 |
||
48 | #define DUMMYUNIONNAME7 u7 |
||
49 | #define DUMMYUNIONNAME8 u8 |
||
50 | #define DUMMYUNIONNAME9 u9 |
||
51 | #else |
||
52 | #define DUMMYUNIONNAME |
||
53 | #define DUMMYUNIONNAME2 |
||
54 | #define DUMMYUNIONNAME3 |
||
55 | #define DUMMYUNIONNAME4 |
||
56 | #define DUMMYUNIONNAME5 |
||
57 | #define DUMMYUNIONNAME6 |
||
58 | #define DUMMYUNIONNAME7 |
||
59 | #define DUMMYUNIONNAME8 |
||
60 | #define DUMMYUNIONNAME9 |
||
61 | #endif |
||
62 | #endif // DUMMYUNIONNAME |
||
63 | |||
64 | #ifndef DUMMYSTRUCTNAME |
||
65 | #if defined(NONAMELESSUNION) || !defined(_MSC_EXTENSIONS) |
||
66 | #define DUMMYSTRUCTNAME s |
||
67 | #define DUMMYSTRUCTNAME2 s2 |
||
68 | #define DUMMYSTRUCTNAME3 s3 |
||
69 | #define DUMMYSTRUCTNAME4 s4 |
||
70 | #define DUMMYSTRUCTNAME5 s5 |
||
71 | #else |
||
72 | #define DUMMYSTRUCTNAME |
||
73 | #define DUMMYSTRUCTNAME2 |
||
74 | #define DUMMYSTRUCTNAME3 |
||
75 | #define DUMMYSTRUCTNAME4 |
||
76 | #define DUMMYSTRUCTNAME5 |
||
77 | #endif |
||
78 | #endif // DUMMYSTRUCTNAME |
||
79 | |||
80 | //#define WIN32_NO_STATUS |
||
81 | |||
82 | #include "compat_win_resource.h" |
||
83 | |||
84 | //#include "ntstatus.h" |
||
85 | |||
86 | #ifndef STATUS_SUCCESS |
||
87 | #define STATUS_SUCCESS ((DWORD )0x00000000L) |
||
88 | #endif |
||
89 | |||
90 | // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
||
91 | // #include "winnt.h" (renamed) |
||
92 | // Required because built-in OpenWatcom structs don't have dummy union names |
||
93 | // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
||
94 | |||
95 | typedef struct _MyIMAGE_RESOURCE_DIRECTORY_ENTRY { |
||
96 | union { |
||
97 | struct { |
||
98 | DWORD NameOffset : 31; |
||
99 | DWORD NameIsString : 1; |
||
100 | } DUMMYSTRUCTNAME; |
||
101 | DWORD Name; |
||
102 | WORD Id; |
||
103 | } DUMMYUNIONNAME; |
||
104 | union { |
||
105 | DWORD OffsetToData; |
||
106 | struct { |
||
107 | DWORD OffsetToDirectory : 31; |
||
108 | DWORD DataIsDirectory : 1; |
||
109 | } DUMMYSTRUCTNAME2; |
||
110 | } DUMMYUNIONNAME2; |
||
111 | } MyIMAGE_RESOURCE_DIRECTORY_ENTRY, * PMyIMAGE_RESOURCE_DIRECTORY_ENTRY; |
||
112 | |||
113 | // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
||
114 | // #include "winternl.h" |
||
115 | // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
||
116 | |||
117 | typedef struct _UNICODE_STRING { |
||
118 | USHORT Length; |
||
119 | USHORT MaximumLength; |
||
120 | PWSTR Buffer; |
||
121 | } UNICODE_STRING; |
||
122 | typedef UNICODE_STRING* PUNICODE_STRING; |
||
123 | typedef CONST char* PCSZ; |
||
124 | |||
125 | // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
||
126 | // Supplement created by Daniel Marschall |
||
406 | daniel-mar | 127 | // Attention: These supplements are VERY simple and they ONLY accept ASCII! |
402 | daniel-mar | 128 | // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
129 | |||
130 | NTSTATUS WINAPI _RtlCreateUnicodeStringFromAsciiz(PUNICODE_STRING target, LPCSTR src) |
||
131 | { |
||
132 | // Simple implementation by Daniel Marschall |
||
133 | size_t i; |
||
134 | |||
135 | target->Length = (USHORT)(strlen(src) * sizeof(WCHAR)); |
||
136 | target->MaximumLength = target->Length * sizeof(WCHAR) + sizeof(WCHAR)/*NUL*/; |
||
137 | if (!(target->Buffer = (PWSTR)HeapAlloc(GetProcessHeap(), 0, target->MaximumLength))) |
||
138 | return STATUS_NO_MEMORY; |
||
139 | memset(target->Buffer, 0, target->MaximumLength); |
||
140 | |||
141 | for (i = 0; i < strlen(src); i++) { |
||
142 | // C++ wrong warning: Buffer overflow (C6386) |
||
143 | #pragma warning(suppress : 6386) |
||
144 | target->Buffer[i] = (WCHAR)src[i]; |
||
145 | } |
||
146 | |||
147 | return STATUS_SUCCESS; |
||
148 | } |
||
149 | |||
406 | daniel-mar | 150 | NTSTATUS WINAPI _InplaceRtlUpcaseUnicodeString(PUNICODE_STRING str) { |
151 | int i; |
||
152 | |||
153 | for (i = 0; i < (int)(str->Length / sizeof(WCHAR)); i++) { |
||
154 | str->Buffer[i] = toupper(str->Buffer[i]); |
||
155 | } |
||
156 | |||
157 | return STATUS_SUCCESS; |
||
158 | } |
||
159 | |||
402 | daniel-mar | 160 | // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
161 | // Source: https://github.com/reactos/wine/blob/master/dlls/ntdll/rtlstr.c (renamed function) |
||
162 | // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
||
163 | |||
164 | NTSTATUS WINAPI _RtlCharToInteger( |
||
165 | PCSZ str, /* [I] '\0' terminated single-byte string containing a number */ |
||
166 | ULONG base, /* [I] Number base for conversion (allowed 0, 2, 8, 10 or 16) */ |
||
167 | ULONG* value) /* [O] Destination for the converted value */ |
||
168 | { |
||
169 | CHAR chCurrent; |
||
170 | int digit; |
||
171 | ULONG RunningTotal = 0; |
||
172 | char bMinus = 0; |
||
173 | |||
174 | while (*str != '\0' && *str <= ' ') { |
||
175 | str++; |
||
176 | } /* while */ |
||
177 | |||
178 | if (*str == '+') { |
||
179 | str++; |
||
180 | } |
||
181 | else if (*str == '-') { |
||
182 | bMinus = 1; |
||
183 | str++; |
||
184 | } /* if */ |
||
185 | |||
186 | if (base == 0) { |
||
187 | base = 10; |
||
188 | if (str[0] == '0') { |
||
189 | if (str[1] == 'b') { |
||
190 | str += 2; |
||
191 | base = 2; |
||
192 | } |
||
193 | else if (str[1] == 'o') { |
||
194 | str += 2; |
||
195 | base = 8; |
||
196 | } |
||
197 | else if (str[1] == 'x') { |
||
198 | str += 2; |
||
199 | base = 16; |
||
200 | } /* if */ |
||
201 | } /* if */ |
||
202 | } |
||
203 | else if (base != 2 && base != 8 && base != 10 && base != 16) { |
||
204 | return STATUS_INVALID_PARAMETER; |
||
205 | } /* if */ |
||
206 | |||
207 | if (value == NULL) { |
||
208 | return STATUS_ACCESS_VIOLATION; |
||
209 | } /* if */ |
||
210 | |||
211 | while (*str != '\0') { |
||
212 | chCurrent = *str; |
||
213 | if (chCurrent >= '0' && chCurrent <= '9') { |
||
214 | digit = chCurrent - '0'; |
||
215 | } |
||
216 | else if (chCurrent >= 'A' && chCurrent <= 'Z') { |
||
217 | digit = chCurrent - 'A' + 10; |
||
218 | } |
||
219 | else if (chCurrent >= 'a' && chCurrent <= 'z') { |
||
220 | digit = chCurrent - 'a' + 10; |
||
221 | } |
||
222 | else { |
||
223 | digit = -1; |
||
224 | } /* if */ |
||
225 | if (digit < 0 || (ULONG)digit >= base) { |
||
226 | *value = bMinus ? -(int)RunningTotal : RunningTotal; |
||
227 | return STATUS_SUCCESS; |
||
228 | } /* if */ |
||
229 | |||
230 | RunningTotal = RunningTotal * base + digit; |
||
231 | str++; |
||
232 | } /* while */ |
||
233 | |||
234 | *value = bMinus ? -(int)RunningTotal : RunningTotal; |
||
235 | return STATUS_SUCCESS; |
||
236 | } |
||
237 | |||
238 | // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
||
239 | // Source: https://raw.githubusercontent.com/wine-mirror/wine/master/include/wine/list.h |
||
240 | // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
||
241 | |||
242 | /* |
||
243 | * Linked lists support |
||
244 | * |
||
245 | * Copyright (C) 2002 Alexandre Julliard |
||
246 | * |
||
247 | * This library is free software; you can redistribute it and/or |
||
248 | * modify it under the terms of the GNU Lesser General Public |
||
249 | * License as published by the Free Software Foundation; either |
||
250 | * version 2.1 of the License, or (at your option) any later version. |
||
251 | * |
||
252 | * This library is distributed in the hope that it will be useful, |
||
253 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
254 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
255 | * Lesser General Public License for more details. |
||
256 | * |
||
257 | * You should have received a copy of the GNU Lesser General Public |
||
258 | * License along with this library; if not, write to the Free Software |
||
259 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
||
260 | */ |
||
261 | |||
262 | #ifndef __WINE_SERVER_LIST_H |
||
263 | #define __WINE_SERVER_LIST_H |
||
264 | |||
265 | #include <stddef.h> |
||
266 | |||
267 | struct list |
||
268 | { |
||
269 | struct list* next; |
||
270 | struct list* prev; |
||
271 | }; |
||
272 | |||
273 | /* Define a list like so: |
||
274 | * |
||
275 | * struct gadget |
||
276 | * { |
||
277 | * struct list entry; <-- doesn't have to be the first item in the struct |
||
278 | * int a, b; |
||
279 | * }; |
||
280 | * |
||
281 | * static struct list global_gadgets = LIST_INIT( global_gadgets ); |
||
282 | * |
||
283 | * or |
||
284 | * |
||
285 | * struct some_global_thing |
||
286 | * { |
||
287 | * struct list gadgets; |
||
288 | * }; |
||
289 | * |
||
290 | * list_init( &some_global_thing->gadgets ); |
||
291 | * |
||
292 | * Manipulate it like this: |
||
293 | * |
||
294 | * list_add_head( &global_gadgets, &new_gadget->entry ); |
||
295 | * list_remove( &new_gadget->entry ); |
||
296 | * list_add_after( &some_random_gadget->entry, &new_gadget->entry ); |
||
297 | * |
||
298 | * And to iterate over it: |
||
299 | * |
||
300 | * struct gadget *gadget; |
||
301 | * LIST_FOR_EACH_ENTRY( gadget, &global_gadgets, struct gadget, entry ) |
||
302 | * { |
||
303 | * ... |
||
304 | * } |
||
305 | * |
||
306 | */ |
||
307 | |||
308 | /* add an element after the specified one */ |
||
309 | static inline void list_add_after(struct list* elem, struct list* to_add) |
||
310 | { |
||
311 | to_add->next = elem->next; |
||
312 | to_add->prev = elem; |
||
313 | elem->next->prev = to_add; |
||
314 | elem->next = to_add; |
||
315 | } |
||
316 | |||
317 | /* add an element before the specified one */ |
||
318 | static inline void list_add_before(struct list* elem, struct list* to_add) |
||
319 | { |
||
320 | to_add->next = elem; |
||
321 | to_add->prev = elem->prev; |
||
322 | elem->prev->next = to_add; |
||
323 | elem->prev = to_add; |
||
324 | } |
||
325 | |||
326 | /* add element at the head of the list */ |
||
327 | static inline void list_add_head(struct list* list, struct list* elem) |
||
328 | { |
||
329 | list_add_after(list, elem); |
||
330 | } |
||
331 | |||
332 | /* add element at the tail of the list */ |
||
333 | static inline void list_add_tail(struct list* list, struct list* elem) |
||
334 | { |
||
335 | list_add_before(list, elem); |
||
336 | } |
||
337 | |||
338 | /* remove an element from its list */ |
||
339 | static inline void list_remove(struct list* elem) |
||
340 | { |
||
341 | elem->next->prev = elem->prev; |
||
342 | elem->prev->next = elem->next; |
||
343 | } |
||
344 | |||
345 | /* get the next element */ |
||
346 | static inline struct list* list_next(const struct list* list, const struct list* elem) |
||
347 | { |
||
348 | struct list* ret = elem->next; |
||
349 | if (elem->next == list) ret = NULL; |
||
350 | return ret; |
||
351 | } |
||
352 | |||
353 | /* get the previous element */ |
||
354 | static inline struct list* list_prev(const struct list* list, const struct list* elem) |
||
355 | { |
||
356 | struct list* ret = elem->prev; |
||
357 | if (elem->prev == list) ret = NULL; |
||
358 | return ret; |
||
359 | } |
||
360 | |||
361 | /* get the first element */ |
||
362 | static inline struct list* list_head(const struct list* list) |
||
363 | { |
||
364 | return list_next(list, list); |
||
365 | } |
||
366 | |||
367 | /* get the last element */ |
||
368 | static inline struct list* list_tail(const struct list* list) |
||
369 | { |
||
370 | return list_prev(list, list); |
||
371 | } |
||
372 | |||
373 | /* check if a list is empty */ |
||
374 | static inline int list_empty(const struct list* list) |
||
375 | { |
||
376 | return list->next == list; |
||
377 | } |
||
378 | |||
379 | /* initialize a list */ |
||
380 | static inline void list_init(struct list* list) |
||
381 | { |
||
382 | list->next = list->prev = list; |
||
383 | } |
||
384 | |||
385 | /* count the elements of a list */ |
||
386 | static inline unsigned int list_count(const struct list* list) |
||
387 | { |
||
388 | unsigned count = 0; |
||
389 | const struct list* ptr; |
||
390 | for (ptr = list->next; ptr != list; ptr = ptr->next) count++; |
||
391 | return count; |
||
392 | } |
||
393 | |||
394 | /* move all elements from src to the tail of dst */ |
||
395 | static inline void list_move_tail(struct list* dst, struct list* src) |
||
396 | { |
||
397 | if (list_empty(src)) return; |
||
398 | |||
399 | dst->prev->next = src->next; |
||
400 | src->next->prev = dst->prev; |
||
401 | dst->prev = src->prev; |
||
402 | src->prev->next = dst; |
||
403 | list_init(src); |
||
404 | } |
||
405 | |||
406 | /* move all elements from src to the head of dst */ |
||
407 | static inline void list_move_head(struct list* dst, struct list* src) |
||
408 | { |
||
409 | if (list_empty(src)) return; |
||
410 | |||
411 | dst->next->prev = src->prev; |
||
412 | src->prev->next = dst->next; |
||
413 | dst->next = src->next; |
||
414 | src->next->prev = dst; |
||
415 | list_init(src); |
||
416 | } |
||
417 | |||
418 | /* iterate through the list */ |
||
419 | #define LIST_FOR_EACH(cursor,list) \ |
||
420 | for ((cursor) = (list)->next; (cursor) != (list); (cursor) = (cursor)->next) |
||
421 | |||
422 | /* iterate through the list, with safety against removal */ |
||
423 | #define LIST_FOR_EACH_SAFE(cursor, cursor2, list) \ |
||
424 | for ((cursor) = (list)->next, (cursor2) = (cursor)->next; \ |
||
425 | (cursor) != (list); \ |
||
426 | (cursor) = (cursor2), (cursor2) = (cursor)->next) |
||
427 | |||
428 | /* iterate through the list using a list entry */ |
||
429 | #define LIST_FOR_EACH_ENTRY(elem, list, type, field) \ |
||
430 | for ((elem) = LIST_ENTRY((list)->next, type, field); \ |
||
431 | &(elem)->field != (list); \ |
||
432 | (elem) = LIST_ENTRY((elem)->field.next, type, field)) |
||
433 | |||
434 | /* iterate through the list using a list entry, with safety against removal */ |
||
435 | #define LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, list, type, field) \ |
||
436 | for ((cursor) = LIST_ENTRY((list)->next, type, field), \ |
||
437 | (cursor2) = LIST_ENTRY((cursor)->field.next, type, field); \ |
||
438 | &(cursor)->field != (list); \ |
||
439 | (cursor) = (cursor2), \ |
||
440 | (cursor2) = LIST_ENTRY((cursor)->field.next, type, field)) |
||
441 | |||
442 | /* iterate through the list in reverse order */ |
||
443 | #define LIST_FOR_EACH_REV(cursor,list) \ |
||
444 | for ((cursor) = (list)->prev; (cursor) != (list); (cursor) = (cursor)->prev) |
||
445 | |||
446 | /* iterate through the list in reverse order, with safety against removal */ |
||
447 | #define LIST_FOR_EACH_SAFE_REV(cursor, cursor2, list) \ |
||
448 | for ((cursor) = (list)->prev, (cursor2) = (cursor)->prev; \ |
||
449 | (cursor) != (list); \ |
||
450 | (cursor) = (cursor2), (cursor2) = (cursor)->prev) |
||
451 | |||
452 | /* iterate through the list in reverse order using a list entry */ |
||
453 | #define LIST_FOR_EACH_ENTRY_REV(elem, list, type, field) \ |
||
454 | for ((elem) = LIST_ENTRY((list)->prev, type, field); \ |
||
455 | &(elem)->field != (list); \ |
||
456 | (elem) = LIST_ENTRY((elem)->field.prev, type, field)) |
||
457 | |||
458 | /* iterate through the list in reverse order using a list entry, with safety against removal */ |
||
459 | #define LIST_FOR_EACH_ENTRY_SAFE_REV(cursor, cursor2, list, type, field) \ |
||
460 | for ((cursor) = LIST_ENTRY((list)->prev, type, field), \ |
||
461 | (cursor2) = LIST_ENTRY((cursor)->field.prev, type, field); \ |
||
462 | &(cursor)->field != (list); \ |
||
463 | (cursor) = (cursor2), \ |
||
464 | (cursor2) = LIST_ENTRY((cursor)->field.prev, type, field)) |
||
465 | |||
466 | /* macros for statically initialized lists */ |
||
467 | #undef LIST_INIT |
||
468 | #define LIST_INIT(list) { &(list), &(list) } |
||
469 | |||
470 | /* get pointer to object containing list element */ |
||
471 | #undef LIST_ENTRY |
||
472 | #define LIST_ENTRY(elem, type, field) \ |
||
473 | ((type *)((char *)(elem) - offsetof(type, field))) |
||
474 | |||
475 | #endif /* __WINE_SERVER_LIST_H */ |
||
476 | |||
477 | // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
||
478 | // Source: https://raw.githubusercontent.com/wine-mirror/wine/master/dlls/kernel32/kernel_private.h |
||
479 | // Modified |
||
480 | // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
||
481 | |||
482 | static inline BOOL set_ntstatus(NTSTATUS status) { |
||
483 | if (status) SetLastError(ERROR_INVALID_HANDLE); // ERROR_INVALID_HANDLE is just a dummy because RtlNtStatusToDosError(status) is not existing on Win9x |
||
484 | return !status; |
||
485 | } |
||
486 | |||
487 | // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
||
488 | // Source: https://github.com/wine-mirror/wine/blob/master/dlls/kernel32/resource.c ( Latest commit c5ff8ff on 17 Nov 2020 ) |
||
489 | // Modified by Daniel Marschall (16 Nov 2021, "Standalone" code, but only Begin/End/UpdateResources in ASCII. Also some small fixes) |
||
490 | // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
||
491 | |||
492 | /* |
||
493 | * Resources |
||
494 | * |
||
495 | * Copyright 1993 Robert J. Amstadt |
||
496 | * Copyright 1995, 2003 Alexandre Julliard |
||
497 | * Copyright 2006 Mike McCormack |
||
498 | * |
||
499 | * This library is free software; you can redistribute it and/or |
||
500 | * modify it under the terms of the GNU Lesser General Public |
||
501 | * License as published by the Free Software Foundation; either |
||
502 | * version 2.1 of the License, or (at your option) any later version. |
||
503 | * |
||
504 | * This library is distributed in the hope that it will be useful, |
||
505 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
506 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
507 | * Lesser General Public License for more details. |
||
508 | * |
||
509 | * You should have received a copy of the GNU Lesser General Public |
||
510 | * License along with this library; if not, write to the Free Software |
||
511 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
||
512 | */ |
||
513 | |||
405 | daniel-mar | 514 | /* retrieve the resource name to pass to the ntdll functions */ |
402 | daniel-mar | 515 | static NTSTATUS get_res_nameA(LPCSTR name, UNICODE_STRING* str) |
516 | { |
||
517 | if (IS_INTRESOURCE(name)) |
||
518 | { |
||
519 | str->Buffer = (PWSTR)ULongToPtr(LOWORD(name)); |
||
520 | return STATUS_SUCCESS; |
||
521 | } |
||
522 | else if (name[0] == '#') |
||
523 | { |
||
524 | ULONG value; |
||
525 | if (_RtlCharToInteger(name + 1, 10, &value) != STATUS_SUCCESS || HIWORD(value)) |
||
526 | return STATUS_INVALID_PARAMETER; |
||
527 | str->Buffer = (PWSTR)ULongToPtr(value); |
||
528 | return STATUS_SUCCESS; |
||
529 | } |
||
530 | else |
||
531 | { |
||
532 | _RtlCreateUnicodeStringFromAsciiz(str, name); |
||
406 | daniel-mar | 533 | |
534 | // Fix by Daniel Marschall: Added RtlUpcaseUnicodeString for get_res_nameA, like it was done for get_res_nameW |
||
418 | daniel-mar | 535 | // Reported in https://bugs.winehq.org/show_bug.cgi?id=52121 |
406 | daniel-mar | 536 | // RtlUpcaseUnicodeString(str, str, FALSE); |
537 | // For our implementation, this simple inplace function works: |
||
538 | _InplaceRtlUpcaseUnicodeString(str); |
||
539 | |||
402 | daniel-mar | 540 | return STATUS_SUCCESS; |
541 | } |
||
542 | } |
||
543 | |||
544 | /* |
||
545 | HRSRC WINAPI WineFindResourceExA(HMODULE module, LPCSTR type, LPCSTR name, WORD lang) |
||
546 | { |
||
547 | NTSTATUS status; |
||
548 | UNICODE_STRING nameW, typeW; |
||
549 | HRSRC ret = NULL; |
||
550 | |||
551 | //TRACE("%p %s %s %04x\n", module, debugstr_a(type), debugstr_a(name), lang); |
||
552 | |||
553 | if (!module) module = GetModuleHandleW(0); |
||
554 | nameW.Buffer = NULL; |
||
555 | typeW.Buffer = NULL; |
||
556 | |||
557 | //__TRY |
||
558 | //{ |
||
559 | if (!(status = get_res_nameA(name, &nameW)) && !(status = get_res_nameA(type, &typeW))) |
||
560 | ret = WineFindResourceExW(module, typeW.Buffer, nameW.Buffer, lang); |
||
561 | else |
||
562 | SetLastError(1); // RtlNtStatusToDosError(status) |
||
563 | //} |
||
564 | //__EXCEPT_PAGE_FAULT |
||
565 | //{ |
||
566 | // SetLastError(ERROR_INVALID_PARAMETER); |
||
567 | //} |
||
568 | //__ENDTRY |
||
569 | |||
570 | if (!IS_INTRESOURCE(nameW.Buffer)) HeapFree(GetProcessHeap(), 0, nameW.Buffer); |
||
571 | if (!IS_INTRESOURCE(typeW.Buffer)) HeapFree(GetProcessHeap(), 0, typeW.Buffer); |
||
572 | return ret; |
||
573 | } |
||
574 | |||
575 | HRSRC WINAPI WineFindResourceA(HMODULE hModule, LPCSTR name, LPCSTR type) |
||
576 | { |
||
577 | return WineFindResourceExA(hModule, type, name, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); |
||
578 | } |
||
579 | |||
580 | BOOL WINAPI WineEnumResourceTypesA(HMODULE hmod, ENUMRESTYPEPROCA lpfun, LONG_PTR lparam) |
||
581 | { |
||
582 | return WineEnumResourceTypesExA(hmod, lpfun, lparam, 0, 0); |
||
583 | } |
||
584 | |||
585 | BOOL WINAPI WineEnumResourceTypesW(HMODULE hmod, ENUMRESTYPEPROCW lpfun, LONG_PTR lparam) |
||
586 | { |
||
587 | return WineEnumResourceTypesExW(hmod, lpfun, lparam, 0, 0); |
||
588 | } |
||
589 | |||
590 | BOOL WINAPI WineEnumResourceNamesA(HMODULE hmod, LPCSTR type, ENUMRESNAMEPROCA lpfun, LONG_PTR lparam) |
||
591 | { |
||
592 | return WineEnumResourceNamesExA(hmod, type, lpfun, lparam, 0, 0); |
||
593 | } |
||
594 | |||
595 | BOOL WINAPI WineEnumResourceLanguagesA(HMODULE hmod, LPCSTR type, LPCSTR name, |
||
596 | ENUMRESLANGPROCA lpfun, LONG_PTR lparam) |
||
597 | { |
||
598 | return WineEnumResourceLanguagesExA(hmod, type, name, lpfun, lparam, 0, 0); |
||
599 | } |
||
600 | |||
601 | BOOL WINAPI WineEnumResourceLanguagesW(HMODULE hmod, LPCWSTR type, LPCWSTR name, |
||
602 | ENUMRESLANGPROCW lpfun, LONG_PTR lparam) |
||
603 | { |
||
604 | return WineEnumResourceLanguagesExW(hmod, type, name, lpfun, lparam, 0, 0); |
||
605 | } |
||
606 | */ |
||
607 | |||
608 | /* |
||
609 | * Data structure for updating resources. |
||
610 | * Type/Name/Language is a keyset for accessing resource data. |
||
611 | * |
||
612 | * QUEUEDUPDATES (root) -> |
||
613 | * list of struct resource_dir_entry (Type) -> |
||
614 | * list of struct resource_dir_entry (Name) -> |
||
615 | * list of struct resource_data Language + Data |
||
616 | */ |
||
617 | |||
618 | typedef struct |
||
619 | { |
||
620 | void* unknown[6]; |
||
621 | LPSTR pFileName; |
||
622 | BOOL bDeleteExistingResources; |
||
623 | struct list root; |
||
624 | } QUEUEDUPDATES; |
||
625 | |||
626 | /* this structure is shared for types and names */ |
||
627 | struct resource_dir_entry { |
||
628 | struct list entry; |
||
629 | LPWSTR id; |
||
630 | struct list children; |
||
631 | }; |
||
632 | |||
633 | /* this structure is the leaf */ |
||
634 | struct resource_data { |
||
635 | struct list entry; |
||
636 | LANGID lang; |
||
637 | DWORD codepage; |
||
638 | DWORD cbData; |
||
639 | void* lpData; |
||
640 | }; |
||
641 | |||
642 | static int resource_strcmp(LPCWSTR a, LPCWSTR b) |
||
643 | { |
||
644 | if (a == b) |
||
645 | return 0; |
||
646 | if (!IS_INTRESOURCE(a) && !IS_INTRESOURCE(b)) |
||
647 | return wcscmp(a, b); |
||
648 | /* strings come before ids */ |
||
649 | if (!IS_INTRESOURCE(a) && IS_INTRESOURCE(b)) |
||
650 | return -1; |
||
651 | if (!IS_INTRESOURCE(b) && IS_INTRESOURCE(a)) |
||
652 | return 1; |
||
653 | return (a < b) ? -1 : 1; |
||
654 | } |
||
655 | |||
656 | static struct resource_dir_entry* find_resource_dir_entry(struct list* dir, LPCWSTR id) |
||
657 | { |
||
658 | struct resource_dir_entry* ent; |
||
659 | |||
660 | /* match either IDs or strings */ |
||
661 | LIST_FOR_EACH_ENTRY(ent, dir, struct resource_dir_entry, entry) |
||
662 | if (!resource_strcmp(id, ent->id)) |
||
663 | return ent; |
||
664 | |||
665 | return NULL; |
||
666 | } |
||
667 | |||
668 | static struct resource_data* find_resource_data(struct list* dir, LANGID lang) |
||
669 | { |
||
670 | struct resource_data* res_data; |
||
671 | |||
672 | /* match only languages here */ |
||
673 | LIST_FOR_EACH_ENTRY(res_data, dir, struct resource_data, entry) |
||
674 | if (lang == res_data->lang) |
||
675 | return res_data; |
||
676 | |||
677 | return NULL; |
||
678 | } |
||
679 | |||
680 | static void add_resource_dir_entry(struct list* dir, struct resource_dir_entry* resdir) |
||
681 | { |
||
682 | struct resource_dir_entry* ent; |
||
683 | |||
684 | LIST_FOR_EACH_ENTRY(ent, dir, struct resource_dir_entry, entry) |
||
685 | { |
||
686 | if (0 > resource_strcmp(ent->id, resdir->id)) |
||
687 | continue; |
||
688 | |||
689 | list_add_before(&ent->entry, &resdir->entry); |
||
690 | return; |
||
691 | } |
||
692 | list_add_tail(dir, &resdir->entry); |
||
693 | } |
||
694 | |||
695 | static void add_resource_data_entry(struct list* dir, struct resource_data* resdata) |
||
696 | { |
||
697 | struct resource_data* ent; |
||
698 | |||
699 | LIST_FOR_EACH_ENTRY(ent, dir, struct resource_data, entry) |
||
700 | { |
||
701 | if (ent->lang < resdata->lang) |
||
702 | continue; |
||
703 | |||
704 | list_add_before(&ent->entry, &resdata->entry); |
||
705 | return; |
||
706 | } |
||
707 | list_add_tail(dir, &resdata->entry); |
||
708 | } |
||
709 | |||
710 | static LPWSTR res_strdupW(LPCWSTR str) |
||
711 | { |
||
712 | LPWSTR ret; |
||
713 | UINT len; |
||
714 | |||
715 | if (IS_INTRESOURCE(str)) |
||
716 | return (LPWSTR)(UINT_PTR)LOWORD(str); |
||
717 | len = (lstrlenW(str) + 1) * sizeof(WCHAR); |
||
718 | ret = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len); |
||
405 | daniel-mar | 719 | if (!ret) return NULL; // Added by Daniel Marschall |
402 | daniel-mar | 720 | memcpy(ret, str, len); |
721 | return ret; |
||
722 | } |
||
723 | |||
724 | static void res_free_str(LPWSTR str) |
||
725 | { |
||
726 | if (!IS_INTRESOURCE(str)) |
||
727 | HeapFree(GetProcessHeap(), 0, str); |
||
728 | } |
||
729 | |||
730 | static BOOL update_add_resource(QUEUEDUPDATES* updates, LPCWSTR Type, LPCWSTR Name, |
||
731 | LANGID Lang, struct resource_data* resdata, |
||
732 | BOOL overwrite_existing) |
||
733 | { |
||
734 | struct resource_dir_entry* restype, * resname; |
||
735 | struct resource_data* existing; |
||
736 | |||
405 | daniel-mar | 737 | //TRACE("%p %s %s %p %d\n", updates, |
738 | // debugstr_w(Type), debugstr_w(Name), resdata, overwrite_existing ); |
||
739 | |||
402 | daniel-mar | 740 | restype = find_resource_dir_entry(&updates->root, Type); |
741 | if (!restype) |
||
742 | { |
||
743 | restype = (struct resource_dir_entry*)HeapAlloc(GetProcessHeap(), 0, sizeof(struct resource_dir_entry)); |
||
744 | if (!restype) return FALSE; |
||
745 | restype->id = res_strdupW(Type); |
||
746 | list_init(&restype->children); |
||
747 | add_resource_dir_entry(&updates->root, restype); |
||
748 | } |
||
749 | |||
750 | resname = find_resource_dir_entry(&restype->children, Name); |
||
751 | if (!resname) |
||
752 | { |
||
753 | resname = (struct resource_dir_entry*)HeapAlloc(GetProcessHeap(), 0, sizeof(struct resource_dir_entry)); |
||
754 | if (!resname) return FALSE; |
||
755 | resname->id = res_strdupW(Name); |
||
756 | list_init(&resname->children); |
||
757 | add_resource_dir_entry(&restype->children, resname); |
||
758 | } |
||
759 | |||
760 | /* |
||
761 | * If there's an existing resource entry with matching (Type,Name,Language) |
||
762 | * it needs to be removed before adding the new data. |
||
763 | */ |
||
764 | existing = find_resource_data(&resname->children, Lang); |
||
765 | if (existing) |
||
766 | { |
||
767 | if (!overwrite_existing) |
||
768 | return FALSE; |
||
769 | list_remove(&existing->entry); |
||
770 | HeapFree(GetProcessHeap(), 0, existing); |
||
771 | } |
||
772 | |||
773 | if (resdata) |
||
774 | add_resource_data_entry(&resname->children, resdata); |
||
775 | |||
776 | return TRUE; |
||
777 | } |
||
778 | |||
779 | static struct resource_data* allocate_resource_data(WORD Language, DWORD codepage, |
||
780 | LPVOID lpData, DWORD cbData, BOOL copy_data) |
||
781 | { |
||
782 | struct resource_data* resdata; |
||
783 | |||
784 | if (!lpData || !cbData) |
||
785 | return NULL; |
||
786 | |||
787 | resdata = (struct resource_data*)HeapAlloc(GetProcessHeap(), 0, sizeof * resdata + (copy_data ? cbData : 0)); |
||
788 | if (resdata) |
||
789 | { |
||
790 | resdata->lang = Language; |
||
791 | resdata->codepage = codepage; |
||
792 | resdata->cbData = cbData; |
||
793 | if (copy_data) |
||
794 | { |
||
795 | resdata->lpData = &resdata[1]; |
||
796 | memcpy(resdata->lpData, lpData, cbData); |
||
797 | } |
||
798 | else |
||
799 | resdata->lpData = lpData; |
||
800 | } |
||
801 | |||
802 | return resdata; |
||
803 | } |
||
804 | |||
805 | static void free_resource_directory(struct list* head, int level) |
||
806 | { |
||
807 | struct list* ptr = NULL; |
||
808 | |||
809 | while ((ptr = list_head(head))) |
||
810 | { |
||
811 | list_remove(ptr); |
||
812 | if (level) |
||
813 | { |
||
814 | struct resource_dir_entry* ent; |
||
815 | |||
816 | ent = LIST_ENTRY(ptr, struct resource_dir_entry, entry); |
||
817 | res_free_str(ent->id); |
||
818 | free_resource_directory(&ent->children, level - 1); |
||
819 | HeapFree(GetProcessHeap(), 0, ent); |
||
820 | } |
||
821 | else |
||
822 | { |
||
823 | struct resource_data* data; |
||
824 | |||
825 | data = LIST_ENTRY(ptr, struct resource_data, entry); |
||
826 | HeapFree(GetProcessHeap(), 0, data); |
||
827 | } |
||
828 | } |
||
829 | } |
||
830 | |||
831 | static IMAGE_NT_HEADERS* get_nt_header(void* base, DWORD mapping_size) |
||
832 | { |
||
833 | IMAGE_NT_HEADERS* nt; |
||
834 | IMAGE_DOS_HEADER* dos; |
||
835 | |||
836 | if (mapping_size < sizeof(*dos)) |
||
837 | return NULL; |
||
838 | |||
839 | dos = (IMAGE_DOS_HEADER*)base; |
||
840 | if (dos->e_magic != IMAGE_DOS_SIGNATURE) |
||
841 | return NULL; |
||
842 | |||
843 | if ((dos->e_lfanew + sizeof(*nt)) > mapping_size) |
||
844 | return NULL; |
||
845 | |||
846 | nt = (IMAGE_NT_HEADERS*)((BYTE*)base + dos->e_lfanew); |
||
847 | |||
848 | if (nt->Signature != IMAGE_NT_SIGNATURE) |
||
849 | return NULL; |
||
850 | |||
851 | return nt; |
||
852 | } |
||
853 | |||
854 | static IMAGE_SECTION_HEADER* get_section_header(void* base, DWORD mapping_size, DWORD* num_sections) |
||
855 | { |
||
856 | IMAGE_NT_HEADERS* nt; |
||
857 | DWORD section_ofs; |
||
858 | |||
859 | nt = get_nt_header(base, mapping_size); |
||
860 | if (!nt) |
||
861 | return NULL; |
||
862 | |||
863 | /* check that we don't go over the end of the file accessing the sections */ |
||
864 | section_ofs = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + nt->FileHeader.SizeOfOptionalHeader; |
||
865 | if ((nt->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER) + section_ofs) > mapping_size) |
||
866 | return NULL; |
||
867 | |||
868 | if (num_sections) |
||
869 | *num_sections = nt->FileHeader.NumberOfSections; |
||
870 | |||
871 | /* from here we have a valid PE exe to update */ |
||
872 | return (IMAGE_SECTION_HEADER*)((BYTE*)nt + section_ofs); |
||
873 | } |
||
874 | |||
875 | static BOOL check_pe_exe(HANDLE file, QUEUEDUPDATES* updates) |
||
876 | { |
||
877 | const IMAGE_NT_HEADERS32* nt; |
||
878 | //const IMAGE_NT_HEADERS64* nt64; |
||
879 | const IMAGE_SECTION_HEADER* sec; |
||
880 | //const IMAGE_DATA_DIRECTORY* dd; |
||
881 | BOOL ret = FALSE; |
||
882 | HANDLE mapping; |
||
883 | DWORD mapping_size, num_sections = 0; |
||
884 | void* base = NULL; |
||
885 | |||
433 | daniel-mar | 886 | UNREFERENCED_PARAMETER(updates); |
887 | |||
402 | daniel-mar | 888 | mapping_size = GetFileSize(file, NULL); |
889 | |||
890 | mapping = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL); |
||
891 | if (!mapping) |
||
892 | goto done; |
||
893 | |||
894 | base = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, mapping_size); |
||
895 | if (!base) |
||
896 | goto done; |
||
897 | |||
898 | nt = (IMAGE_NT_HEADERS32*)get_nt_header(base, mapping_size); |
||
899 | if (!nt) |
||
900 | goto done; |
||
901 | |||
405 | daniel-mar | 902 | //Fix by Daniel Marschall: Removed, because the variables are not used! |
402 | daniel-mar | 903 | //nt64 = (IMAGE_NT_HEADERS64*)nt; |
904 | //dd = &nt->OptionalHeader.DataDirectory[0]; |
||
905 | //if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) |
||
906 | // dd = &nt64->OptionalHeader.DataDirectory[0]; |
||
405 | daniel-mar | 907 | //TRACE("resources: %08x %08x\n", |
908 | // dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress, |
||
909 | // dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size); |
||
402 | daniel-mar | 910 | |
911 | sec = get_section_header(base, mapping_size, &num_sections); |
||
912 | if (!sec) |
||
913 | goto done; |
||
914 | |||
915 | ret = TRUE; |
||
916 | |||
917 | done: |
||
918 | if (base) |
||
919 | UnmapViewOfFile(base); |
||
920 | if (mapping) |
||
921 | CloseHandle(mapping); |
||
922 | |||
923 | return ret; |
||
924 | } |
||
925 | |||
926 | struct resource_size_info { |
||
927 | DWORD types_ofs; |
||
928 | DWORD names_ofs; |
||
929 | DWORD langs_ofs; |
||
930 | DWORD data_entry_ofs; |
||
931 | DWORD strings_ofs; |
||
932 | DWORD data_ofs; |
||
933 | DWORD total_size; |
||
934 | }; |
||
935 | |||
936 | struct mapping_info { |
||
937 | HANDLE file; |
||
938 | void* base; |
||
939 | DWORD size; |
||
940 | BOOL read_write; |
||
941 | }; |
||
942 | |||
943 | static const IMAGE_SECTION_HEADER* section_from_rva(void* base, DWORD mapping_size, DWORD rva) |
||
944 | { |
||
945 | const IMAGE_SECTION_HEADER* sec; |
||
946 | DWORD num_sections = 0; |
||
947 | int i; |
||
948 | |||
949 | sec = get_section_header(base, mapping_size, &num_sections); |
||
950 | if (!sec) |
||
951 | return NULL; |
||
952 | |||
953 | for (i = num_sections - 1; i >= 0; i--) |
||
954 | { |
||
955 | if (sec[i].VirtualAddress <= rva && |
||
956 | rva <= (DWORD)sec[i].VirtualAddress + sec[i].SizeOfRawData) |
||
957 | { |
||
958 | return &sec[i]; |
||
959 | } |
||
960 | } |
||
961 | |||
962 | return NULL; |
||
963 | } |
||
964 | |||
965 | static void* address_from_rva(void* base, DWORD mapping_size, DWORD rva, DWORD len) |
||
966 | { |
||
967 | const IMAGE_SECTION_HEADER* sec; |
||
968 | |||
969 | sec = section_from_rva(base, mapping_size, rva); |
||
970 | if (!sec) |
||
971 | return NULL; |
||
972 | |||
973 | if (rva + len <= (DWORD)sec->VirtualAddress + sec->SizeOfRawData) |
||
974 | return (void*)((LPBYTE)base + (sec->PointerToRawData + rva - sec->VirtualAddress)); |
||
975 | |||
976 | return NULL; |
||
977 | } |
||
978 | |||
979 | static LPWSTR resource_dup_string(const IMAGE_RESOURCE_DIRECTORY* root, const MyIMAGE_RESOURCE_DIRECTORY_ENTRY* entry) |
||
980 | { |
||
981 | const IMAGE_RESOURCE_DIR_STRING_U* string; |
||
982 | LPWSTR s; |
||
983 | |||
984 | if (!entry->u.s.NameIsString) |
||
985 | return (LPWSTR)UIntToPtr(entry->u.Id); |
||
986 | |||
987 | string = (const IMAGE_RESOURCE_DIR_STRING_U*)(((const char*)root) + entry->u.s.NameOffset); |
||
988 | s = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (string->Length + 1) * sizeof(WCHAR)); |
||
989 | if (!s) return NULL; |
||
990 | memcpy(s, string->NameString, (string->Length + 1) * sizeof(WCHAR)); |
||
991 | s[string->Length] = 0; |
||
992 | |||
993 | return s; |
||
994 | } |
||
995 | |||
405 | daniel-mar | 996 | /* this function is based on the code in winedump's pe.c */ |
402 | daniel-mar | 997 | static BOOL enumerate_mapped_resources(QUEUEDUPDATES* updates, |
998 | void* base, DWORD mapping_size, |
||
999 | const IMAGE_RESOURCE_DIRECTORY* root) |
||
1000 | { |
||
1001 | const IMAGE_RESOURCE_DIRECTORY* namedir, * langdir; |
||
1002 | const MyIMAGE_RESOURCE_DIRECTORY_ENTRY* e1, * e2, * e3; |
||
1003 | const IMAGE_RESOURCE_DATA_ENTRY* data; |
||
1004 | DWORD i, j, k; |
||
1005 | |||
405 | daniel-mar | 1006 | //TRACE("version (%d.%d) %d named %d id entries\n", |
1007 | // root->MajorVersion, root->MinorVersion, root->NumberOfNamedEntries, root->NumberOfIdEntries); |
||
1008 | |||
402 | daniel-mar | 1009 | for (i = 0; i < (DWORD)root->NumberOfNamedEntries + (DWORD)root->NumberOfIdEntries; i++) |
1010 | { |
||
1011 | LPWSTR Type; |
||
1012 | |||
1013 | e1 = (const MyIMAGE_RESOURCE_DIRECTORY_ENTRY*)(root + 1) + i; |
||
1014 | |||
1015 | Type = resource_dup_string(root, e1); |
||
1016 | |||
1017 | namedir = (const IMAGE_RESOURCE_DIRECTORY*)((const char*)root + e1->u2.s2.OffsetToDirectory); |
||
1018 | for (j = 0; j < (DWORD)namedir->NumberOfNamedEntries + (DWORD)namedir->NumberOfIdEntries; j++) |
||
1019 | { |
||
1020 | LPWSTR Name; |
||
1021 | |||
1022 | e2 = (const MyIMAGE_RESOURCE_DIRECTORY_ENTRY*)(namedir + 1) + j; |
||
1023 | |||
1024 | Name = resource_dup_string(root, e2); |
||
1025 | |||
1026 | langdir = (const IMAGE_RESOURCE_DIRECTORY*)((const char*)root + e2->u2.s2.OffsetToDirectory); |
||
1027 | for (k = 0; k < (DWORD)langdir->NumberOfNamedEntries + (DWORD)langdir->NumberOfIdEntries; k++) |
||
1028 | { |
||
1029 | LANGID Lang; |
||
1030 | void* p; |
||
1031 | struct resource_data* resdata; |
||
1032 | |||
1033 | e3 = (const MyIMAGE_RESOURCE_DIRECTORY_ENTRY*)(langdir + 1) + k; |
||
1034 | |||
1035 | Lang = e3->u.Id; |
||
1036 | |||
1037 | data = (const IMAGE_RESOURCE_DATA_ENTRY*)((const char*)root + e3->u2.OffsetToData); |
||
1038 | |||
1039 | p = address_from_rva(base, mapping_size, data->OffsetToData, data->Size); |
||
1040 | |||
1041 | resdata = allocate_resource_data(Lang, data->CodePage, p, data->Size, FALSE); |
||
1042 | if (resdata) |
||
1043 | { |
||
1044 | if (!update_add_resource(updates, Type, Name, Lang, resdata, FALSE)) |
||
1045 | HeapFree(GetProcessHeap(), 0, resdata); |
||
1046 | } |
||
1047 | } |
||
1048 | res_free_str(Name); |
||
1049 | } |
||
1050 | res_free_str(Type); |
||
1051 | } |
||
1052 | |||
1053 | return TRUE; |
||
1054 | } |
||
1055 | |||
1056 | static BOOL read_mapped_resources(QUEUEDUPDATES* updates, void* base, DWORD mapping_size) |
||
1057 | { |
||
1058 | const IMAGE_RESOURCE_DIRECTORY* root; |
||
1059 | const IMAGE_NT_HEADERS* nt; |
||
1060 | const IMAGE_SECTION_HEADER* sec; |
||
1061 | DWORD num_sections = 0, i; |
||
1062 | |||
1063 | nt = get_nt_header(base, mapping_size); |
||
1064 | if (!nt) |
||
1065 | return FALSE; |
||
1066 | |||
1067 | sec = get_section_header(base, mapping_size, &num_sections); |
||
1068 | if (!sec) |
||
1069 | return FALSE; |
||
1070 | |||
1071 | for (i = 0; i < num_sections; i++) |
||
1072 | if (!memcmp(sec[i].Name, ".rsrc", 6)) |
||
1073 | break; |
||
1074 | |||
1075 | if (i == num_sections) |
||
1076 | return TRUE; |
||
1077 | |||
1078 | /* check the resource data is inside the mapping */ |
||
1079 | if (sec[i].PointerToRawData > mapping_size || |
||
1080 | (sec[i].PointerToRawData + sec[i].SizeOfRawData) > mapping_size) |
||
1081 | return TRUE; |
||
1082 | |||
405 | daniel-mar | 1083 | //TRACE("found .rsrc at %08x, size %08x\n", sec[i].PointerToRawData, sec[i].SizeOfRawData); |
1084 | |||
402 | daniel-mar | 1085 | if (!sec[i].PointerToRawData || sec[i].SizeOfRawData < sizeof(IMAGE_RESOURCE_DIRECTORY)) |
1086 | return TRUE; |
||
1087 | |||
1088 | root = (IMAGE_RESOURCE_DIRECTORY*)((BYTE*)base + sec[i].PointerToRawData); |
||
1089 | enumerate_mapped_resources(updates, base, mapping_size, root); |
||
1090 | |||
1091 | return TRUE; |
||
1092 | } |
||
1093 | |||
1094 | static BOOL map_file_into_memory(struct mapping_info* mi) |
||
1095 | { |
||
1096 | DWORD page_attr, perm; |
||
1097 | HANDLE mapping; |
||
1098 | |||
1099 | if (mi->read_write) |
||
1100 | { |
||
1101 | page_attr = PAGE_READWRITE; |
||
1102 | perm = FILE_MAP_WRITE | FILE_MAP_READ; |
||
1103 | } |
||
1104 | else |
||
1105 | { |
||
1106 | page_attr = PAGE_READONLY; |
||
1107 | perm = FILE_MAP_READ; |
||
1108 | } |
||
1109 | |||
1110 | mapping = CreateFileMappingA(mi->file, NULL, page_attr, 0, 0, NULL); |
||
1111 | if (!mapping) return FALSE; |
||
1112 | |||
1113 | mi->base = MapViewOfFile(mapping, perm, 0, 0, mi->size); |
||
1114 | CloseHandle(mapping); |
||
1115 | |||
1116 | return mi->base != NULL; |
||
1117 | } |
||
1118 | |||
1119 | static BOOL unmap_file_from_memory(struct mapping_info* mi) |
||
1120 | { |
||
1121 | if (mi->base) |
||
1122 | UnmapViewOfFile(mi->base); |
||
1123 | mi->base = NULL; |
||
1124 | return TRUE; |
||
1125 | } |
||
1126 | |||
1127 | static void destroy_mapping(struct mapping_info* mi) |
||
1128 | { |
||
1129 | if (!mi) |
||
1130 | return; |
||
1131 | unmap_file_from_memory(mi); |
||
1132 | if (mi->file) |
||
1133 | CloseHandle(mi->file); |
||
1134 | HeapFree(GetProcessHeap(), 0, mi); |
||
1135 | } |
||
1136 | |||
1137 | static struct mapping_info* create_mapping(LPCSTR filename, BOOL rw) |
||
1138 | { |
||
1139 | struct mapping_info* mi; |
||
1140 | |||
405 | daniel-mar | 1141 | // TODO: Which Windows version supports HEAP_ZERO_MEMORY? Can we safely use it, or is memset() safer? |
402 | daniel-mar | 1142 | mi = (struct mapping_info*)HeapAlloc(GetProcessHeap(), 0/*HEAP_ZERO_MEMORY*/, sizeof * mi); |
1143 | if (!mi) { |
||
1144 | return NULL; |
||
1145 | } |
||
1146 | memset(mi, 0, sizeof * mi); |
||
1147 | |||
1148 | mi->read_write = rw; |
||
1149 | |||
405 | daniel-mar | 1150 | // Fix by Daniel Marschall: Changed "0" to "FILE_SHARE_READ | (rw ? FILE_SHARE_WRITE : 0)" |
418 | daniel-mar | 1151 | // Reported in https://bugs.winehq.org/show_bug.cgi?id=52121 |
402 | daniel-mar | 1152 | mi->file = CreateFileA(filename, GENERIC_READ | (rw ? GENERIC_WRITE : 0), |
1153 | FILE_SHARE_READ | (rw ? FILE_SHARE_WRITE : 0), NULL, OPEN_EXISTING, 0, 0); |
||
1154 | |||
1155 | if (mi->file != INVALID_HANDLE_VALUE) |
||
1156 | { |
||
1157 | mi->size = GetFileSize(mi->file, NULL); |
||
1158 | |||
405 | daniel-mar | 1159 | if (map_file_into_memory(mi)) |
402 | daniel-mar | 1160 | return mi; |
1161 | } |
||
1162 | destroy_mapping(mi); |
||
1163 | return NULL; |
||
1164 | } |
||
1165 | |||
1166 | static BOOL resize_mapping(struct mapping_info* mi, DWORD new_size) |
||
1167 | { |
||
1168 | if (!unmap_file_from_memory(mi)) |
||
1169 | return FALSE; |
||
1170 | |||
1171 | /* change the file size */ |
||
1172 | SetFilePointer(mi->file, new_size, NULL, FILE_BEGIN); |
||
1173 | if (!SetEndOfFile(mi->file)) |
||
1174 | { |
||
1175 | //ERR("failed to set file size to %08x\n", new_size); |
||
1176 | return FALSE; |
||
1177 | } |
||
1178 | |||
1179 | mi->size = new_size; |
||
1180 | |||
1181 | return map_file_into_memory(mi); |
||
1182 | } |
||
1183 | |||
1184 | static void get_resource_sizes(QUEUEDUPDATES* updates, struct resource_size_info* si) |
||
1185 | { |
||
1186 | struct resource_dir_entry* types, * names; |
||
1187 | struct resource_data* data; |
||
1188 | DWORD num_types = 0, num_names = 0, num_langs = 0, strings_size = 0, data_size = 0; |
||
1189 | |||
1190 | memset(si, 0, sizeof * si); |
||
1191 | |||
1192 | LIST_FOR_EACH_ENTRY(types, &updates->root, struct resource_dir_entry, entry) |
||
1193 | { |
||
1194 | num_types++; |
||
1195 | if (!IS_INTRESOURCE(types->id)) |
||
1196 | strings_size += sizeof(WORD) + lstrlenW(types->id) * sizeof(WCHAR); |
||
1197 | |||
1198 | LIST_FOR_EACH_ENTRY(names, &types->children, struct resource_dir_entry, entry) |
||
1199 | { |
||
1200 | num_names++; |
||
1201 | |||
1202 | if (!IS_INTRESOURCE(names->id)) |
||
1203 | strings_size += sizeof(WORD) + lstrlenW(names->id) * sizeof(WCHAR); |
||
1204 | |||
1205 | LIST_FOR_EACH_ENTRY(data, &names->children, struct resource_data, entry) |
||
1206 | { |
||
1207 | num_langs++; |
||
1208 | data_size += (data->cbData + 3) & ~3; |
||
1209 | } |
||
1210 | } |
||
1211 | } |
||
1212 | |||
1213 | /* names are at the end of the types */ |
||
1214 | si->names_ofs = sizeof(IMAGE_RESOURCE_DIRECTORY) + |
||
1215 | num_types * sizeof(MyIMAGE_RESOURCE_DIRECTORY_ENTRY); |
||
1216 | |||
1217 | /* language directories are at the end of the names */ |
||
1218 | si->langs_ofs = si->names_ofs + |
||
1219 | num_types * sizeof(IMAGE_RESOURCE_DIRECTORY) + |
||
1220 | num_names * sizeof(MyIMAGE_RESOURCE_DIRECTORY_ENTRY); |
||
1221 | |||
1222 | si->data_entry_ofs = si->langs_ofs + |
||
1223 | num_names * sizeof(IMAGE_RESOURCE_DIRECTORY) + |
||
1224 | num_langs * sizeof(MyIMAGE_RESOURCE_DIRECTORY_ENTRY); |
||
1225 | |||
1226 | si->strings_ofs = si->data_entry_ofs + |
||
1227 | num_langs * sizeof(IMAGE_RESOURCE_DATA_ENTRY); |
||
1228 | |||
1229 | si->data_ofs = si->strings_ofs + ((strings_size + 3) & ~3); |
||
1230 | |||
1231 | si->total_size = si->data_ofs + data_size; |
||
405 | daniel-mar | 1232 | |
1233 | //TRACE("names %08x langs %08x data entries %08x strings %08x data %08x total %08x\n", |
||
1234 | // si->names_ofs, si->langs_ofs, si->data_entry_ofs, |
||
1235 | // si->strings_ofs, si->data_ofs, si->total_size); |
||
402 | daniel-mar | 1236 | } |
1237 | |||
1238 | static void res_write_padding(BYTE* res_base, DWORD size) |
||
1239 | { |
||
1240 | static const BYTE pad[] = { |
||
1241 | 'P','A','D','D','I','N','G','X','X','P','A','D','D','I','N','G' }; |
||
1242 | DWORD i; |
||
1243 | |||
1244 | for (i = 0; i < size / sizeof pad; i++) |
||
1245 | memcpy(&res_base[i * sizeof pad], pad, sizeof pad); |
||
1246 | memcpy(&res_base[i * sizeof pad], pad, size % sizeof pad); |
||
1247 | } |
||
1248 | |||
1249 | static BOOL write_resources(QUEUEDUPDATES* updates, LPBYTE base, struct resource_size_info* si, DWORD rva) |
||
1250 | { |
||
1251 | struct resource_dir_entry* types, * names; |
||
1252 | struct resource_data* data; |
||
1253 | IMAGE_RESOURCE_DIRECTORY* root; |
||
1254 | |||
405 | daniel-mar | 1255 | //TRACE("%p %p %p %08x\n", updates, base, si, rva ); |
1256 | |||
402 | daniel-mar | 1257 | memset(base, 0, si->total_size); |
1258 | |||
1259 | /* the root entry always exists */ |
||
1260 | root = (IMAGE_RESOURCE_DIRECTORY*)base; |
||
1261 | memset(root, 0, sizeof * root); |
||
1262 | root->MajorVersion = 4; |
||
1263 | si->types_ofs = sizeof * root; |
||
1264 | LIST_FOR_EACH_ENTRY(types, &updates->root, struct resource_dir_entry, entry) |
||
1265 | { |
||
1266 | MyIMAGE_RESOURCE_DIRECTORY_ENTRY* e1; |
||
1267 | IMAGE_RESOURCE_DIRECTORY* namedir; |
||
1268 | |||
1269 | e1 = (MyIMAGE_RESOURCE_DIRECTORY_ENTRY*)&base[si->types_ofs]; |
||
1270 | memset(e1, 0, sizeof * e1); |
||
1271 | if (!IS_INTRESOURCE(types->id)) |
||
1272 | { |
||
1273 | WCHAR* strings; |
||
1274 | DWORD len; |
||
1275 | |||
1276 | root->NumberOfNamedEntries++; |
||
1277 | e1->u.s.NameIsString = 1; |
||
1278 | e1->u.s.NameOffset = si->strings_ofs; |
||
1279 | |||
1280 | strings = (WCHAR*)&base[si->strings_ofs]; |
||
1281 | len = lstrlenW(types->id); |
||
1282 | strings[0] = (WCHAR)len; |
||
1283 | memcpy(&strings[1], types->id, len * sizeof(WCHAR)); |
||
1284 | si->strings_ofs += (len + 1) * sizeof(WCHAR); |
||
1285 | } |
||
1286 | else |
||
1287 | { |
||
1288 | root->NumberOfIdEntries++; |
||
1289 | e1->u.Id = LOWORD(types->id); |
||
1290 | } |
||
1291 | e1->u2.s2.OffsetToDirectory = si->names_ofs; |
||
1292 | e1->u2.s2.DataIsDirectory = TRUE; |
||
1293 | si->types_ofs += sizeof(MyIMAGE_RESOURCE_DIRECTORY_ENTRY); |
||
1294 | |||
1295 | namedir = (IMAGE_RESOURCE_DIRECTORY*)&base[si->names_ofs]; |
||
1296 | memset(namedir, 0, sizeof * namedir); |
||
1297 | namedir->MajorVersion = 4; |
||
1298 | si->names_ofs += sizeof(IMAGE_RESOURCE_DIRECTORY); |
||
1299 | |||
1300 | LIST_FOR_EACH_ENTRY(names, &types->children, struct resource_dir_entry, entry) |
||
1301 | { |
||
1302 | MyIMAGE_RESOURCE_DIRECTORY_ENTRY* e2; |
||
1303 | IMAGE_RESOURCE_DIRECTORY* langdir; |
||
1304 | |||
1305 | e2 = (MyIMAGE_RESOURCE_DIRECTORY_ENTRY*)&base[si->names_ofs]; |
||
1306 | memset(e2, 0, sizeof * e2); |
||
1307 | if (!IS_INTRESOURCE(names->id)) |
||
1308 | { |
||
1309 | WCHAR* strings; |
||
1310 | DWORD len; |
||
1311 | |||
1312 | namedir->NumberOfNamedEntries++; |
||
1313 | e2->u.s.NameIsString = 1; |
||
1314 | e2->u.s.NameOffset = si->strings_ofs; |
||
1315 | |||
1316 | strings = (WCHAR*)&base[si->strings_ofs]; |
||
1317 | len = lstrlenW(names->id); |
||
1318 | strings[0] = (WCHAR)len; |
||
1319 | memcpy(&strings[1], names->id, len * sizeof(WCHAR)); |
||
1320 | si->strings_ofs += (len + 1) * sizeof(WCHAR); |
||
1321 | } |
||
1322 | else |
||
1323 | { |
||
1324 | namedir->NumberOfIdEntries++; |
||
1325 | e2->u.Id = LOWORD(names->id); |
||
1326 | } |
||
1327 | e2->u2.s2.OffsetToDirectory = si->langs_ofs; |
||
1328 | e2->u2.s2.DataIsDirectory = TRUE; |
||
1329 | si->names_ofs += sizeof(MyIMAGE_RESOURCE_DIRECTORY_ENTRY); |
||
1330 | |||
1331 | langdir = (IMAGE_RESOURCE_DIRECTORY*)&base[si->langs_ofs]; |
||
1332 | memset(langdir, 0, sizeof * langdir); |
||
1333 | langdir->MajorVersion = 4; |
||
1334 | si->langs_ofs += sizeof(IMAGE_RESOURCE_DIRECTORY); |
||
1335 | |||
1336 | LIST_FOR_EACH_ENTRY(data, &names->children, struct resource_data, entry) |
||
1337 | { |
||
1338 | MyIMAGE_RESOURCE_DIRECTORY_ENTRY* e3; |
||
1339 | IMAGE_RESOURCE_DATA_ENTRY* de; |
||
1340 | int pad_size; |
||
1341 | |||
1342 | e3 = (MyIMAGE_RESOURCE_DIRECTORY_ENTRY*)&base[si->langs_ofs]; |
||
1343 | memset(e3, 0, sizeof * e3); |
||
1344 | langdir->NumberOfIdEntries++; |
||
1345 | e3->u.Id = LOWORD(data->lang); |
||
1346 | e3->u2.OffsetToData = si->data_entry_ofs; |
||
1347 | |||
1348 | si->langs_ofs += sizeof(MyIMAGE_RESOURCE_DIRECTORY_ENTRY); |
||
1349 | |||
1350 | /* write out all the data entries */ |
||
1351 | de = (IMAGE_RESOURCE_DATA_ENTRY*)&base[si->data_entry_ofs]; |
||
1352 | memset(de, 0, sizeof * de); |
||
1353 | de->OffsetToData = si->data_ofs + rva; |
||
1354 | de->Size = data->cbData; |
||
1355 | de->CodePage = data->codepage; |
||
1356 | si->data_entry_ofs += sizeof(IMAGE_RESOURCE_DATA_ENTRY); |
||
1357 | |||
1358 | /* write out the resource data */ |
||
1359 | memcpy(&base[si->data_ofs], data->lpData, data->cbData); |
||
1360 | si->data_ofs += data->cbData; |
||
1361 | |||
1362 | pad_size = (-(int)si->data_ofs) & 3; |
||
1363 | res_write_padding(&base[si->data_ofs], pad_size); |
||
1364 | si->data_ofs += pad_size; |
||
1365 | } |
||
1366 | } |
||
1367 | } |
||
1368 | |||
1369 | return TRUE; |
||
1370 | } |
||
1371 | |||
405 | daniel-mar | 1372 | /* |
1373 | * FIXME: |
||
1374 | * Assumes that the resources are in .rsrc |
||
1375 | * and .rsrc is the last section in the file. |
||
1376 | * Not sure whether updating resources will other cases on Windows. |
||
1377 | * If the resources lie in a section containing other data, |
||
1378 | * resizing that section could possibly cause trouble. |
||
1379 | * If the section with the resources isn't last, the remaining |
||
1380 | * sections need to be moved down in the file, and the section header |
||
1381 | * would need to be adjusted. |
||
1382 | * If we needed to add a section, what would we name it? |
||
1383 | * If we needed to add a section and there wasn't space in the file |
||
1384 | * header, how would that work? |
||
1385 | * Seems that at least some of these cases can't be handled properly. |
||
1386 | */ |
||
402 | daniel-mar | 1387 | static IMAGE_SECTION_HEADER* get_resource_section(void* base, DWORD mapping_size) |
1388 | { |
||
1389 | IMAGE_SECTION_HEADER* sec; |
||
1390 | IMAGE_NT_HEADERS* nt; |
||
1391 | DWORD i, num_sections = 0; |
||
1392 | |||
1393 | nt = get_nt_header(base, mapping_size); |
||
1394 | if (!nt) |
||
1395 | return NULL; |
||
1396 | |||
1397 | sec = get_section_header(base, mapping_size, &num_sections); |
||
1398 | if (!sec) |
||
1399 | return NULL; |
||
1400 | |||
1401 | /* find the resources section */ |
||
1402 | for (i = 0; i < num_sections; i++) |
||
1403 | if (!memcmp(sec[i].Name, ".rsrc", 6)) |
||
1404 | break; |
||
1405 | |||
1406 | if (i == num_sections) |
||
1407 | return NULL; |
||
1408 | |||
1409 | return &sec[i]; |
||
1410 | } |
||
1411 | |||
415 | daniel-mar | 1412 | static IMAGE_SECTION_HEADER* get_last_section(void* base, DWORD mapping_size) |
1413 | { |
||
1414 | // Fix by Fix by Daniel Marschall: Added this function which is required by the "SizeOfImage" field calculation |
||
418 | daniel-mar | 1415 | // Reported Wine bug here: https://bugs.winehq.org/show_bug.cgi?id=52119 |
415 | daniel-mar | 1416 | |
1417 | IMAGE_SECTION_HEADER* sec; |
||
1418 | IMAGE_NT_HEADERS* nt; |
||
1419 | DWORD num_sections = 0; |
||
1420 | |||
1421 | nt = get_nt_header(base, mapping_size); |
||
1422 | if (!nt) |
||
1423 | return NULL; |
||
1424 | |||
1425 | sec = get_section_header(base, mapping_size, &num_sections); |
||
1426 | if (!sec) |
||
1427 | return NULL; |
||
1428 | |||
1429 | /* find the resources section */ |
||
1430 | return &sec[num_sections - 1]; |
||
1431 | } |
||
1432 | |||
402 | daniel-mar | 1433 | static DWORD get_init_data_size(void* base, DWORD mapping_size) |
1434 | { |
||
1435 | DWORD i, sz = 0, num_sections = 0; |
||
1436 | IMAGE_SECTION_HEADER* s; |
||
1437 | |||
1438 | s = get_section_header(base, mapping_size, &num_sections); |
||
1439 | |||
1440 | for (i = 0; i < num_sections; i++) |
||
1441 | if (s[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) |
||
1442 | sz += s[i].SizeOfRawData; |
||
1443 | |||
405 | daniel-mar | 1444 | //TRACE("size = %08x\n", sz); |
1445 | |||
402 | daniel-mar | 1446 | return sz; |
1447 | } |
||
1448 | |||
415 | daniel-mar | 1449 | // |
1450 | // peRoundUpToAlignment() - rounds dwValue up to nearest dwAlign |
||
1451 | // |
||
1452 | DWORD peRoundUpToAlignment(DWORD dwAlign, DWORD dwVal) |
||
1453 | { |
||
1454 | // Fix by Fix by Daniel Marschall: Added this function, based on |
||
1455 | // https://stackoverflow.com/questions/39022853/how-is-sizeofimage-in-the-pe-optional-header-computed |
||
418 | daniel-mar | 1456 | // Reported Wine bug here: https://bugs.winehq.org/show_bug.cgi?id=52119 |
415 | daniel-mar | 1457 | if (dwAlign) |
1458 | { |
||
1459 | //do the rounding with bitwise operations... |
||
1460 | |||
1461 | //create bit mask of bits to keep |
||
1462 | // e.g. if section alignment is 0x1000 1000000000000 |
||
1463 | // we want the following bitmask 11111111111111111111000000000000 |
||
1464 | DWORD dwMask = ~(dwAlign - 1); |
||
1465 | |||
1466 | //round up by adding full alignment (dwAlign-1 since if already aligned we don't want anything to change), |
||
1467 | // then mask off any lower bits |
||
1468 | dwVal = (dwVal + dwAlign - 1) & dwMask; |
||
1469 | } |
||
1470 | |||
1471 | return(dwVal); |
||
1472 | |||
416 | daniel-mar | 1473 | } |
415 | daniel-mar | 1474 | |
416 | daniel-mar | 1475 | ULONGLONG peRoundUpToAlignment64(ULONGLONG dwAlign, ULONGLONG dwVal) |
1476 | { |
||
1477 | // Fix by Fix by Daniel Marschall: Added this function, based on |
||
1478 | // https://stackoverflow.com/questions/39022853/how-is-sizeofimage-in-the-pe-optional-header-computed |
||
418 | daniel-mar | 1479 | // Reported Wine bug here: https://bugs.winehq.org/show_bug.cgi?id=52119 |
416 | daniel-mar | 1480 | if (dwAlign) |
1481 | { |
||
1482 | //do the rounding with bitwise operations... |
||
1483 | |||
1484 | //create bit mask of bits to keep |
||
1485 | // e.g. if section alignment is 0x1000 1000000000000 |
||
1486 | // we want the following bitmask 11111111111111111111000000000000 |
||
1487 | ULONGLONG dwMask = ~(dwAlign - 1); |
||
1488 | |||
1489 | //round up by adding full alignment (dwAlign-1 since if already aligned we don't want anything to change), |
||
1490 | // then mask off any lower bits |
||
1491 | dwVal = (dwVal + dwAlign - 1) & dwMask; |
||
1492 | } |
||
1493 | |||
1494 | return(dwVal); |
||
1495 | |||
1496 | } |
||
1497 | |||
402 | daniel-mar | 1498 | static BOOL write_raw_resources(QUEUEDUPDATES* updates) |
1499 | { |
||
1500 | CHAR tempdir[MAX_PATH], tempfile[MAX_PATH]; |
||
1501 | DWORD i, section_size; |
||
1502 | BOOL ret = FALSE; |
||
415 | daniel-mar | 1503 | IMAGE_SECTION_HEADER* sec, *lastsec; |
402 | daniel-mar | 1504 | IMAGE_NT_HEADERS32* nt; |
1505 | IMAGE_NT_HEADERS64* nt64; |
||
1506 | struct resource_size_info res_size; |
||
1507 | BYTE* res_base; |
||
1508 | struct mapping_info* read_map = NULL, * write_map = NULL; |
||
1509 | DWORD PeSectionAlignment, PeFileAlignment, PeSizeOfImage; |
||
1510 | |||
1511 | /* copy the exe to a temp file then update the temp file... */ |
||
1512 | tempdir[0] = 0; |
||
405 | daniel-mar | 1513 | if (!GetTempPathA(MAX_PATH, tempdir)) |
402 | daniel-mar | 1514 | return ret; |
1515 | |||
405 | daniel-mar | 1516 | if (!GetTempFileNameA(tempdir, "resu", 0, tempfile)) |
402 | daniel-mar | 1517 | return ret; |
1518 | |||
405 | daniel-mar | 1519 | if (!CopyFileA(updates->pFileName, tempfile, FALSE)) |
402 | daniel-mar | 1520 | goto done; |
1521 | |||
405 | daniel-mar | 1522 | //TRACE("tempfile %s\n", debugstr_w(tempfile)); |
1523 | |||
402 | daniel-mar | 1524 | if (!updates->bDeleteExistingResources) |
1525 | { |
||
1526 | read_map = create_mapping(updates->pFileName, FALSE); |
||
405 | daniel-mar | 1527 | if (!read_map) |
402 | daniel-mar | 1528 | goto done; |
1529 | |||
1530 | ret = read_mapped_resources(updates, read_map->base, read_map->size); |
||
1531 | if (!ret) |
||
1532 | { |
||
1533 | //ERR("failed to read existing resources\n"); |
||
1534 | goto done; |
||
1535 | } |
||
1536 | } |
||
1537 | |||
1538 | write_map = create_mapping(tempfile, TRUE); |
||
405 | daniel-mar | 1539 | if (!write_map) |
402 | daniel-mar | 1540 | goto done; |
1541 | |||
1542 | nt = (IMAGE_NT_HEADERS32*)get_nt_header(write_map->base, write_map->size); |
||
405 | daniel-mar | 1543 | if (!nt) |
402 | daniel-mar | 1544 | goto done; |
1545 | |||
1546 | nt64 = (IMAGE_NT_HEADERS64*)nt; |
||
1547 | if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { |
||
1548 | PeSectionAlignment = nt64->OptionalHeader.SectionAlignment; |
||
1549 | PeFileAlignment = nt64->OptionalHeader.FileAlignment; |
||
1550 | PeSizeOfImage = nt64->OptionalHeader.SizeOfImage; |
||
1551 | } |
||
1552 | else { |
||
1553 | PeSectionAlignment = nt->OptionalHeader.SectionAlignment; |
||
1554 | PeFileAlignment = nt->OptionalHeader.FileAlignment; |
||
1555 | PeSizeOfImage = nt->OptionalHeader.SizeOfImage; |
||
1556 | } |
||
1557 | |||
1558 | if ((LONG)PeSectionAlignment <= 0) |
||
1559 | { |
||
1560 | //ERR("invalid section alignment %08x\n", PeSectionAlignment); |
||
1561 | goto done; |
||
1562 | } |
||
1563 | |||
1564 | if ((LONG)PeFileAlignment <= 0) |
||
1565 | { |
||
1566 | //ERR("invalid file alignment %08x\n", PeFileAlignment); |
||
1567 | goto done; |
||
1568 | } |
||
1569 | |||
1570 | sec = get_resource_section(write_map->base, write_map->size); |
||
1571 | if (!sec) /* no section, add one */ |
||
1572 | { |
||
1573 | DWORD num_sections; |
||
1574 | |||
1575 | sec = get_section_header(write_map->base, write_map->size, &num_sections); |
||
405 | daniel-mar | 1576 | if (!sec) |
402 | daniel-mar | 1577 | goto done; |
1578 | |||
1579 | sec += num_sections; |
||
1580 | nt->FileHeader.NumberOfSections++; |
||
1581 | |||
1582 | memset(sec, 0, sizeof * sec); |
||
1583 | memcpy(sec->Name, ".rsrc", 5); |
||
1584 | sec->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; |
||
1585 | sec->VirtualAddress = PeSizeOfImage; |
||
1586 | } |
||
1587 | |||
1588 | if (!sec->PointerToRawData) /* empty section */ |
||
1589 | { |
||
1590 | sec->PointerToRawData = write_map->size + (-(int)write_map->size) % PeFileAlignment; |
||
1591 | sec->SizeOfRawData = 0; |
||
1592 | } |
||
1593 | |||
405 | daniel-mar | 1594 | //TRACE("before .rsrc at %08x, size %08x\n", sec->PointerToRawData, sec->SizeOfRawData); |
1595 | |||
402 | daniel-mar | 1596 | get_resource_sizes(updates, &res_size); |
1597 | |||
1598 | /* round up the section size */ |
||
1599 | section_size = res_size.total_size; |
||
1600 | section_size += (-(int)section_size) % PeFileAlignment; |
||
1601 | |||
405 | daniel-mar | 1602 | //TRACE("requires %08x (%08x) bytes\n", res_size.total_size, section_size ); |
1603 | |||
402 | daniel-mar | 1604 | /* check if the file size needs to be changed */ |
1605 | if (section_size != sec->SizeOfRawData) |
||
1606 | { |
||
1607 | DWORD old_size = write_map->size; |
||
1608 | DWORD virtual_section_size = res_size.total_size + (-(int)res_size.total_size) % PeSectionAlignment; |
||
1609 | int delta = section_size - (sec->SizeOfRawData + (-(int)sec->SizeOfRawData) % PeFileAlignment); |
||
1610 | int rva_delta = virtual_section_size - |
||
1611 | (sec->Misc.VirtualSize + (-(int)sec->Misc.VirtualSize) % PeSectionAlignment); |
||
1612 | /* when new section is added it could end past current mapping size */ |
||
1613 | BOOL rsrc_is_last = sec->PointerToRawData + sec->SizeOfRawData >= old_size; |
||
1614 | /* align .rsrc size when possible */ |
||
1615 | DWORD mapping_size = rsrc_is_last ? sec->PointerToRawData + section_size : old_size + delta; |
||
1616 | |||
1617 | /* postpone file truncation if there are some data to be moved down from file end */ |
||
1618 | BOOL resize_after = mapping_size < old_size && !rsrc_is_last; |
||
1619 | |||
405 | daniel-mar | 1620 | //TRACE("file size %08x -> %08x\n", old_size, mapping_size); |
1621 | |||
402 | daniel-mar | 1622 | if (!resize_after) |
1623 | { |
||
1624 | /* unmap the file before changing the file size */ |
||
1625 | ret = resize_mapping(write_map, mapping_size); |
||
1626 | |||
1627 | /* get the pointers again - they might be different after remapping */ |
||
1628 | nt = (IMAGE_NT_HEADERS32*)get_nt_header(write_map->base, mapping_size); |
||
1629 | if (!nt) |
||
1630 | { |
||
1631 | //ERR("couldn't get NT header\n"); |
||
1632 | goto done; |
||
1633 | } |
||
1634 | nt64 = (IMAGE_NT_HEADERS64*)nt; |
||
1635 | |||
1636 | sec = get_resource_section(write_map->base, mapping_size); |
||
405 | daniel-mar | 1637 | if (!sec) |
402 | daniel-mar | 1638 | goto done; |
1639 | } |
||
1640 | |||
1641 | if (!rsrc_is_last) /* not last section, relocate trailing sections */ |
||
1642 | { |
||
1643 | IMAGE_SECTION_HEADER* s; |
||
1644 | DWORD tail_start = sec->PointerToRawData + sec->SizeOfRawData; |
||
1645 | DWORD i, num_sections = 0; |
||
1646 | |||
1647 | memmove((char*)write_map->base + tail_start + delta, (char*)write_map->base + tail_start, old_size - tail_start); |
||
1648 | |||
1649 | s = get_section_header(write_map->base, mapping_size, &num_sections); |
||
1650 | |||
1651 | for (i = 0; i < num_sections; i++) |
||
1652 | { |
||
1653 | if (s[i].PointerToRawData > sec->PointerToRawData) |
||
1654 | { |
||
1655 | s[i].PointerToRawData += delta; |
||
1656 | s[i].VirtualAddress += rva_delta; |
||
1657 | } |
||
1658 | } |
||
1659 | } |
||
1660 | |||
1661 | if (resize_after) |
||
1662 | { |
||
1663 | ret = resize_mapping(write_map, mapping_size); |
||
1664 | |||
1665 | nt = (IMAGE_NT_HEADERS32*)get_nt_header(write_map->base, mapping_size); |
||
1666 | if (!nt) |
||
1667 | { |
||
1668 | //ERR("couldn't get NT header\n"); |
||
1669 | goto done; |
||
1670 | } |
||
1671 | nt64 = (IMAGE_NT_HEADERS64*)nt; |
||
1672 | |||
1673 | sec = get_resource_section(write_map->base, mapping_size); |
||
405 | daniel-mar | 1674 | if (!sec) |
402 | daniel-mar | 1675 | goto done; |
1676 | } |
||
1677 | |||
1678 | /* adjust the PE header information */ |
||
1679 | sec->SizeOfRawData = section_size; |
||
1680 | sec->Misc.VirtualSize = virtual_section_size; |
||
1681 | if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { |
||
416 | daniel-mar | 1682 | ULONGLONG pEndOfLastSection, pEndOfLastSectionMem, uCalcSizeOfFile; |
415 | daniel-mar | 1683 | |
402 | daniel-mar | 1684 | nt64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = sec->VirtualAddress; |
1685 | nt64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = res_size.total_size; |
||
1686 | nt64->OptionalHeader.SizeOfInitializedData = get_init_data_size(write_map->base, mapping_size); |
||
1687 | |||
1688 | for (i = 0; i < nt64->OptionalHeader.NumberOfRvaAndSizes; i++) |
||
1689 | if (nt64->OptionalHeader.DataDirectory[i].VirtualAddress > sec->VirtualAddress) |
||
1690 | nt64->OptionalHeader.DataDirectory[i].VirtualAddress += rva_delta; |
||
415 | daniel-mar | 1691 | |
1692 | //nt64->OptionalHeader.SizeOfImage += rva_delta; |
||
1693 | // Fix by Daniel Marschall: Added this calculation of "SizeOfImage". |
||
1694 | // With the original implementation, Windows won't load some images! |
||
1695 | // https://stackoverflow.com/questions/39022853/how-is-sizeofimage-in-the-pe-optional-header-computed |
||
418 | daniel-mar | 1696 | // Reported Wine bug here: https://bugs.winehq.org/show_bug.cgi?id=52119 |
415 | daniel-mar | 1697 | lastsec = get_last_section(write_map->base, mapping_size); |
444 | daniel-mar | 1698 | pEndOfLastSection = (ULONGLONG)lastsec->VirtualAddress + (ULONGLONG)lastsec->Misc.VirtualSize + nt64->OptionalHeader.ImageBase; |
415 | daniel-mar | 1699 | //NOTE: we are rounding to memory section alignment, not file |
416 | daniel-mar | 1700 | pEndOfLastSectionMem = peRoundUpToAlignment64(nt64->OptionalHeader.SectionAlignment, pEndOfLastSection); |
415 | daniel-mar | 1701 | uCalcSizeOfFile = pEndOfLastSectionMem - nt64->OptionalHeader.ImageBase; |
416 | daniel-mar | 1702 | nt64->OptionalHeader.SizeOfImage = (DWORD)uCalcSizeOfFile; |
402 | daniel-mar | 1703 | } |
1704 | else { |
||
415 | daniel-mar | 1705 | DWORD pEndOfLastSection, pEndOfLastSectionMem, uCalcSizeOfFile; |
1706 | |||
402 | daniel-mar | 1707 | nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = sec->VirtualAddress; |
1708 | nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = res_size.total_size; |
||
1709 | nt->OptionalHeader.SizeOfInitializedData = get_init_data_size(write_map->base, mapping_size); |
||
1710 | |||
1711 | for (i = 0; i < nt->OptionalHeader.NumberOfRvaAndSizes; i++) |
||
1712 | if (nt->OptionalHeader.DataDirectory[i].VirtualAddress > sec->VirtualAddress) |
||
1713 | nt->OptionalHeader.DataDirectory[i].VirtualAddress += rva_delta; |
||
415 | daniel-mar | 1714 | |
1715 | //nt->OptionalHeader.SizeOfImage += rva_delta; |
||
1716 | // Fix by Daniel Marschall: Added this calculation of "SizeOfImage". |
||
1717 | // With the original implementation, Windows won't load some images! |
||
1718 | // https://stackoverflow.com/questions/39022853/how-is-sizeofimage-in-the-pe-optional-header-computed |
||
418 | daniel-mar | 1719 | // Reported Wine bug here: https://bugs.winehq.org/show_bug.cgi?id=52119 |
415 | daniel-mar | 1720 | lastsec = get_last_section(write_map->base, mapping_size); |
1721 | pEndOfLastSection = lastsec->VirtualAddress + lastsec->Misc.VirtualSize + nt->OptionalHeader.ImageBase; |
||
1722 | //NOTE: we are rounding to memory section alignment, not file |
||
1723 | pEndOfLastSectionMem = peRoundUpToAlignment(nt->OptionalHeader.SectionAlignment, pEndOfLastSection); |
||
1724 | uCalcSizeOfFile = pEndOfLastSectionMem - nt->OptionalHeader.ImageBase; |
||
1725 | nt->OptionalHeader.SizeOfImage = uCalcSizeOfFile; |
||
402 | daniel-mar | 1726 | } |
1727 | } |
||
1728 | |||
1729 | res_base = (LPBYTE)write_map->base + sec->PointerToRawData; |
||
1730 | |||
405 | daniel-mar | 1731 | //TRACE("base = %p offset = %08x\n", write_map->base, sec->PointerToRawData); |
1732 | |||
402 | daniel-mar | 1733 | ret = write_resources(updates, res_base, &res_size, sec->VirtualAddress); |
1734 | |||
1735 | res_write_padding(res_base + res_size.total_size, section_size - res_size.total_size); |
||
1736 | |||
405 | daniel-mar | 1737 | //TRACE("after .rsrc at %08x, size %08x\n", sec->PointerToRawData, sec->SizeOfRawData); |
1738 | |||
402 | daniel-mar | 1739 | done: |
1740 | destroy_mapping(read_map); |
||
1741 | destroy_mapping(write_map); |
||
1742 | |||
405 | daniel-mar | 1743 | if (ret) |
402 | daniel-mar | 1744 | ret = CopyFileA(tempfile, updates->pFileName, FALSE); |
1745 | |||
1746 | DeleteFileA(tempfile); |
||
1747 | |||
1748 | return ret; |
||
1749 | } |
||
1750 | |||
1751 | // ------------------------------------------------------------------------------------------------------------------------ |
||
1752 | |||
1753 | HANDLE WINAPI WineBeginUpdateResourceA(LPCSTR pFileName, BOOL bDeleteExistingResources) |
||
1754 | { |
||
1755 | QUEUEDUPDATES* updates = NULL; |
||
1756 | HANDLE hUpdate, file, ret = NULL; |
||
1757 | |||
405 | daniel-mar | 1758 | //TRACE("%s, %d\n", debugstr_w(pFileName), bDeleteExistingResources); |
1759 | |||
402 | daniel-mar | 1760 | hUpdate = GlobalAlloc(GHND, sizeof(QUEUEDUPDATES)); |
1761 | if (!hUpdate) |
||
1762 | return ret; |
||
1763 | |||
1764 | updates = (QUEUEDUPDATES*)GlobalLock(hUpdate); |
||
1765 | if (updates) |
||
1766 | { |
||
1767 | list_init(&updates->root); |
||
1768 | updates->bDeleteExistingResources = bDeleteExistingResources; |
||
1769 | updates->pFileName = (LPSTR)HeapAlloc(GetProcessHeap(), 0, (lstrlenA(pFileName) + 1) * sizeof(CHAR)); |
||
1770 | if (updates->pFileName) |
||
1771 | { |
||
1772 | lstrcpyA(updates->pFileName, pFileName); |
||
1773 | |||
405 | daniel-mar | 1774 | // Fix by Daniel Marschall: Changed "GENERIC_READ | GENERIC_WRITE, 0" to "GENERIC_READ, FILE_SHARE_READ" |
418 | daniel-mar | 1775 | // Reported in https://bugs.winehq.org/show_bug.cgi?id=52121 |
402 | daniel-mar | 1776 | file = CreateFileA(pFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); |
1777 | |||
1778 | /* if resources are deleted, only the file's presence is checked */ |
||
1779 | if (file != INVALID_HANDLE_VALUE && |
||
1780 | (bDeleteExistingResources || check_pe_exe(file, updates))) |
||
1781 | ret = hUpdate; |
||
1782 | else |
||
1783 | HeapFree(GetProcessHeap(), 0, updates->pFileName); |
||
1784 | |||
1785 | CloseHandle(file); |
||
1786 | } |
||
1787 | GlobalUnlock(hUpdate); |
||
1788 | } |
||
1789 | |||
1790 | if (!ret) |
||
1791 | GlobalFree(hUpdate); |
||
1792 | |||
1793 | return ret; |
||
1794 | } |
||
1795 | |||
1796 | BOOL WINAPI WineEndUpdateResourceA(HANDLE hUpdate, BOOL fDiscard) |
||
1797 | { |
||
1798 | QUEUEDUPDATES* updates; |
||
1799 | BOOL ret; |
||
1800 | |||
405 | daniel-mar | 1801 | // TRACE("%p %d\n", hUpdate, fDiscard); |
1802 | |||
402 | daniel-mar | 1803 | updates = (QUEUEDUPDATES*)GlobalLock(hUpdate); |
405 | daniel-mar | 1804 | if (!updates) |
402 | daniel-mar | 1805 | return FALSE; |
1806 | |||
1807 | ret = fDiscard || write_raw_resources(updates); |
||
1808 | |||
1809 | free_resource_directory(&updates->root, 2); |
||
1810 | |||
1811 | HeapFree(GetProcessHeap(), 0, updates->pFileName); |
||
1812 | GlobalUnlock(hUpdate); |
||
1813 | GlobalFree(hUpdate); |
||
1814 | |||
1815 | return ret; |
||
1816 | } |
||
1817 | |||
1818 | BOOL WINAPI WineUpdateResourceA(HANDLE hUpdate, LPCSTR lpType, LPCSTR lpName, |
||
1819 | WORD wLanguage, LPVOID lpData, DWORD cbData) |
||
1820 | { |
||
1821 | QUEUEDUPDATES* updates; |
||
1822 | UNICODE_STRING nameW, typeW; |
||
1823 | BOOL ret = FALSE; |
||
1824 | |||
405 | daniel-mar | 1825 | //TRACE("%p %s %s %08x %p %d\n", hUpdate, |
1826 | // debugstr_w(lpType), debugstr_w(lpName), wLanguage, lpData, cbData); |
||
1827 | |||
402 | daniel-mar | 1828 | nameW.Buffer = typeW.Buffer = NULL; |
1829 | updates = (QUEUEDUPDATES*)GlobalLock(hUpdate); |
||
1830 | if (updates) |
||
1831 | { |
||
1832 | if (!set_ntstatus(get_res_nameA(lpName, &nameW))) goto done; |
||
1833 | if (!set_ntstatus(get_res_nameA(lpType, &typeW))) goto done; |
||
1834 | |||
1835 | if (lpData == NULL && cbData == 0) /* remove resource */ |
||
1836 | { |
||
1837 | ret = update_add_resource(updates, typeW.Buffer, nameW.Buffer, wLanguage, NULL, TRUE); |
||
1838 | } |
||
1839 | else |
||
1840 | { |
||
1841 | struct resource_data* data; |
||
1842 | data = allocate_resource_data(wLanguage, 0, lpData, cbData, TRUE); |
||
1843 | if (data) |
||
1844 | ret = update_add_resource(updates, typeW.Buffer, nameW.Buffer, wLanguage, data, TRUE); |
||
1845 | } |
||
1846 | |||
1847 | done: |
||
1848 | GlobalUnlock(hUpdate); |
||
1849 | } |
||
1850 | |||
1851 | if (!IS_INTRESOURCE(nameW.Buffer)) HeapFree(GetProcessHeap(), 0, nameW.Buffer); |
||
1852 | if (!IS_INTRESOURCE(typeW.Buffer)) HeapFree(GetProcessHeap(), 0, typeW.Buffer); |
||
1853 | return ret; |
||
1854 | } |