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
25 #define NONAMELESSUNION
26 #define NONAMELESSSTRUCT
28 #define WIN32_NO_STATUS
32 #include "wine/debug.h"
33 #include "wine/exception.h"
34 #include "wine/list.h"
35 #include "kernel_private.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(resource
);
39 /* we don't want to include winuser.h just for this */
40 #define IS_INTRESOURCE(x) (((ULONG_PTR)(x) >> 16) == 0)
42 /* retrieve the resource name to pass to the ntdll functions */
43 static NTSTATUS
get_res_nameA( LPCSTR name
, UNICODE_STRING
*str
)
45 if (IS_INTRESOURCE(name
))
47 str
->Buffer
= ULongToPtr(LOWORD(name
));
48 return STATUS_SUCCESS
;
53 if (RtlCharToInteger( name
+ 1, 10, &value
) != STATUS_SUCCESS
|| HIWORD(value
))
54 return STATUS_INVALID_PARAMETER
;
55 str
->Buffer
= ULongToPtr(value
);
56 return STATUS_SUCCESS
;
58 RtlCreateUnicodeStringFromAsciiz( str
, name
);
59 return STATUS_SUCCESS
;
62 static NTSTATUS
get_res_nameW( LPCWSTR name
, UNICODE_STRING
*str
)
64 if (IS_INTRESOURCE(name
))
66 str
->Buffer
= ULongToPtr( LOWORD(name
) );
67 return STATUS_SUCCESS
;
72 RtlInitUnicodeString( str
, name
+ 1 );
73 if (RtlUnicodeStringToInteger( str
, 10, &value
) != STATUS_SUCCESS
|| HIWORD(value
))
74 return STATUS_INVALID_PARAMETER
;
75 str
->Buffer
= ULongToPtr(value
);
76 return STATUS_SUCCESS
;
78 RtlCreateUnicodeString( str
, name
);
79 RtlUpcaseUnicodeString( str
, str
, FALSE
);
80 return STATUS_SUCCESS
;
83 /**********************************************************************
84 * FindResourceExA (KERNEL32.@)
86 HRSRC WINAPI
FindResourceExA( HMODULE module
, LPCSTR type
, LPCSTR name
, WORD lang
)
89 UNICODE_STRING nameW
, typeW
;
92 TRACE( "%p %s %s %04x\n", module
, debugstr_a(type
), debugstr_a(name
), lang
);
94 if (!module
) module
= GetModuleHandleW(0);
100 if (!(status
= get_res_nameA( name
, &nameW
)) && !(status
= get_res_nameA( type
, &typeW
)))
101 ret
= FindResourceExW( module
, typeW
.Buffer
, nameW
.Buffer
, lang
);
103 SetLastError( RtlNtStatusToDosError(status
) );
107 SetLastError( ERROR_INVALID_PARAMETER
);
111 if (!IS_INTRESOURCE(nameW
.Buffer
)) HeapFree( GetProcessHeap(), 0, nameW
.Buffer
);
112 if (!IS_INTRESOURCE(typeW
.Buffer
)) HeapFree( GetProcessHeap(), 0, typeW
.Buffer
);
117 /**********************************************************************
118 * FindResourceA (KERNEL32.@)
120 HRSRC WINAPI
FindResourceA( HMODULE hModule
, LPCSTR name
, LPCSTR type
)
122 return FindResourceExA( hModule
, type
, name
, MAKELANGID( LANG_NEUTRAL
, SUBLANG_NEUTRAL
) );
126 /**********************************************************************
127 * EnumResourceTypesA (KERNEL32.@)
129 BOOL WINAPI
EnumResourceTypesA( HMODULE hmod
, ENUMRESTYPEPROCA lpfun
, LONG_PTR lparam
)
131 return EnumResourceTypesExA( hmod
, lpfun
, lparam
, 0, 0 );
135 /**********************************************************************
136 * EnumResourceTypesW (KERNEL32.@)
138 BOOL WINAPI
EnumResourceTypesW( HMODULE hmod
, ENUMRESTYPEPROCW lpfun
, LONG_PTR lparam
)
140 return EnumResourceTypesExW( hmod
, lpfun
, lparam
, 0, 0 );
144 /**********************************************************************
145 * EnumResourceNamesA (KERNEL32.@)
147 BOOL WINAPI
EnumResourceNamesA( HMODULE hmod
, LPCSTR type
, ENUMRESNAMEPROCA lpfun
, LONG_PTR lparam
)
149 return EnumResourceNamesExA( hmod
, type
, lpfun
, lparam
, 0, 0 );
153 /**********************************************************************
154 * EnumResourceLanguagesA (KERNEL32.@)
156 BOOL WINAPI
EnumResourceLanguagesA( HMODULE hmod
, LPCSTR type
, LPCSTR name
,
157 ENUMRESLANGPROCA lpfun
, LONG_PTR lparam
)
159 return EnumResourceLanguagesExA( hmod
, type
, name
, lpfun
, lparam
, 0, 0 );
163 /**********************************************************************
164 * EnumResourceLanguagesW (KERNEL32.@)
166 BOOL WINAPI
EnumResourceLanguagesW( HMODULE hmod
, LPCWSTR type
, LPCWSTR name
,
167 ENUMRESLANGPROCW lpfun
, LONG_PTR lparam
)
169 return EnumResourceLanguagesExW( hmod
, type
, name
, lpfun
, lparam
, 0, 0 );
174 * Data structure for updating resources.
175 * Type/Name/Language is a keyset for accessing resource data.
177 * QUEUEDUPDATES (root) ->
178 * list of struct resource_dir_entry (Type) ->
179 * list of struct resource_dir_entry (Name) ->
180 * list of struct resource_data Language + Data
187 BOOL bDeleteExistingResources
;
191 /* this structure is shared for types and names */
192 struct resource_dir_entry
{
195 struct list children
;
198 /* this structure is the leaf */
199 struct resource_data
{
207 static int resource_strcmp( LPCWSTR a
, LPCWSTR b
)
211 if (!IS_INTRESOURCE( a
) && !IS_INTRESOURCE( b
) )
212 return wcscmp( a
, b
);
213 /* strings come before ids */
214 if (!IS_INTRESOURCE( a
) && IS_INTRESOURCE( b
))
216 if (!IS_INTRESOURCE( b
) && IS_INTRESOURCE( a
))
218 return ( a
< b
) ? -1 : 1;
221 static struct resource_dir_entry
*find_resource_dir_entry( struct list
*dir
, LPCWSTR id
)
223 struct resource_dir_entry
*ent
;
225 /* match either IDs or strings */
226 LIST_FOR_EACH_ENTRY( ent
, dir
, struct resource_dir_entry
, entry
)
227 if (!resource_strcmp( id
, ent
->id
))
233 static struct resource_data
*find_resource_data( struct list
*dir
, LANGID lang
)
235 struct resource_data
*res_data
;
237 /* match only languages here */
238 LIST_FOR_EACH_ENTRY( res_data
, dir
, struct resource_data
, entry
)
239 if ( lang
== res_data
->lang
)
245 static void add_resource_dir_entry( struct list
*dir
, struct resource_dir_entry
*resdir
)
247 struct resource_dir_entry
*ent
;
249 LIST_FOR_EACH_ENTRY( ent
, dir
, struct resource_dir_entry
, entry
)
251 if (0>resource_strcmp( ent
->id
, resdir
->id
))
254 list_add_before( &ent
->entry
, &resdir
->entry
);
257 list_add_tail( dir
, &resdir
->entry
);
260 static void add_resource_data_entry( struct list
*dir
, struct resource_data
*resdata
)
262 struct resource_data
*ent
;
264 LIST_FOR_EACH_ENTRY( ent
, dir
, struct resource_data
, entry
)
266 if (ent
->lang
< resdata
->lang
)
269 list_add_before( &ent
->entry
, &resdata
->entry
);
272 list_add_tail( dir
, &resdata
->entry
);
275 static LPWSTR
res_strdupW( LPCWSTR str
)
280 if (IS_INTRESOURCE(str
))
281 return (LPWSTR
) (UINT_PTR
) LOWORD(str
);
282 len
= (lstrlenW( str
) + 1) * sizeof (WCHAR
);
283 ret
= HeapAlloc( GetProcessHeap(), 0, len
);
284 memcpy( ret
, str
, len
);
288 static void res_free_str( LPWSTR str
)
290 if (!IS_INTRESOURCE(str
))
291 HeapFree( GetProcessHeap(), 0, str
);
294 static BOOL
update_add_resource( QUEUEDUPDATES
*updates
, LPCWSTR Type
, LPCWSTR Name
,
295 LANGID Lang
, struct resource_data
*resdata
,
296 BOOL overwrite_existing
)
298 struct resource_dir_entry
*restype
, *resname
;
299 struct resource_data
*existing
;
301 TRACE("%p %s %s %p %d\n", updates
,
302 debugstr_w(Type
), debugstr_w(Name
), resdata
, overwrite_existing
);
304 restype
= find_resource_dir_entry( &updates
->root
, Type
);
307 restype
= HeapAlloc( GetProcessHeap(), 0, sizeof *restype
);
308 restype
->id
= res_strdupW( Type
);
309 list_init( &restype
->children
);
310 add_resource_dir_entry( &updates
->root
, restype
);
313 resname
= find_resource_dir_entry( &restype
->children
, Name
);
316 resname
= HeapAlloc( GetProcessHeap(), 0, sizeof *resname
);
317 resname
->id
= res_strdupW( Name
);
318 list_init( &resname
->children
);
319 add_resource_dir_entry( &restype
->children
, resname
);
323 * If there's an existing resource entry with matching (Type,Name,Language)
324 * it needs to be removed before adding the new data.
326 existing
= find_resource_data( &resname
->children
, Lang
);
329 if (!overwrite_existing
)
331 list_remove( &existing
->entry
);
332 HeapFree( GetProcessHeap(), 0, existing
);
336 add_resource_data_entry( &resname
->children
, resdata
);
341 static struct resource_data
*allocate_resource_data( WORD Language
, DWORD codepage
,
342 LPVOID lpData
, DWORD cbData
, BOOL copy_data
)
344 struct resource_data
*resdata
;
346 if (!lpData
|| !cbData
)
349 resdata
= HeapAlloc( GetProcessHeap(), 0, sizeof *resdata
+ (copy_data
? cbData
: 0) );
352 resdata
->lang
= Language
;
353 resdata
->codepage
= codepage
;
354 resdata
->cbData
= cbData
;
357 resdata
->lpData
= &resdata
[1];
358 memcpy( resdata
->lpData
, lpData
, cbData
);
361 resdata
->lpData
= lpData
;
367 static void free_resource_directory( struct list
*head
, int level
)
369 struct list
*ptr
= NULL
;
371 while ((ptr
= list_head( head
)))
376 struct resource_dir_entry
*ent
;
378 ent
= LIST_ENTRY( ptr
, struct resource_dir_entry
, entry
);
379 res_free_str( ent
->id
);
380 free_resource_directory( &ent
->children
, level
- 1 );
381 HeapFree(GetProcessHeap(), 0, ent
);
385 struct resource_data
*data
;
387 data
= LIST_ENTRY( ptr
, struct resource_data
, entry
);
388 HeapFree( GetProcessHeap(), 0, data
);
393 static IMAGE_NT_HEADERS
*get_nt_header( void *base
, DWORD mapping_size
)
395 IMAGE_NT_HEADERS
*nt
;
396 IMAGE_DOS_HEADER
*dos
;
398 if (mapping_size
<sizeof (*dos
))
402 if (dos
->e_magic
!= IMAGE_DOS_SIGNATURE
)
405 if ((dos
->e_lfanew
+ sizeof (*nt
)) > mapping_size
)
408 nt
= (void*) ((BYTE
*)base
+ dos
->e_lfanew
);
410 if (nt
->Signature
!= IMAGE_NT_SIGNATURE
)
416 static IMAGE_SECTION_HEADER
*get_section_header( void *base
, DWORD mapping_size
, DWORD
*num_sections
)
418 IMAGE_NT_HEADERS
*nt
;
421 nt
= get_nt_header( base
, mapping_size
);
425 /* check that we don't go over the end of the file accessing the sections */
426 section_ofs
= FIELD_OFFSET(IMAGE_NT_HEADERS
, OptionalHeader
) + nt
->FileHeader
.SizeOfOptionalHeader
;
427 if ((nt
->FileHeader
.NumberOfSections
* sizeof (IMAGE_SECTION_HEADER
) + section_ofs
) > mapping_size
)
431 *num_sections
= nt
->FileHeader
.NumberOfSections
;
433 /* from here we have a valid PE exe to update */
434 return (void*) ((BYTE
*)nt
+ section_ofs
);
437 static BOOL
check_pe_exe( HANDLE file
, QUEUEDUPDATES
*updates
)
439 const IMAGE_NT_HEADERS32
*nt
;
440 const IMAGE_NT_HEADERS64
*nt64
;
441 const IMAGE_SECTION_HEADER
*sec
;
442 const IMAGE_DATA_DIRECTORY
*dd
;
445 DWORD mapping_size
, num_sections
= 0;
448 mapping_size
= GetFileSize( file
, NULL
);
450 mapping
= CreateFileMappingW( file
, NULL
, PAGE_READONLY
, 0, 0, NULL
);
454 base
= MapViewOfFile( mapping
, FILE_MAP_READ
, 0, 0, mapping_size
);
458 nt
= (IMAGE_NT_HEADERS32
*)get_nt_header( base
, mapping_size
);
462 nt64
= (IMAGE_NT_HEADERS64
*)nt
;
463 dd
= &nt
->OptionalHeader
.DataDirectory
[0];
464 if (nt
->OptionalHeader
.Magic
== IMAGE_NT_OPTIONAL_HDR64_MAGIC
)
465 dd
= &nt64
->OptionalHeader
.DataDirectory
[0];
467 TRACE("resources: %08x %08x\n",
468 dd
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
,
469 dd
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].Size
);
471 sec
= get_section_header( base
, mapping_size
, &num_sections
);
479 UnmapViewOfFile( base
);
481 CloseHandle( mapping
);
486 struct resource_size_info
{
490 DWORD data_entry_ofs
;
496 struct mapping_info
{
503 static const IMAGE_SECTION_HEADER
*section_from_rva( void *base
, DWORD mapping_size
, DWORD rva
)
505 const IMAGE_SECTION_HEADER
*sec
;
506 DWORD num_sections
= 0;
509 sec
= get_section_header( base
, mapping_size
, &num_sections
);
513 for (i
=num_sections
-1; i
>=0; i
--)
515 if (sec
[i
].VirtualAddress
<= rva
&&
516 rva
<= (DWORD
)sec
[i
].VirtualAddress
+ sec
[i
].SizeOfRawData
)
525 static void *address_from_rva( void *base
, DWORD mapping_size
, DWORD rva
, DWORD len
)
527 const IMAGE_SECTION_HEADER
*sec
;
529 sec
= section_from_rva( base
, mapping_size
, rva
);
533 if (rva
+ len
<= (DWORD
)sec
->VirtualAddress
+ sec
->SizeOfRawData
)
534 return (void*)((LPBYTE
) base
+ (sec
->PointerToRawData
+ rva
- sec
->VirtualAddress
));
539 static LPWSTR
resource_dup_string( const IMAGE_RESOURCE_DIRECTORY
*root
, const IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
)
541 const IMAGE_RESOURCE_DIR_STRING_U
* string
;
544 if (!entry
->u
.s
.NameIsString
)
545 return UIntToPtr(entry
->u
.Id
);
547 string
= (const IMAGE_RESOURCE_DIR_STRING_U
*) (((const char *)root
) + entry
->u
.s
.NameOffset
);
548 s
= HeapAlloc(GetProcessHeap(), 0, (string
->Length
+ 1)*sizeof (WCHAR
) );
549 memcpy( s
, string
->NameString
, (string
->Length
+ 1)*sizeof (WCHAR
) );
550 s
[string
->Length
] = 0;
555 /* this function is based on the code in winedump's pe.c */
556 static BOOL
enumerate_mapped_resources( QUEUEDUPDATES
*updates
,
557 void *base
, DWORD mapping_size
,
558 const IMAGE_RESOURCE_DIRECTORY
*root
)
560 const IMAGE_RESOURCE_DIRECTORY
*namedir
, *langdir
;
561 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*e1
, *e2
, *e3
;
562 const IMAGE_RESOURCE_DATA_ENTRY
*data
;
565 TRACE("version (%d.%d) %d named %d id entries\n",
566 root
->MajorVersion
, root
->MinorVersion
, root
->NumberOfNamedEntries
, root
->NumberOfIdEntries
);
568 for (i
= 0; i
< root
->NumberOfNamedEntries
+ root
->NumberOfIdEntries
; i
++)
572 e1
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(root
+ 1) + i
;
574 Type
= resource_dup_string( root
, e1
);
576 namedir
= (const IMAGE_RESOURCE_DIRECTORY
*)((const char *)root
+ e1
->u2
.s2
.OffsetToDirectory
);
577 for (j
= 0; j
< namedir
->NumberOfNamedEntries
+ namedir
->NumberOfIdEntries
; j
++)
581 e2
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(namedir
+ 1) + j
;
583 Name
= resource_dup_string( root
, e2
);
585 langdir
= (const IMAGE_RESOURCE_DIRECTORY
*)((const char *)root
+ e2
->u2
.s2
.OffsetToDirectory
);
586 for (k
= 0; k
< langdir
->NumberOfNamedEntries
+ langdir
->NumberOfIdEntries
; k
++)
590 struct resource_data
*resdata
;
592 e3
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(langdir
+ 1) + k
;
596 data
= (const IMAGE_RESOURCE_DATA_ENTRY
*)((const char *)root
+ e3
->u2
.OffsetToData
);
598 p
= address_from_rva( base
, mapping_size
, data
->OffsetToData
, data
->Size
);
600 resdata
= allocate_resource_data( Lang
, data
->CodePage
, p
, data
->Size
, FALSE
);
603 if (!update_add_resource( updates
, Type
, Name
, Lang
, resdata
, FALSE
))
604 HeapFree( GetProcessHeap(), 0, resdata
);
607 res_free_str( Name
);
609 res_free_str( Type
);
615 static BOOL
read_mapped_resources( QUEUEDUPDATES
*updates
, void *base
, DWORD mapping_size
)
617 const IMAGE_RESOURCE_DIRECTORY
*root
;
618 const IMAGE_NT_HEADERS
*nt
;
619 const IMAGE_SECTION_HEADER
*sec
;
620 DWORD num_sections
= 0, i
;
622 nt
= get_nt_header( base
, mapping_size
);
626 sec
= get_section_header( base
, mapping_size
, &num_sections
);
630 for (i
=0; i
<num_sections
; i
++)
631 if (!memcmp(sec
[i
].Name
, ".rsrc", 6))
634 if (i
== num_sections
)
637 /* check the resource data is inside the mapping */
638 if (sec
[i
].PointerToRawData
> mapping_size
||
639 (sec
[i
].PointerToRawData
+ sec
[i
].SizeOfRawData
) > mapping_size
)
642 TRACE("found .rsrc at %08x, size %08x\n", sec
[i
].PointerToRawData
, sec
[i
].SizeOfRawData
);
644 if (!sec
[i
].PointerToRawData
|| sec
[i
].SizeOfRawData
< sizeof(IMAGE_RESOURCE_DIRECTORY
))
647 root
= (void*) ((BYTE
*)base
+ sec
[i
].PointerToRawData
);
648 enumerate_mapped_resources( updates
, base
, mapping_size
, root
);
653 static BOOL
map_file_into_memory( struct mapping_info
*mi
)
655 DWORD page_attr
, perm
;
660 page_attr
= PAGE_READWRITE
;
661 perm
= FILE_MAP_WRITE
| FILE_MAP_READ
;
665 page_attr
= PAGE_READONLY
;
666 perm
= FILE_MAP_READ
;
669 mapping
= CreateFileMappingW( mi
->file
, NULL
, page_attr
, 0, 0, NULL
);
670 if (!mapping
) return FALSE
;
672 mi
->base
= MapViewOfFile( mapping
, perm
, 0, 0, mi
->size
);
673 CloseHandle( mapping
);
675 return mi
->base
!= NULL
;
678 static BOOL
unmap_file_from_memory( struct mapping_info
*mi
)
681 UnmapViewOfFile( mi
->base
);
686 static void destroy_mapping( struct mapping_info
*mi
)
690 unmap_file_from_memory( mi
);
692 CloseHandle( mi
->file
);
693 HeapFree( GetProcessHeap(), 0, mi
);
696 static struct mapping_info
*create_mapping( LPCWSTR name
, BOOL rw
)
698 struct mapping_info
*mi
;
700 mi
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof *mi
);
706 mi
->file
= CreateFileW( name
, GENERIC_READ
| (rw
? GENERIC_WRITE
: 0),
707 0, NULL
, OPEN_EXISTING
, 0, 0 );
709 if (mi
->file
!= INVALID_HANDLE_VALUE
)
711 mi
->size
= GetFileSize( mi
->file
, NULL
);
713 if (map_file_into_memory( mi
))
716 destroy_mapping( mi
);
720 static BOOL
resize_mapping( struct mapping_info
*mi
, DWORD new_size
)
722 if (!unmap_file_from_memory( mi
))
725 /* change the file size */
726 SetFilePointer( mi
->file
, new_size
, NULL
, FILE_BEGIN
);
727 if (!SetEndOfFile( mi
->file
))
729 ERR("failed to set file size to %08x\n", new_size
);
735 return map_file_into_memory( mi
);
738 static void get_resource_sizes( QUEUEDUPDATES
*updates
, struct resource_size_info
*si
)
740 struct resource_dir_entry
*types
, *names
;
741 struct resource_data
*data
;
742 DWORD num_types
= 0, num_names
= 0, num_langs
= 0, strings_size
= 0, data_size
= 0;
744 memset( si
, 0, sizeof *si
);
746 LIST_FOR_EACH_ENTRY( types
, &updates
->root
, struct resource_dir_entry
, entry
)
749 if (!IS_INTRESOURCE( types
->id
))
750 strings_size
+= sizeof (WORD
) + lstrlenW( types
->id
)*sizeof (WCHAR
);
752 LIST_FOR_EACH_ENTRY( names
, &types
->children
, struct resource_dir_entry
, entry
)
756 if (!IS_INTRESOURCE( names
->id
))
757 strings_size
+= sizeof (WORD
) + lstrlenW( names
->id
)*sizeof (WCHAR
);
759 LIST_FOR_EACH_ENTRY( data
, &names
->children
, struct resource_data
, entry
)
762 data_size
+= (data
->cbData
+ 3) & ~3;
767 /* names are at the end of the types */
768 si
->names_ofs
= sizeof (IMAGE_RESOURCE_DIRECTORY
) +
769 num_types
* sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY
);
771 /* language directories are at the end of the names */
772 si
->langs_ofs
= si
->names_ofs
+
773 num_types
* sizeof (IMAGE_RESOURCE_DIRECTORY
) +
774 num_names
* sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY
);
776 si
->data_entry_ofs
= si
->langs_ofs
+
777 num_names
* sizeof (IMAGE_RESOURCE_DIRECTORY
) +
778 num_langs
* sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY
);
780 si
->strings_ofs
= si
->data_entry_ofs
+
781 num_langs
* sizeof (IMAGE_RESOURCE_DATA_ENTRY
);
783 si
->data_ofs
= si
->strings_ofs
+ ((strings_size
+ 3) & ~3);
785 si
->total_size
= si
->data_ofs
+ data_size
;
787 TRACE("names %08x langs %08x data entries %08x strings %08x data %08x total %08x\n",
788 si
->names_ofs
, si
->langs_ofs
, si
->data_entry_ofs
,
789 si
->strings_ofs
, si
->data_ofs
, si
->total_size
);
792 static void res_write_padding( BYTE
*res_base
, DWORD size
)
794 static const BYTE pad
[] = {
795 'P','A','D','D','I','N','G','X','X','P','A','D','D','I','N','G' };
798 for ( i
= 0; i
< size
/ sizeof pad
; i
++ )
799 memcpy( &res_base
[i
*sizeof pad
], pad
, sizeof pad
);
800 memcpy( &res_base
[i
*sizeof pad
], pad
, size
%sizeof pad
);
803 static BOOL
write_resources( QUEUEDUPDATES
*updates
, LPBYTE base
, struct resource_size_info
*si
, DWORD rva
)
805 struct resource_dir_entry
*types
, *names
;
806 struct resource_data
*data
;
807 IMAGE_RESOURCE_DIRECTORY
*root
;
809 TRACE("%p %p %p %08x\n", updates
, base
, si
, rva
);
811 memset( base
, 0, si
->total_size
);
813 /* the root entry always exists */
814 root
= (IMAGE_RESOURCE_DIRECTORY
*) base
;
815 memset( root
, 0, sizeof *root
);
816 root
->MajorVersion
= 4;
817 si
->types_ofs
= sizeof *root
;
818 LIST_FOR_EACH_ENTRY( types
, &updates
->root
, struct resource_dir_entry
, entry
)
820 IMAGE_RESOURCE_DIRECTORY_ENTRY
*e1
;
821 IMAGE_RESOURCE_DIRECTORY
*namedir
;
823 e1
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*) &base
[si
->types_ofs
];
824 memset( e1
, 0, sizeof *e1
);
825 if (!IS_INTRESOURCE( types
->id
))
830 root
->NumberOfNamedEntries
++;
831 e1
->u
.s
.NameIsString
= 1;
832 e1
->u
.s
.NameOffset
= si
->strings_ofs
;
834 strings
= (WCHAR
*) &base
[si
->strings_ofs
];
835 len
= lstrlenW( types
->id
);
837 memcpy( &strings
[1], types
->id
, len
* sizeof (WCHAR
) );
838 si
->strings_ofs
+= (len
+ 1) * sizeof (WCHAR
);
842 root
->NumberOfIdEntries
++;
843 e1
->u
.Id
= LOWORD( types
->id
);
845 e1
->u2
.s2
.OffsetToDirectory
= si
->names_ofs
;
846 e1
->u2
.s2
.DataIsDirectory
= TRUE
;
847 si
->types_ofs
+= sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY
);
849 namedir
= (IMAGE_RESOURCE_DIRECTORY
*) &base
[si
->names_ofs
];
850 memset( namedir
, 0, sizeof *namedir
);
851 namedir
->MajorVersion
= 4;
852 si
->names_ofs
+= sizeof (IMAGE_RESOURCE_DIRECTORY
);
854 LIST_FOR_EACH_ENTRY( names
, &types
->children
, struct resource_dir_entry
, entry
)
856 IMAGE_RESOURCE_DIRECTORY_ENTRY
*e2
;
857 IMAGE_RESOURCE_DIRECTORY
*langdir
;
859 e2
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*) &base
[si
->names_ofs
];
860 memset( e2
, 0, sizeof *e2
);
861 if (!IS_INTRESOURCE( names
->id
))
866 namedir
->NumberOfNamedEntries
++;
867 e2
->u
.s
.NameIsString
= 1;
868 e2
->u
.s
.NameOffset
= si
->strings_ofs
;
870 strings
= (WCHAR
*) &base
[si
->strings_ofs
];
871 len
= lstrlenW( names
->id
);
873 memcpy( &strings
[1], names
->id
, len
* sizeof (WCHAR
) );
874 si
->strings_ofs
+= (len
+ 1) * sizeof (WCHAR
);
878 namedir
->NumberOfIdEntries
++;
879 e2
->u
.Id
= LOWORD( names
->id
);
881 e2
->u2
.s2
.OffsetToDirectory
= si
->langs_ofs
;
882 e2
->u2
.s2
.DataIsDirectory
= TRUE
;
883 si
->names_ofs
+= sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY
);
885 langdir
= (IMAGE_RESOURCE_DIRECTORY
*) &base
[si
->langs_ofs
];
886 memset( langdir
, 0, sizeof *langdir
);
887 langdir
->MajorVersion
= 4;
888 si
->langs_ofs
+= sizeof (IMAGE_RESOURCE_DIRECTORY
);
890 LIST_FOR_EACH_ENTRY( data
, &names
->children
, struct resource_data
, entry
)
892 IMAGE_RESOURCE_DIRECTORY_ENTRY
*e3
;
893 IMAGE_RESOURCE_DATA_ENTRY
*de
;
896 e3
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*) &base
[si
->langs_ofs
];
897 memset( e3
, 0, sizeof *e3
);
898 langdir
->NumberOfIdEntries
++;
899 e3
->u
.Id
= LOWORD( data
->lang
);
900 e3
->u2
.OffsetToData
= si
->data_entry_ofs
;
902 si
->langs_ofs
+= sizeof (IMAGE_RESOURCE_DIRECTORY_ENTRY
);
904 /* write out all the data entries */
905 de
= (IMAGE_RESOURCE_DATA_ENTRY
*) &base
[si
->data_entry_ofs
];
906 memset( de
, 0, sizeof *de
);
907 de
->OffsetToData
= si
->data_ofs
+ rva
;
908 de
->Size
= data
->cbData
;
909 de
->CodePage
= data
->codepage
;
910 si
->data_entry_ofs
+= sizeof (IMAGE_RESOURCE_DATA_ENTRY
);
912 /* write out the resource data */
913 memcpy( &base
[si
->data_ofs
], data
->lpData
, data
->cbData
);
914 si
->data_ofs
+= data
->cbData
;
916 pad_size
= (-si
->data_ofs
)&3;
917 res_write_padding( &base
[si
->data_ofs
], pad_size
);
918 si
->data_ofs
+= pad_size
;
928 * Assumes that the resources are in .rsrc
929 * and .rsrc is the last section in the file.
930 * Not sure whether updating resources will other cases on Windows.
931 * If the resources lie in a section containing other data,
932 * resizing that section could possibly cause trouble.
933 * If the section with the resources isn't last, the remaining
934 * sections need to be moved down in the file, and the section header
935 * would need to be adjusted.
936 * If we needed to add a section, what would we name it?
937 * If we needed to add a section and there wasn't space in the file
938 * header, how would that work?
939 * Seems that at least some of these cases can't be handled properly.
941 static IMAGE_SECTION_HEADER
*get_resource_section( void *base
, DWORD mapping_size
)
943 IMAGE_SECTION_HEADER
*sec
;
944 IMAGE_NT_HEADERS
*nt
;
945 DWORD i
, num_sections
= 0;
947 nt
= get_nt_header( base
, mapping_size
);
951 sec
= get_section_header( base
, mapping_size
, &num_sections
);
955 /* find the resources section */
956 for (i
=0; i
<num_sections
; i
++)
957 if (!memcmp(sec
[i
].Name
, ".rsrc", 6))
960 if (i
== num_sections
)
966 static DWORD
get_init_data_size( void *base
, DWORD mapping_size
)
968 DWORD i
, sz
= 0, num_sections
= 0;
969 IMAGE_SECTION_HEADER
*s
;
971 s
= get_section_header( base
, mapping_size
, &num_sections
);
973 for (i
=0; i
<num_sections
; i
++)
974 if (s
[i
].Characteristics
& IMAGE_SCN_CNT_INITIALIZED_DATA
)
975 sz
+= s
[i
].SizeOfRawData
;
977 TRACE("size = %08x\n", sz
);
982 static BOOL
write_raw_resources( QUEUEDUPDATES
*updates
)
984 WCHAR tempdir
[MAX_PATH
], tempfile
[MAX_PATH
];
985 DWORD i
, section_size
;
987 IMAGE_SECTION_HEADER
*sec
;
988 IMAGE_NT_HEADERS32
*nt
;
989 IMAGE_NT_HEADERS64
*nt64
;
990 struct resource_size_info res_size
;
992 struct mapping_info
*read_map
= NULL
, *write_map
= NULL
;
993 DWORD PeSectionAlignment
, PeFileAlignment
, PeSizeOfImage
;
995 /* copy the exe to a temp file then update the temp file... */
997 if (!GetTempPathW( MAX_PATH
, tempdir
))
1000 if (!GetTempFileNameW( tempdir
, L
"resu", 0, tempfile
))
1003 if (!CopyFileW( updates
->pFileName
, tempfile
, FALSE
))
1006 TRACE("tempfile %s\n", debugstr_w(tempfile
));
1008 if (!updates
->bDeleteExistingResources
)
1010 read_map
= create_mapping( updates
->pFileName
, FALSE
);
1014 ret
= read_mapped_resources( updates
, read_map
->base
, read_map
->size
);
1017 ERR("failed to read existing resources\n");
1022 write_map
= create_mapping( tempfile
, TRUE
);
1026 nt
= (IMAGE_NT_HEADERS32
*)get_nt_header( write_map
->base
, write_map
->size
);
1030 nt64
= (IMAGE_NT_HEADERS64
*)nt
;
1031 if (nt
->OptionalHeader
.Magic
== IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
1032 PeSectionAlignment
= nt64
->OptionalHeader
.SectionAlignment
;
1033 PeFileAlignment
= nt64
->OptionalHeader
.FileAlignment
;
1034 PeSizeOfImage
= nt64
->OptionalHeader
.SizeOfImage
;
1036 PeSectionAlignment
= nt
->OptionalHeader
.SectionAlignment
;
1037 PeFileAlignment
= nt
->OptionalHeader
.FileAlignment
;
1038 PeSizeOfImage
= nt
->OptionalHeader
.SizeOfImage
;
1041 if ((LONG
)PeSectionAlignment
<= 0)
1043 ERR("invalid section alignment %08x\n", PeSectionAlignment
);
1047 if ((LONG
)PeFileAlignment
<= 0)
1049 ERR("invalid file alignment %08x\n", PeFileAlignment
);
1053 sec
= get_resource_section( write_map
->base
, write_map
->size
);
1054 if (!sec
) /* no section, add one */
1058 sec
= get_section_header( write_map
->base
, write_map
->size
, &num_sections
);
1062 sec
+= num_sections
;
1063 nt
->FileHeader
.NumberOfSections
++;
1065 memset( sec
, 0, sizeof *sec
);
1066 memcpy( sec
->Name
, ".rsrc", 5 );
1067 sec
->Characteristics
= IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_READ
;
1068 sec
->VirtualAddress
= PeSizeOfImage
;
1071 if (!sec
->PointerToRawData
) /* empty section */
1073 sec
->PointerToRawData
= write_map
->size
+ (-write_map
->size
) % PeFileAlignment
;
1074 sec
->SizeOfRawData
= 0;
1077 TRACE("before .rsrc at %08x, size %08x\n", sec
->PointerToRawData
, sec
->SizeOfRawData
);
1079 get_resource_sizes( updates
, &res_size
);
1081 /* round up the section size */
1082 section_size
= res_size
.total_size
;
1083 section_size
+= (-section_size
) % PeFileAlignment
;
1085 TRACE("requires %08x (%08x) bytes\n", res_size
.total_size
, section_size
);
1087 /* check if the file size needs to be changed */
1088 if (section_size
!= sec
->SizeOfRawData
)
1090 DWORD old_size
= write_map
->size
;
1091 DWORD virtual_section_size
= res_size
.total_size
+ (-res_size
.total_size
) % PeSectionAlignment
;
1092 int delta
= section_size
- (sec
->SizeOfRawData
+ (-sec
->SizeOfRawData
) % PeFileAlignment
);
1093 int rva_delta
= virtual_section_size
-
1094 (sec
->Misc
.VirtualSize
+ (-sec
->Misc
.VirtualSize
) % PeSectionAlignment
);
1095 /* when new section is added it could end past current mapping size */
1096 BOOL rsrc_is_last
= sec
->PointerToRawData
+ sec
->SizeOfRawData
>= old_size
;
1097 /* align .rsrc size when possible */
1098 DWORD mapping_size
= rsrc_is_last
? sec
->PointerToRawData
+ section_size
: old_size
+ delta
;
1100 /* postpone file truncation if there are some data to be moved down from file end */
1101 BOOL resize_after
= mapping_size
< old_size
&& !rsrc_is_last
;
1103 TRACE("file size %08x -> %08x\n", old_size
, mapping_size
);
1107 /* unmap the file before changing the file size */
1108 ret
= resize_mapping( write_map
, mapping_size
);
1110 /* get the pointers again - they might be different after remapping */
1111 nt
= (IMAGE_NT_HEADERS32
*)get_nt_header( write_map
->base
, mapping_size
);
1114 ERR("couldn't get NT header\n");
1117 nt64
= (IMAGE_NT_HEADERS64
*)nt
;
1119 sec
= get_resource_section( write_map
->base
, mapping_size
);
1124 if (!rsrc_is_last
) /* not last section, relocate trailing sections */
1126 IMAGE_SECTION_HEADER
*s
;
1127 DWORD tail_start
= sec
->PointerToRawData
+ sec
->SizeOfRawData
;
1128 DWORD i
, num_sections
= 0;
1130 memmove( (char*)write_map
->base
+ tail_start
+ delta
, (char*)write_map
->base
+ tail_start
, old_size
- tail_start
);
1132 s
= get_section_header( write_map
->base
, mapping_size
, &num_sections
);
1134 for (i
=0; i
<num_sections
; i
++)
1136 if (s
[i
].PointerToRawData
> sec
->PointerToRawData
)
1138 s
[i
].PointerToRawData
+= delta
;
1139 s
[i
].VirtualAddress
+= rva_delta
;
1146 ret
= resize_mapping( write_map
, mapping_size
);
1148 nt
= (IMAGE_NT_HEADERS32
*)get_nt_header( write_map
->base
, mapping_size
);
1151 ERR("couldn't get NT header\n");
1154 nt64
= (IMAGE_NT_HEADERS64
*)nt
;
1156 sec
= get_resource_section( write_map
->base
, mapping_size
);
1161 /* adjust the PE header information */
1162 sec
->SizeOfRawData
= section_size
;
1163 sec
->Misc
.VirtualSize
= virtual_section_size
;
1164 if (nt
->OptionalHeader
.Magic
== IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
1165 nt64
->OptionalHeader
.SizeOfImage
+= rva_delta
;
1166 nt64
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
= sec
->VirtualAddress
;
1167 nt64
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].Size
= res_size
.total_size
;
1168 nt64
->OptionalHeader
.SizeOfInitializedData
= get_init_data_size( write_map
->base
, mapping_size
);
1170 for (i
=0; i
<nt64
->OptionalHeader
.NumberOfRvaAndSizes
; i
++)
1171 if (nt64
->OptionalHeader
.DataDirectory
[i
].VirtualAddress
> sec
->VirtualAddress
)
1172 nt64
->OptionalHeader
.DataDirectory
[i
].VirtualAddress
+= rva_delta
;
1174 nt
->OptionalHeader
.SizeOfImage
+= rva_delta
;
1175 nt
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
= sec
->VirtualAddress
;
1176 nt
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].Size
= res_size
.total_size
;
1177 nt
->OptionalHeader
.SizeOfInitializedData
= get_init_data_size( write_map
->base
, mapping_size
);
1179 for (i
=0; i
<nt
->OptionalHeader
.NumberOfRvaAndSizes
; i
++)
1180 if (nt
->OptionalHeader
.DataDirectory
[i
].VirtualAddress
> sec
->VirtualAddress
)
1181 nt
->OptionalHeader
.DataDirectory
[i
].VirtualAddress
+= rva_delta
;
1185 res_base
= (LPBYTE
) write_map
->base
+ sec
->PointerToRawData
;
1187 TRACE("base = %p offset = %08x\n", write_map
->base
, sec
->PointerToRawData
);
1189 ret
= write_resources( updates
, res_base
, &res_size
, sec
->VirtualAddress
);
1191 res_write_padding( res_base
+ res_size
.total_size
, section_size
- res_size
.total_size
);
1193 TRACE("after .rsrc at %08x, size %08x\n", sec
->PointerToRawData
, sec
->SizeOfRawData
);
1196 destroy_mapping( read_map
);
1197 destroy_mapping( write_map
);
1200 ret
= CopyFileW( tempfile
, updates
->pFileName
, FALSE
);
1202 DeleteFileW( tempfile
);
1207 /***********************************************************************
1208 * BeginUpdateResourceW (KERNEL32.@)
1210 HANDLE WINAPI
BeginUpdateResourceW( LPCWSTR pFileName
, BOOL bDeleteExistingResources
)
1212 QUEUEDUPDATES
*updates
= NULL
;
1213 HANDLE hUpdate
, file
, ret
= NULL
;
1215 TRACE("%s, %d\n", debugstr_w(pFileName
), bDeleteExistingResources
);
1217 hUpdate
= GlobalAlloc(GHND
, sizeof(QUEUEDUPDATES
));
1221 updates
= GlobalLock(hUpdate
);
1224 list_init( &updates
->root
);
1225 updates
->bDeleteExistingResources
= bDeleteExistingResources
;
1226 updates
->pFileName
= HeapAlloc(GetProcessHeap(), 0, (lstrlenW(pFileName
)+1)*sizeof(WCHAR
));
1227 if (updates
->pFileName
)
1229 lstrcpyW(updates
->pFileName
, pFileName
);
1231 file
= CreateFileW( pFileName
, GENERIC_READ
| GENERIC_WRITE
,
1232 0, NULL
, OPEN_EXISTING
, 0, 0 );
1234 /* if resources are deleted, only the file's presence is checked */
1235 if (file
!= INVALID_HANDLE_VALUE
&&
1236 (bDeleteExistingResources
|| check_pe_exe( file
, updates
)))
1239 HeapFree( GetProcessHeap(), 0, updates
->pFileName
);
1241 CloseHandle( file
);
1243 GlobalUnlock(hUpdate
);
1247 GlobalFree(hUpdate
);
1253 /***********************************************************************
1254 * BeginUpdateResourceA (KERNEL32.@)
1256 HANDLE WINAPI
BeginUpdateResourceA( LPCSTR pFileName
, BOOL bDeleteExistingResources
)
1258 UNICODE_STRING FileNameW
;
1260 RtlCreateUnicodeStringFromAsciiz(&FileNameW
, pFileName
);
1261 ret
= BeginUpdateResourceW(FileNameW
.Buffer
, bDeleteExistingResources
);
1262 RtlFreeUnicodeString(&FileNameW
);
1267 /***********************************************************************
1268 * EndUpdateResourceW (KERNEL32.@)
1270 BOOL WINAPI
EndUpdateResourceW( HANDLE hUpdate
, BOOL fDiscard
)
1272 QUEUEDUPDATES
*updates
;
1275 TRACE("%p %d\n", hUpdate
, fDiscard
);
1277 updates
= GlobalLock(hUpdate
);
1281 ret
= fDiscard
|| write_raw_resources( updates
);
1283 free_resource_directory( &updates
->root
, 2 );
1285 HeapFree( GetProcessHeap(), 0, updates
->pFileName
);
1286 GlobalUnlock( hUpdate
);
1287 GlobalFree( hUpdate
);
1293 /***********************************************************************
1294 * EndUpdateResourceA (KERNEL32.@)
1296 BOOL WINAPI
EndUpdateResourceA( HANDLE hUpdate
, BOOL fDiscard
)
1298 return EndUpdateResourceW(hUpdate
, fDiscard
);
1302 /***********************************************************************
1303 * UpdateResourceW (KERNEL32.@)
1305 BOOL WINAPI
UpdateResourceW( HANDLE hUpdate
, LPCWSTR lpType
, LPCWSTR lpName
,
1306 WORD wLanguage
, LPVOID lpData
, DWORD cbData
)
1308 QUEUEDUPDATES
*updates
;
1309 UNICODE_STRING nameW
, typeW
;
1312 TRACE("%p %s %s %08x %p %d\n", hUpdate
,
1313 debugstr_w(lpType
), debugstr_w(lpName
), wLanguage
, lpData
, cbData
);
1315 nameW
.Buffer
= typeW
.Buffer
= NULL
;
1316 updates
= GlobalLock(hUpdate
);
1319 if (!set_ntstatus( get_res_nameW( lpName
, &nameW
))) goto done
;
1320 if (!set_ntstatus( get_res_nameW( lpType
, &typeW
))) goto done
;
1322 if (lpData
== NULL
&& cbData
== 0) /* remove resource */
1324 ret
= update_add_resource( updates
, typeW
.Buffer
, nameW
.Buffer
, wLanguage
, NULL
, TRUE
);
1328 struct resource_data
*data
;
1329 data
= allocate_resource_data( wLanguage
, 0, lpData
, cbData
, TRUE
);
1331 ret
= update_add_resource( updates
, typeW
.Buffer
, nameW
.Buffer
, wLanguage
, data
, TRUE
);
1335 GlobalUnlock(hUpdate
);
1338 if (!IS_INTRESOURCE(nameW
.Buffer
)) HeapFree( GetProcessHeap(), 0, nameW
.Buffer
);
1339 if (!IS_INTRESOURCE(typeW
.Buffer
)) HeapFree( GetProcessHeap(), 0, typeW
.Buffer
);
1344 /***********************************************************************
1345 * UpdateResourceA (KERNEL32.@)
1347 BOOL WINAPI
UpdateResourceA( HANDLE hUpdate
, LPCSTR lpType
, LPCSTR lpName
,
1348 WORD wLanguage
, LPVOID lpData
, DWORD cbData
)
1351 UNICODE_STRING TypeW
;
1352 UNICODE_STRING NameW
;
1353 if(IS_INTRESOURCE(lpType
))
1354 TypeW
.Buffer
= ULongToPtr(LOWORD(lpType
));
1356 RtlCreateUnicodeStringFromAsciiz(&TypeW
, lpType
);
1357 if(IS_INTRESOURCE(lpName
))
1358 NameW
.Buffer
= ULongToPtr(LOWORD(lpName
));
1360 RtlCreateUnicodeStringFromAsciiz(&NameW
, lpName
);
1361 ret
= UpdateResourceW(hUpdate
, TypeW
.Buffer
, NameW
.Buffer
, wLanguage
, lpData
, cbData
);
1362 if(!IS_INTRESOURCE(lpType
)) RtlFreeUnicodeString(&TypeW
);
1363 if(!IS_INTRESOURCE(lpName
)) RtlFreeUnicodeString(&NameW
);