Subversion Repositories filter_foundry

Rev

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