4 * Copyright 1993 Robert J. Amstadt
5 * Copyright 1995, 2003 Alexandre Julliard
6 * Copyright 2006 Mike McCormack
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #define WIN32_NO_STATUS
30 #include "ddk/ntddk.h"
31 #include "wine/debug.h"
32 #include "wine/exception.h"
33 #include "wine/list.h"
34 #include "kernel_private.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(resource
);
38 /* we don't want to include winuser.h just for this */
39 #define IS_INTRESOURCE(x) (((ULONG_PTR)(x) >> 16) == 0)
41 /* retrieve the resource name to pass to the ntdll functions */
42 static NTSTATUS
get_res_nameA( LPCSTR name
, UNICODE_STRING
*str
)
44 if (IS_INTRESOURCE(name
))
46 str
->Buffer
= ULongToPtr(LOWORD(name
));
47 return STATUS_SUCCESS
;
52 if (RtlCharToInteger( name
+ 1, 10, &value
) != STATUS_SUCCESS
|| HIWORD(value
))
53 return STATUS_INVALID_PARAMETER
;
54 str
->Buffer
= ULongToPtr(value
);
55 return STATUS_SUCCESS
;
57 RtlCreateUnicodeStringFromAsciiz( str
, name
);
58 return STATUS_SUCCESS
;
61 static NTSTATUS
get_res_nameW( LPCWSTR name
, UNICODE_STRING
*str
)
63 if (IS_INTRESOURCE(name
))
65 str
->Buffer
= ULongToPtr( LOWORD(name
) );
66 return STATUS_SUCCESS
;
71 RtlInitUnicodeString( str
, name
+ 1 );
72 if (RtlUnicodeStringToInteger( str
, 10, &value
) != STATUS_SUCCESS
|| HIWORD(value
))
73 return STATUS_INVALID_PARAMETER
;
74 str
->Buffer
= ULongToPtr(value
);
75 return STATUS_SUCCESS
;
77 RtlCreateUnicodeString( str
, name
);
78 RtlUpcaseUnicodeString( str
, str
, FALSE
);
79 return STATUS_SUCCESS
;
82 /**********************************************************************
83 * FindResourceExA (KERNEL32.@)
85 HRSRC WINAPI
FindResourceExA( HMODULE module
, LPCSTR type
, LPCSTR name
, WORD lang
)
88 UNICODE_STRING nameW
, typeW
;
91 TRACE( "%p %s %s %04x\n", module
, debugstr_a(type
), debugstr_a(name
), lang
);
93 if (!module
) module
= GetModuleHandleW(0);
99 if (!(status
= get_res_nameA( name
, &nameW
)) && !(status
= get_res_nameA( type
, &typeW
)))
100 ret
= FindResourceExW( module
, typeW
.Buffer
, nameW
.Buffer
, lang
);
102 SetLastError( RtlNtStatusToDosError(status
) );
106 SetLastError( ERROR_INVALID_PARAMETER
);
110 if (!IS_INTRESOURCE(nameW
.Buffer
)) HeapFree( GetProcessHeap(), 0, nameW
.Buffer
);
111 if (!IS_INTRESOURCE(typeW
.Buffer
)) HeapFree( GetProcessHeap(), 0, typeW
.Buffer
);
116 /**********************************************************************
117 * FindResourceA (KERNEL32.@)
119 HRSRC WINAPI
FindResourceA( HMODULE hModule
, LPCSTR name
, LPCSTR type
)
121 return FindResourceExA( hModule
, type
, name
, MAKELANGID( LANG_NEUTRAL
, SUBLANG_NEUTRAL
) );
125 /**********************************************************************
126 * EnumResourceTypesA (KERNEL32.@)
128 BOOL WINAPI
EnumResourceTypesA( HMODULE hmod
, ENUMRESTYPEPROCA lpfun
, LONG_PTR lparam
)
130 return EnumResourceTypesExA( hmod
, lpfun
, lparam
, 0, 0 );
134 /**********************************************************************
135 * EnumResourceTypesW (KERNEL32.@)
137 BOOL WINAPI
EnumResourceTypesW( HMODULE hmod
, ENUMRESTYPEPROCW lpfun
, LONG_PTR lparam
)
139 return EnumResourceTypesExW( hmod
, lpfun
, lparam
, 0, 0 );
143 /**********************************************************************
144 * EnumResourceNamesA (KERNEL32.@)
146 BOOL WINAPI
EnumResourceNamesA( HMODULE hmod
, LPCSTR type
, ENUMRESNAMEPROCA lpfun
, LONG_PTR lparam
)
148 return EnumResourceNamesExA( hmod
, type
, lpfun
, lparam
, 0, 0 );
152 /**********************************************************************
153 * EnumResourceLanguagesA (KERNEL32.@)
155 BOOL WINAPI
EnumResourceLanguagesA( HMODULE hmod
, LPCSTR type
, LPCSTR name
,
156 ENUMRESLANGPROCA lpfun
, LONG_PTR lparam
)
158 return EnumResourceLanguagesExA( hmod
, type
, name
, lpfun
, lparam
, 0, 0 );
162 /**********************************************************************
163 * EnumResourceLanguagesW (KERNEL32.@)
165 BOOL WINAPI
EnumResourceLanguagesW( HMODULE hmod
, LPCWSTR type
, LPCWSTR name
,
166 ENUMRESLANGPROCW lpfun
, LONG_PTR lparam
)
168 return EnumResourceLanguagesExW( hmod
, type
, name
, lpfun
, lparam
, 0, 0 );
173 * Data structure for updating resources.
174 * Type/Name/Language is a keyset for accessing resource data.
176 * QUEUEDUPDATES (root) ->
177 * list of struct resource_dir_entry (Type) ->
178 * list of struct resource_dir_entry (Name) ->
179 * list of struct resource_data Language + Data
186 BOOL bDeleteExistingResources
;
190 /* this structure is shared for types and names */
191 struct resource_dir_entry
{
194 struct list children
;
197 /* this structure is the leaf */
198 struct resource_data
{
206 static int resource_strcmp( LPCWSTR a
, LPCWSTR b
)
210 if (!IS_INTRESOURCE( a
) && !IS_INTRESOURCE( b
) )
211 return wcscmp( a
, b
);
212 /* strings come before ids */
213 if (!IS_INTRESOURCE( a
) && IS_INTRESOURCE( b
))
215 if (!IS_INTRESOURCE( b
) && IS_INTRESOURCE( a
))
217 return ( a
< b
) ? -1 : 1;
220 static struct resource_dir_entry
*find_resource_dir_entry( struct list
*dir
, LPCWSTR id
)
222 struct resource_dir_entry
*ent
;
224 /* match either IDs or strings */
225 LIST_FOR_EACH_ENTRY( ent
, dir
, struct resource_dir_entry
, entry
)
226 if (!resource_strcmp( id
, ent
->id
))
232 static struct resource_data
*find_resource_data( struct list
*dir
, LANGID lang
)
234 struct resource_data
*res_data
;
236 /* match only languages here */
237 LIST_FOR_EACH_ENTRY( res_data
, dir
, struct resource_data
, entry
)
238 if ( lang
== res_data
->lang
)
244 static void add_resource_dir_entry( struct list
*dir
, struct resource_dir_entry
*resdir
)
246 struct resource_dir_entry
*ent
;
248 LIST_FOR_EACH_ENTRY( ent
, dir
, struct resource_dir_entry
, entry
)
250 if (0>resource_strcmp( ent
->id
, resdir
->id
))
253 list_add_before( &ent
->entry
, &resdir
->entry
);
256 list_add_tail( dir
, &resdir
->entry
);
259 static void add_resource_data_entry( struct list
*dir
, struct resource_data
*resdata
)
261 struct resource_data
*ent
;
263 LIST_FOR_EACH_ENTRY( ent
, dir
, struct resource_data
, entry
)
265 if (ent
->lang
< resdata
->lang
)
268 list_add_before( &ent
->entry
, &resdata
->entry
);
271 list_add_tail( dir
, &resdata
->entry
);
274 static LPWSTR
res_strdupW( LPCWSTR str
)
279 if (IS_INTRESOURCE(str
))
280 return (LPWSTR
) (UINT_PTR
) LOWORD(str
);
281 len
= (lstrlenW( str
) + 1) * sizeof (WCHAR
);
282 ret
= HeapAlloc( GetProcessHeap(), 0, len
);
283 memcpy( ret
, str
, len
);
287 static void res_free_str( LPWSTR str
)
289 if (!IS_INTRESOURCE(str
))
290 HeapFree( GetProcessHeap(), 0, str
);
293 static BOOL
update_add_resource( QUEUEDUPDATES
*updates
, LPCWSTR Type
, LPCWSTR Name
,
294 LANGID Lang
, struct resource_data
*resdata
,
295 BOOL overwrite_existing
)
297 struct resource_dir_entry
*restype
, *resname
;
298 struct resource_data
*existing
;
300 TRACE("%p %s %s %p %d\n", updates
,
301 debugstr_w(Type
), debugstr_w(Name
), resdata
, overwrite_existing
);
303 restype
= find_resource_dir_entry( &updates
->root
, Type
);
306 restype
= HeapAlloc( GetProcessHeap(), 0, sizeof *restype
);
307 restype
->id
= res_strdupW( Type
);
308 list_init( &restype
->children
);
309 add_resource_dir_entry( &updates
->root
, restype
);
312 resname
= find_resource_dir_entry( &restype
->children
, Name
);
315 resname
= HeapAlloc( GetProcessHeap(), 0, sizeof *resname
);
316 resname
->id
= res_strdupW( Name
);
317 list_init( &resname
->children
);
318 add_resource_dir_entry( &restype
->children
, resname
);
322 * If there's an existing resource entry with matching (Type,Name,Language)
323 * it needs to be removed before adding the new data.
325 existing
= find_resource_data( &resname
->children
, Lang
);
328 if (!overwrite_existing
)
330 list_remove( &existing
->entry
);
331 HeapFree( GetProcessHeap(), 0, existing
);
335 add_resource_data_entry( &resname
->children
, resdata
);
340 static struct resource_data
*allocate_resource_data( WORD Language
, DWORD codepage
,
341 LPVOID lpData
, DWORD cbData
, BOOL copy_data
)
343 struct resource_data
*resdata
;
345 if (!lpData
|| !cbData
)
348 resdata
= HeapAlloc( GetProcessHeap(), 0, sizeof *resdata
+ (copy_data
? cbData
: 0) );
351 resdata
->lang
= Language
;
352 resdata
->codepage
= codepage
;
353 resdata
->cbData
= cbData
;
356 resdata
->lpData
= &resdata
[1];
357 memcpy( resdata
->lpData
, lpData
, cbData
);
360 resdata
->lpData
= lpData
;
366 static void free_resource_directory( struct list
*head
, int level
)
368 struct list
*ptr
= NULL
;
370 while ((ptr
= list_head( head
)))
375 struct resource_dir_entry
*ent
;
377 ent
= LIST_ENTRY( ptr
, struct resource_dir_entry
, entry
);
378 res_free_str( ent
->id
);
379 free_resource_directory( &ent
->children
, level
- 1 );
380 HeapFree(GetProcessHeap(), 0, ent
);
384 struct resource_data
*data
;
386 data
= LIST_ENTRY( ptr
, struct resource_data
, entry
);
387 HeapFree( GetProcessHeap(), 0, data
);
392 static IMAGE_NT_HEADERS
*get_nt_header( void *base
, DWORD mapping_size
)
394 IMAGE_NT_HEADERS
*nt
;
395 IMAGE_DOS_HEADER
*dos
;
397 if (mapping_size
<sizeof (*dos
))
401 if (dos
->e_magic
!= IMAGE_DOS_SIGNATURE
)
404 if ((dos
->e_lfanew
+ sizeof (*nt
)) > mapping_size
)
407 nt
= (void*) ((BYTE
*)base
+ dos
->e_lfanew
);
409 if (nt
->Signature
!= IMAGE_NT_SIGNATURE
)
415 static IMAGE_SECTION_HEADER
*get_section_header( void *base
, DWORD mapping_size
, DWORD
*num_sections
)
417 IMAGE_NT_HEADERS
*nt
;
420 nt
= get_nt_header( base
, mapping_size
);
424 /* check that we don't go over the end of the file accessing the sections */
425 section_ofs
= FIELD_OFFSET(IMAGE_NT_HEADERS
, OptionalHeader
) + nt
->FileHeader
.SizeOfOptionalHeader
;
426 if ((nt
->FileHeader
.NumberOfSections
* sizeof (IMAGE_SECTION_HEADER
) + section_ofs
) > mapping_size
)
430 *num_sections
= nt
->FileHeader
.NumberOfSections
;
432 /* from here we have a valid PE exe to update */
433 return (void*) ((BYTE
*)nt
+ section_ofs
);
436 static BOOL
check_pe_exe( HANDLE file
, QUEUEDUPDATES
*updates
)
438 const IMAGE_NT_HEADERS32
*nt
;
439 const IMAGE_NT_HEADERS64
*nt64
;
440 const IMAGE_SECTION_HEADER
*sec
;
441 const IMAGE_DATA_DIRECTORY
*dd
;
444 DWORD mapping_size
, num_sections
= 0;
447 mapping_size
= GetFileSize( file
, NULL
);
449 mapping
= CreateFileMappingW( file
, NULL
, PAGE_READONLY
, 0, 0, NULL
);
453 base
= MapViewOfFile( mapping
, FILE_MAP_READ
, 0, 0, mapping_size
);
457 nt
= (IMAGE_NT_HEADERS32
*)get_nt_header( base
, mapping_size
);
461 nt64
= (IMAGE_NT_HEADERS64
*)nt
;
462 dd
= &nt
->OptionalHeader
.DataDirectory
[0];
463 if (nt
->OptionalHeader
.Magic
== IMAGE_NT_OPTIONAL_HDR64_MAGIC
)
464 dd
= &nt64
->OptionalHeader
.DataDirectory
[0];
466 TRACE("resources: %08lx %08lx\n",
467 dd
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
,
468 dd
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].Size
);
470 sec
= get_section_header( base
, mapping_size
, &num_sections
);
478 UnmapViewOfFile( base
);
480 CloseHandle( mapping
);
485 struct resource_size_info
{
489 DWORD data_entry_ofs
;
495 struct mapping_info
{
502 static const IMAGE_SECTION_HEADER
*section_from_rva( void *base
, DWORD mapping_size
, DWORD rva
)
504 const IMAGE_SECTION_HEADER
*sec
;
505 DWORD num_sections
= 0;
508 sec
= get_section_header( base
, mapping_size
, &num_sections
);
512 for (i
=num_sections
-1; i
>=0; i
--)
514 if (sec
[i
].VirtualAddress
<= rva
&&
515 rva
<= (DWORD
)sec
[i
].VirtualAddress
+ sec
[i
].SizeOfRawData
)
524 static void *address_from_rva( void *base
, DWORD mapping_size
, DWORD rva
, DWORD len
)
526 const IMAGE_SECTION_HEADER
*sec
;
528 sec
= section_from_rva( base
, mapping_size
, rva
);
532 if (rva
+ len
<= (DWORD
)sec
->VirtualAddress
+ sec
->SizeOfRawData
)
533 return (void*)((LPBYTE
) base
+ (sec
->PointerToRawData
+ rva
- sec
->VirtualAddress
));
538 static LPWSTR
resource_dup_string( const IMAGE_RESOURCE_DIRECTORY
*root
, const IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
)
540 const IMAGE_RESOURCE_DIR_STRING_U
* string
;
543 if (!entry
->NameIsString
)
544 return UIntToPtr(entry
->Id
);
546 string
= (const IMAGE_RESOURCE_DIR_STRING_U
*) (((const char *)root
) + entry
->NameOffset
);
547 s
= HeapAlloc(GetProcessHeap(), 0, (string
->Length
+ 1)*sizeof (WCHAR
) );
548 memcpy( s
, string
->NameString
, (string
->Length
+ 1)*sizeof (WCHAR
) );
549 s
[string
->Length
] = 0;
554 /* this function is based on the code in winedump's pe.c */
555 static BOOL
enumerate_mapped_resources( QUEUEDUPDATES
*updates
,
556 void *base
, DWORD mapping_size
,
557 const IMAGE_RESOURCE_DIRECTORY
*root
)
559 const IMAGE_RESOURCE_DIRECTORY
*namedir
, *langdir
;
560 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*e1
, *e2
, *e3
;
561 const IMAGE_RESOURCE_DATA_ENTRY
*data
;
564 TRACE("version (%d.%d) %d named %d id entries\n",
565 root
->MajorVersion
, root
->MinorVersion
, root
->NumberOfNamedEntries
, root
->NumberOfIdEntries
);
567 for (i
= 0; i
< root
->NumberOfNamedEntries
+ root
->NumberOfIdEntries
; i
++)
571 e1
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(root
+ 1) + i
;
573 Type
= resource_dup_string( root
, e1
);
575 namedir
= (const IMAGE_RESOURCE_DIRECTORY
*)((const char *)root
+ e1
->OffsetToDirectory
);
576 for (j
= 0; j
< namedir
->NumberOfNamedEntries
+ namedir
->NumberOfIdEntries
; j
++)
580 e2
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(namedir
+ 1) + j
;
582 Name
= resource_dup_string( root
, e2
);
584 langdir
= (const IMAGE_RESOURCE_DIRECTORY
*)((const char *)root
+ e2
->OffsetToDirectory
);
585 for (k
= 0; k
< langdir
->NumberOfNamedEntries
+ langdir
->NumberOfIdEntries
; k
++)
589 struct resource_data
*resdata
;
591 e3
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(langdir
+ 1) + k
;
595 data
= (const IMAGE_RESOURCE_DATA_ENTRY
*)((const char *)root
+ e3
->OffsetToData
);
597 p
= address_from_rva( base
, mapping_size
, data
->OffsetToData
, data
->Size
);
599 resdata
= allocate_resource_data( Lang
, data
->CodePage
, p
, data
->Size
, FALSE
);
602 if (!update_add_resource( updates
, Type
, Name
, Lang
, resdata
, FALSE
))
603 HeapFree( GetProcessHeap(), 0, resdata
);
606 res_free_str( Name
);
608 res_free_str( Type
);
614 static BOOL
read_mapped_resources( QUEUEDUPDATES
*updates
, void *base
, DWORD mapping_size
)
616 const IMAGE_RESOURCE_DIRECTORY
*root
;
617 const IMAGE_NT_HEADERS
*nt
;
618 const IMAGE_SECTION_HEADER
*sec
;
619 DWORD num_sections
= 0, i
;
621 nt
= get_nt_header( base
, mapping_size
);
625 sec
= get_section_header( base
, mapping_size
, &num_sections
);
629 for (i
=0; i
<num_sections
; i
++)
630 if (!memcmp(sec
[i
].Name
, ".rsrc", 6))
633 if (i
== num_sections
)
636 /* check the resource data is inside the mapping */
637 if (sec
[i
].PointerToRawData
> mapping_size
||
638 (sec
[i
].PointerToRawData
+ sec
[i
].SizeOfRawData
) > mapping_size
)
641 TRACE("found .rsrc at %08lx, size %08lx\n", sec
[i
].PointerToRawData
, sec
[i
].SizeOfRawData
);
643 if (!sec
[i
].PointerToRawData
|| sec
[i
].SizeOfRawData
< sizeof(IMAGE_RESOURCE_DIRECTORY
))
646 root
= (void*) ((BYTE
*)base
+ sec
[i
].PointerToRawData
);
647 enumerate_mapped_resources( updates
, base
, mapping_size
, root
);
652 static BOOL
map_file_into_memory( struct mapping_info
*mi
)
654 DWORD page_attr
, perm
;
659 page_attr
= PAGE_READWRITE
;
660 perm
= FILE_MAP_WRITE
| FILE_MAP_READ
;
664 page_attr
= PAGE_READONLY
;
665 perm
= FILE_MAP_READ
;
668 mapping
= CreateFileMappingW( mi
->file
, NULL
, page_attr
, 0, 0, NULL
);
669 if (!mapping
) return FALSE
;
671 mi
->base
= MapViewOfFile( mapping
, perm
, 0, 0, mi
->size
);
672 CloseHandle( mapping
);
674 return mi
->base
!= NULL
;
677 static BOOL
unmap_file_from_memory( struct mapping_info
*mi
)
680 UnmapViewOfFile( mi
->base
);
685 static void destroy_mapping( struct mapping_info
*mi
)
689 unmap_file_from_memory( mi
);
691 CloseHandle( mi
->file
);
692 HeapFree( GetProcessHeap(), 0, mi
);
695 static struct mapping_info
*create_mapping( LPCWSTR name
, BOOL rw
)
697 struct mapping_info
*mi
;
699 mi
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof *mi
);
705 mi
->file
= CreateFileW( name
, GENERIC_READ
| (rw
? GENERIC_WRITE
: 0),
706 0, NULL
, OPEN_EXISTING
, 0, 0 );
708 if (mi
->file
!= INVALID_HANDLE_VALUE
)
710 mi
->size
= GetFileSize( mi
->file
, NULL
);
712 if (map_file_into_memory( mi
))
715 destroy_mapping( mi
);
719 static BOOL
resize_mapping( struct mapping_info
*mi
, DWORD new_size
)
721 if (!unmap_file_from_memory( mi
))
724 /* change the file size */
725 SetFilePointer( mi
->file
, new_size
, NULL
, FILE_BEGIN
);
726 if (!SetEndOfFile( mi
->file
))
728 ERR("failed to set file size to %08lx\n", new_size
);
734 return map_file_into_memory( mi
);
737 static void get_resource_sizes( QUEUEDUPDATES
*updates
, struct resource_size_info
*si
)
739 struct resource_dir_entry
*types
, *names
;
740 struct resource_data
*data
;
741 DWORD num_types
= 0, num_names
= 0, num_langs
= 0, strings_size
= 0, data_size
= 0;
743 memset( si
, 0, sizeof *si
);
745 LIST_FOR_EACH_ENTRY( types
, &updates
->root
, struct resource_dir_entry
, entry
)
748 if (!IS_INTRESOURCE( types
->id
))
749 strings_size
+= sizeof (WORD
) + lstrlenW( types
->id
)*sizeof (WCHAR
);
751 LIST_FOR_EACH_ENTRY( names
, &types
->children
, struct resource_dir_entry
, entry
)
755 if (!IS_INTRESOURCE( names
->id
))
756 strings_size
+= sizeof (WORD
) + lstrlenW( names
->id
)*sizeof (WCHAR
);
758 LIST_FOR_EACH_ENTRY( data
, &names
->children
, struct resource_data
, entry
)
761 data_size
+= (data
->cbData
+ 3) & ~3;
766 /* names are at the end of the types */
767 si
->names_ofs
= sizeof (IMAGE_RESOURCE_DIRECTORY
) +
768 num_types
* sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY
);
770 /* language directories are at the end of the names */
771 si
->langs_ofs
= si
->names_ofs
+
772 num_types
* sizeof (IMAGE_RESOURCE_DIRECTORY
) +
773 num_names
* sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY
);
775 si
->data_entry_ofs
= si
->langs_ofs
+
776 num_names
* sizeof (IMAGE_RESOURCE_DIRECTORY
) +
777 num_langs
* sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY
);
779 si
->strings_ofs
= si
->data_entry_ofs
+
780 num_langs
* sizeof (IMAGE_RESOURCE_DATA_ENTRY
);
782 si
->data_ofs
= si
->strings_ofs
+ ((strings_size
+ 3) & ~3);
784 si
->total_size
= si
->data_ofs
+ data_size
;
786 TRACE("names %08lx langs %08lx data entries %08lx strings %08lx data %08lx total %08lx\n",
787 si
->names_ofs
, si
->langs_ofs
, si
->data_entry_ofs
,
788 si
->strings_ofs
, si
->data_ofs
, si
->total_size
);
791 static void res_write_padding( BYTE
*res_base
, DWORD size
)
793 static const BYTE pad
[] = {
794 'P','A','D','D','I','N','G','X','X','P','A','D','D','I','N','G' };
797 for ( i
= 0; i
< size
/ sizeof pad
; i
++ )
798 memcpy( &res_base
[i
*sizeof pad
], pad
, sizeof pad
);
799 memcpy( &res_base
[i
*sizeof pad
], pad
, size
%sizeof pad
);
802 static BOOL
write_resources( QUEUEDUPDATES
*updates
, LPBYTE base
, struct resource_size_info
*si
, DWORD rva
)
804 struct resource_dir_entry
*types
, *names
;
805 struct resource_data
*data
;
806 IMAGE_RESOURCE_DIRECTORY
*root
;
808 TRACE("%p %p %p %08lx\n", updates
, base
, si
, rva
);
810 memset( base
, 0, si
->total_size
);
812 /* the root entry always exists */
813 root
= (IMAGE_RESOURCE_DIRECTORY
*) base
;
814 memset( root
, 0, sizeof *root
);
815 root
->MajorVersion
= 4;
816 si
->types_ofs
= sizeof *root
;
817 LIST_FOR_EACH_ENTRY( types
, &updates
->root
, struct resource_dir_entry
, entry
)
819 IMAGE_RESOURCE_DIRECTORY_ENTRY
*e1
;
820 IMAGE_RESOURCE_DIRECTORY
*namedir
;
822 e1
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*) &base
[si
->types_ofs
];
823 memset( e1
, 0, sizeof *e1
);
824 if (!IS_INTRESOURCE( types
->id
))
829 root
->NumberOfNamedEntries
++;
830 e1
->NameIsString
= 1;
831 e1
->NameOffset
= si
->strings_ofs
;
833 strings
= (WCHAR
*) &base
[si
->strings_ofs
];
834 len
= lstrlenW( types
->id
);
836 memcpy( &strings
[1], types
->id
, len
* sizeof (WCHAR
) );
837 si
->strings_ofs
+= (len
+ 1) * sizeof (WCHAR
);
841 root
->NumberOfIdEntries
++;
842 e1
->Id
= LOWORD( types
->id
);
844 e1
->OffsetToDirectory
= si
->names_ofs
;
845 e1
->DataIsDirectory
= TRUE
;
846 si
->types_ofs
+= sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY
);
848 namedir
= (IMAGE_RESOURCE_DIRECTORY
*) &base
[si
->names_ofs
];
849 memset( namedir
, 0, sizeof *namedir
);
850 namedir
->MajorVersion
= 4;
851 si
->names_ofs
+= sizeof (IMAGE_RESOURCE_DIRECTORY
);
853 LIST_FOR_EACH_ENTRY( names
, &types
->children
, struct resource_dir_entry
, entry
)
855 IMAGE_RESOURCE_DIRECTORY_ENTRY
*e2
;
856 IMAGE_RESOURCE_DIRECTORY
*langdir
;
858 e2
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*) &base
[si
->names_ofs
];
859 memset( e2
, 0, sizeof *e2
);
860 if (!IS_INTRESOURCE( names
->id
))
865 namedir
->NumberOfNamedEntries
++;
866 e2
->NameIsString
= 1;
867 e2
->NameOffset
= si
->strings_ofs
;
869 strings
= (WCHAR
*) &base
[si
->strings_ofs
];
870 len
= lstrlenW( names
->id
);
872 memcpy( &strings
[1], names
->id
, len
* sizeof (WCHAR
) );
873 si
->strings_ofs
+= (len
+ 1) * sizeof (WCHAR
);
877 namedir
->NumberOfIdEntries
++;
878 e2
->Id
= LOWORD( names
->id
);
880 e2
->OffsetToDirectory
= si
->langs_ofs
;
881 e2
->DataIsDirectory
= TRUE
;
882 si
->names_ofs
+= sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY
);
884 langdir
= (IMAGE_RESOURCE_DIRECTORY
*) &base
[si
->langs_ofs
];
885 memset( langdir
, 0, sizeof *langdir
);
886 langdir
->MajorVersion
= 4;
887 si
->langs_ofs
+= sizeof (IMAGE_RESOURCE_DIRECTORY
);
889 LIST_FOR_EACH_ENTRY( data
, &names
->children
, struct resource_data
, entry
)
891 IMAGE_RESOURCE_DIRECTORY_ENTRY
*e3
;
892 IMAGE_RESOURCE_DATA_ENTRY
*de
;
895 e3
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*) &base
[si
->langs_ofs
];
896 memset( e3
, 0, sizeof *e3
);
897 langdir
->NumberOfIdEntries
++;
898 e3
->Id
= LOWORD( data
->lang
);
899 e3
->OffsetToData
= si
->data_entry_ofs
;
901 si
->langs_ofs
+= sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY
);
903 /* write out all the data entries */
904 de
= (IMAGE_RESOURCE_DATA_ENTRY
*) &base
[si
->data_entry_ofs
];
905 memset( de
, 0, sizeof *de
);
906 de
->OffsetToData
= si
->data_ofs
+ rva
;
907 de
->Size
= data
->cbData
;
908 de
->CodePage
= data
->codepage
;
909 si
->data_entry_ofs
+= sizeof (IMAGE_RESOURCE_DATA_ENTRY
);
911 /* write out the resource data */
912 memcpy( &base
[si
->data_ofs
], data
->lpData
, data
->cbData
);
913 si
->data_ofs
+= data
->cbData
;
915 pad_size
= (-si
->data_ofs
)&3;
916 res_write_padding( &base
[si
->data_ofs
], pad_size
);
917 si
->data_ofs
+= pad_size
;
927 * Assumes that the resources are in .rsrc
928 * and .rsrc is the last section in the file.
929 * Not sure whether updating resources will other cases on Windows.
930 * If the resources lie in a section containing other data,
931 * resizing that section could possibly cause trouble.
932 * If the section with the resources isn't last, the remaining
933 * sections need to be moved down in the file, and the section header
934 * would need to be adjusted.
935 * If we needed to add a section, what would we name it?
936 * If we needed to add a section and there wasn't space in the file
937 * header, how would that work?
938 * Seems that at least some of these cases can't be handled properly.
940 static IMAGE_SECTION_HEADER
*get_resource_section( void *base
, DWORD mapping_size
)
942 IMAGE_SECTION_HEADER
*sec
;
943 IMAGE_NT_HEADERS
*nt
;
944 DWORD i
, num_sections
= 0;
946 nt
= get_nt_header( base
, mapping_size
);
950 sec
= get_section_header( base
, mapping_size
, &num_sections
);
954 /* find the resources section */
955 for (i
=0; i
<num_sections
; i
++)
956 if (!memcmp(sec
[i
].Name
, ".rsrc", 6))
959 if (i
== num_sections
)
965 static DWORD
get_init_data_size( void *base
, DWORD mapping_size
)
967 DWORD i
, sz
= 0, num_sections
= 0;
968 IMAGE_SECTION_HEADER
*s
;
970 s
= get_section_header( base
, mapping_size
, &num_sections
);
972 for (i
=0; i
<num_sections
; i
++)
973 if (s
[i
].Characteristics
& IMAGE_SCN_CNT_INITIALIZED_DATA
)
974 sz
+= s
[i
].SizeOfRawData
;
976 TRACE("size = %08lx\n", sz
);
981 static BOOL
write_raw_resources( QUEUEDUPDATES
*updates
)
983 WCHAR tempdir
[MAX_PATH
], tempfile
[MAX_PATH
];
984 DWORD i
, section_size
;
986 IMAGE_SECTION_HEADER
*sec
;
987 IMAGE_NT_HEADERS32
*nt
;
988 IMAGE_NT_HEADERS64
*nt64
;
989 struct resource_size_info res_size
;
991 struct mapping_info
*read_map
= NULL
, *write_map
= NULL
;
992 DWORD PeSectionAlignment
, PeFileAlignment
, PeSizeOfImage
;
994 /* copy the exe to a temp file then update the temp file... */
996 if (!GetTempPathW( MAX_PATH
, tempdir
))
999 if (!GetTempFileNameW( tempdir
, L
"resu", 0, tempfile
))
1002 if (!CopyFileW( updates
->pFileName
, tempfile
, FALSE
))
1005 TRACE("tempfile %s\n", debugstr_w(tempfile
));
1007 if (!updates
->bDeleteExistingResources
)
1009 read_map
= create_mapping( updates
->pFileName
, FALSE
);
1013 ret
= read_mapped_resources( updates
, read_map
->base
, read_map
->size
);
1016 ERR("failed to read existing resources\n");
1021 write_map
= create_mapping( tempfile
, TRUE
);
1025 nt
= (IMAGE_NT_HEADERS32
*)get_nt_header( write_map
->base
, write_map
->size
);
1029 nt64
= (IMAGE_NT_HEADERS64
*)nt
;
1030 if (nt
->OptionalHeader
.Magic
== IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
1031 PeSectionAlignment
= nt64
->OptionalHeader
.SectionAlignment
;
1032 PeFileAlignment
= nt64
->OptionalHeader
.FileAlignment
;
1033 PeSizeOfImage
= nt64
->OptionalHeader
.SizeOfImage
;
1035 PeSectionAlignment
= nt
->OptionalHeader
.SectionAlignment
;
1036 PeFileAlignment
= nt
->OptionalHeader
.FileAlignment
;
1037 PeSizeOfImage
= nt
->OptionalHeader
.SizeOfImage
;
1040 if ((LONG
)PeSectionAlignment
<= 0)
1042 ERR("invalid section alignment %08lx\n", PeSectionAlignment
);
1046 if ((LONG
)PeFileAlignment
<= 0)
1048 ERR("invalid file alignment %08lx\n", PeFileAlignment
);
1052 sec
= get_resource_section( write_map
->base
, write_map
->size
);
1053 if (!sec
) /* no section, add one */
1057 sec
= get_section_header( write_map
->base
, write_map
->size
, &num_sections
);
1061 sec
+= num_sections
;
1062 nt
->FileHeader
.NumberOfSections
++;
1064 memset( sec
, 0, sizeof *sec
);
1065 memcpy( sec
->Name
, ".rsrc", 5 );
1066 sec
->Characteristics
= IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_READ
;
1067 sec
->VirtualAddress
= PeSizeOfImage
;
1070 if (!sec
->PointerToRawData
) /* empty section */
1072 sec
->PointerToRawData
= write_map
->size
+ (-write_map
->size
) % PeFileAlignment
;
1073 sec
->SizeOfRawData
= 0;
1076 TRACE("before .rsrc at %08lx, size %08lx\n", sec
->PointerToRawData
, sec
->SizeOfRawData
);
1078 get_resource_sizes( updates
, &res_size
);
1080 /* round up the section size */
1081 section_size
= res_size
.total_size
;
1082 section_size
+= (-section_size
) % PeFileAlignment
;
1084 TRACE("requires %08lx (%08lx) bytes\n", res_size
.total_size
, section_size
);
1086 /* check if the file size needs to be changed */
1087 if (section_size
!= sec
->SizeOfRawData
)
1089 DWORD old_size
= write_map
->size
;
1090 DWORD virtual_section_size
= res_size
.total_size
+ (-res_size
.total_size
) % PeSectionAlignment
;
1091 int delta
= section_size
- (sec
->SizeOfRawData
+ (-sec
->SizeOfRawData
) % PeFileAlignment
);
1092 int rva_delta
= virtual_section_size
-
1093 (sec
->Misc
.VirtualSize
+ (-sec
->Misc
.VirtualSize
) % PeSectionAlignment
);
1094 /* when new section is added it could end past current mapping size */
1095 BOOL rsrc_is_last
= sec
->PointerToRawData
+ sec
->SizeOfRawData
>= old_size
;
1096 /* align .rsrc size when possible */
1097 DWORD mapping_size
= rsrc_is_last
? sec
->PointerToRawData
+ section_size
: old_size
+ delta
;
1099 /* postpone file truncation if there are some data to be moved down from file end */
1100 BOOL resize_after
= mapping_size
< old_size
&& !rsrc_is_last
;
1102 TRACE("file size %08lx -> %08lx\n", old_size
, mapping_size
);
1106 /* unmap the file before changing the file size */
1107 ret
= resize_mapping( write_map
, mapping_size
);
1109 /* get the pointers again - they might be different after remapping */
1110 nt
= (IMAGE_NT_HEADERS32
*)get_nt_header( write_map
->base
, mapping_size
);
1113 ERR("couldn't get NT header\n");
1116 nt64
= (IMAGE_NT_HEADERS64
*)nt
;
1118 sec
= get_resource_section( write_map
->base
, mapping_size
);
1123 if (!rsrc_is_last
) /* not last section, relocate trailing sections */
1125 IMAGE_SECTION_HEADER
*s
;
1126 DWORD tail_start
= sec
->PointerToRawData
+ sec
->SizeOfRawData
;
1127 DWORD i
, num_sections
= 0;
1129 memmove( (char*)write_map
->base
+ tail_start
+ delta
, (char*)write_map
->base
+ tail_start
, old_size
- tail_start
);
1131 s
= get_section_header( write_map
->base
, mapping_size
, &num_sections
);
1133 for (i
=0; i
<num_sections
; i
++)
1135 if (s
[i
].PointerToRawData
> sec
->PointerToRawData
)
1137 s
[i
].PointerToRawData
+= delta
;
1138 s
[i
].VirtualAddress
+= rva_delta
;
1145 ret
= resize_mapping( write_map
, mapping_size
);
1147 nt
= (IMAGE_NT_HEADERS32
*)get_nt_header( write_map
->base
, mapping_size
);
1150 ERR("couldn't get NT header\n");
1153 nt64
= (IMAGE_NT_HEADERS64
*)nt
;
1155 sec
= get_resource_section( write_map
->base
, mapping_size
);
1160 /* adjust the PE header information */
1161 sec
->SizeOfRawData
= section_size
;
1162 sec
->Misc
.VirtualSize
= virtual_section_size
;
1163 if (nt
->OptionalHeader
.Magic
== IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
1164 nt64
->OptionalHeader
.SizeOfImage
+= rva_delta
;
1165 nt64
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
= sec
->VirtualAddress
;
1166 nt64
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].Size
= res_size
.total_size
;
1167 nt64
->OptionalHeader
.SizeOfInitializedData
= get_init_data_size( write_map
->base
, mapping_size
);
1169 for (i
=0; i
<nt64
->OptionalHeader
.NumberOfRvaAndSizes
; i
++)
1170 if (nt64
->OptionalHeader
.DataDirectory
[i
].VirtualAddress
> sec
->VirtualAddress
)
1171 nt64
->OptionalHeader
.DataDirectory
[i
].VirtualAddress
+= rva_delta
;
1173 nt
->OptionalHeader
.SizeOfImage
+= rva_delta
;
1174 nt
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
= sec
->VirtualAddress
;
1175 nt
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].Size
= res_size
.total_size
;
1176 nt
->OptionalHeader
.SizeOfInitializedData
= get_init_data_size( write_map
->base
, mapping_size
);
1178 for (i
=0; i
<nt
->OptionalHeader
.NumberOfRvaAndSizes
; i
++)
1179 if (nt
->OptionalHeader
.DataDirectory
[i
].VirtualAddress
> sec
->VirtualAddress
)
1180 nt
->OptionalHeader
.DataDirectory
[i
].VirtualAddress
+= rva_delta
;
1184 res_base
= (LPBYTE
) write_map
->base
+ sec
->PointerToRawData
;
1186 TRACE("base = %p offset = %08lx\n", write_map
->base
, sec
->PointerToRawData
);
1188 ret
= write_resources( updates
, res_base
, &res_size
, sec
->VirtualAddress
);
1190 res_write_padding( res_base
+ res_size
.total_size
, section_size
- res_size
.total_size
);
1192 TRACE("after .rsrc at %08lx, size %08lx\n", sec
->PointerToRawData
, sec
->SizeOfRawData
);
1195 destroy_mapping( read_map
);
1196 destroy_mapping( write_map
);
1199 ret
= CopyFileW( tempfile
, updates
->pFileName
, FALSE
);
1201 DeleteFileW( tempfile
);
1206 /***********************************************************************
1207 * BeginUpdateResourceW (KERNEL32.@)
1209 HANDLE WINAPI
BeginUpdateResourceW( LPCWSTR pFileName
, BOOL bDeleteExistingResources
)
1211 QUEUEDUPDATES
*updates
= NULL
;
1212 HANDLE hUpdate
, file
, ret
= NULL
;
1214 TRACE("%s, %d\n", debugstr_w(pFileName
), bDeleteExistingResources
);
1216 hUpdate
= GlobalAlloc(GHND
, sizeof(QUEUEDUPDATES
));
1220 updates
= GlobalLock(hUpdate
);
1223 list_init( &updates
->root
);
1224 updates
->bDeleteExistingResources
= bDeleteExistingResources
;
1225 updates
->pFileName
= HeapAlloc(GetProcessHeap(), 0, (lstrlenW(pFileName
)+1)*sizeof(WCHAR
));
1226 if (updates
->pFileName
)
1228 lstrcpyW(updates
->pFileName
, pFileName
);
1230 file
= CreateFileW( pFileName
, GENERIC_READ
| GENERIC_WRITE
,
1231 0, NULL
, OPEN_EXISTING
, 0, 0 );
1233 /* if resources are deleted, only the file's presence is checked */
1234 if (file
!= INVALID_HANDLE_VALUE
&&
1235 (bDeleteExistingResources
|| check_pe_exe( file
, updates
)))
1238 HeapFree( GetProcessHeap(), 0, updates
->pFileName
);
1240 CloseHandle( file
);
1242 GlobalUnlock(hUpdate
);
1246 GlobalFree(hUpdate
);
1252 /***********************************************************************
1253 * BeginUpdateResourceA (KERNEL32.@)
1255 HANDLE WINAPI
BeginUpdateResourceA( LPCSTR pFileName
, BOOL bDeleteExistingResources
)
1257 UNICODE_STRING FileNameW
;
1259 RtlCreateUnicodeStringFromAsciiz(&FileNameW
, pFileName
);
1260 ret
= BeginUpdateResourceW(FileNameW
.Buffer
, bDeleteExistingResources
);
1261 RtlFreeUnicodeString(&FileNameW
);
1266 /***********************************************************************
1267 * EndUpdateResourceW (KERNEL32.@)
1269 BOOL WINAPI
EndUpdateResourceW( HANDLE hUpdate
, BOOL fDiscard
)
1271 QUEUEDUPDATES
*updates
;
1274 TRACE("%p %d\n", hUpdate
, fDiscard
);
1276 updates
= GlobalLock(hUpdate
);
1280 ret
= fDiscard
|| write_raw_resources( updates
);
1282 free_resource_directory( &updates
->root
, 2 );
1284 HeapFree( GetProcessHeap(), 0, updates
->pFileName
);
1285 GlobalUnlock( hUpdate
);
1286 GlobalFree( hUpdate
);
1292 /***********************************************************************
1293 * EndUpdateResourceA (KERNEL32.@)
1295 BOOL WINAPI
EndUpdateResourceA( HANDLE hUpdate
, BOOL fDiscard
)
1297 return EndUpdateResourceW(hUpdate
, fDiscard
);
1301 /***********************************************************************
1302 * UpdateResourceW (KERNEL32.@)
1304 BOOL WINAPI
UpdateResourceW( HANDLE hUpdate
, LPCWSTR lpType
, LPCWSTR lpName
,
1305 WORD wLanguage
, LPVOID lpData
, DWORD cbData
)
1307 QUEUEDUPDATES
*updates
;
1308 UNICODE_STRING nameW
, typeW
;
1311 TRACE("%p %s %s %08x %p %ld\n", hUpdate
,
1312 debugstr_w(lpType
), debugstr_w(lpName
), wLanguage
, lpData
, cbData
);
1314 nameW
.Buffer
= typeW
.Buffer
= NULL
;
1315 updates
= GlobalLock(hUpdate
);
1318 if (!set_ntstatus( get_res_nameW( lpName
, &nameW
))) goto done
;
1319 if (!set_ntstatus( get_res_nameW( lpType
, &typeW
))) goto done
;
1321 if (lpData
== NULL
&& cbData
== 0) /* remove resource */
1323 ret
= update_add_resource( updates
, typeW
.Buffer
, nameW
.Buffer
, wLanguage
, NULL
, TRUE
);
1327 struct resource_data
*data
;
1328 data
= allocate_resource_data( wLanguage
, 0, lpData
, cbData
, TRUE
);
1330 ret
= update_add_resource( updates
, typeW
.Buffer
, nameW
.Buffer
, wLanguage
, data
, TRUE
);
1334 GlobalUnlock(hUpdate
);
1337 if (!IS_INTRESOURCE(nameW
.Buffer
)) HeapFree( GetProcessHeap(), 0, nameW
.Buffer
);
1338 if (!IS_INTRESOURCE(typeW
.Buffer
)) HeapFree( GetProcessHeap(), 0, typeW
.Buffer
);
1343 /***********************************************************************
1344 * UpdateResourceA (KERNEL32.@)
1346 BOOL WINAPI
UpdateResourceA( HANDLE hUpdate
, LPCSTR lpType
, LPCSTR lpName
,
1347 WORD wLanguage
, LPVOID lpData
, DWORD cbData
)
1350 UNICODE_STRING TypeW
;
1351 UNICODE_STRING NameW
;
1352 if(IS_INTRESOURCE(lpType
))
1353 TypeW
.Buffer
= ULongToPtr(LOWORD(lpType
));
1355 RtlCreateUnicodeStringFromAsciiz(&TypeW
, lpType
);
1356 if(IS_INTRESOURCE(lpName
))
1357 NameW
.Buffer
= ULongToPtr(LOWORD(lpName
));
1359 RtlCreateUnicodeStringFromAsciiz(&NameW
, lpName
);
1360 ret
= UpdateResourceW(hUpdate
, TypeW
.Buffer
, NameW
.Buffer
, wLanguage
, lpData
, cbData
);
1361 if(!IS_INTRESOURCE(lpType
)) RtlFreeUnicodeString(&TypeW
);
1362 if(!IS_INTRESOURCE(lpName
)) RtlFreeUnicodeString(&NameW
);