[PATCH 3/3] kernel32: Implement BeginUpdateResource by reading the
raw resource data
Mike McCormack
mike at codeweavers.com
Tue Dec 26 23:15:02 CST 2006
---
dlls/kernel32/resource.c | 320
+++++++++++++++++++++++++++++++++++++++-
dlls/kernel32/tests/resource.c | 6 +-
2 files changed, 315 insertions(+), 11 deletions(-)
diff --git a/dlls/kernel32/resource.c b/dlls/kernel32/resource.c
index c817fcb..ee15aa4 100644
--- a/dlls/kernel32/resource.c
+++ b/dlls/kernel32/resource.c
@@ -616,16 +616,199 @@ DWORD WINAPI SizeofResource( HINSTANCE hModule,
HRSRC hRsrc )
return ((PIMAGE_RESOURCE_DATA_ENTRY)hRsrc)->Size;
}
-typedef struct
-{
+/*
+ * Data structure for updating resources.
+ * Type/Name/Language is a keyset for accessing resource data.
+ *
+ * QUEUEDUPDATES (root) ->
+ * list of struct resouce_dir_entry (Type) ->
+ * list of struct resouce_dir_entry (Name) ->
+ * list of struct resouce_data Language + Data
+ */
+
+typedef struct {
LPWSTR pFileName;
+ struct list root;
} QUEUEDUPDATES;
+/* this structure is shared for types and names */
+struct resource_dir_entry {
+ struct list entry;
+ LPWSTR id;
+ struct list children;
+};
+
+/* this structure is the leaf */
+struct resource_data {
+ struct list entry;
+ LANGID lang;
+ DWORD codepage;
+ DWORD cbData;
+ BYTE data[1];
+};
+
+int resource_strcmp( LPCWSTR a, LPCWSTR b )
+{
+ if ( a == b )
+ return 0;
+ if (HIWORD( a ) && HIWORD( b ) )
+ return lstrcmpW( a, b );
+ /* strings come before ids */
+ if (HIWORD( a ) && !HIWORD( b ))
+ return -1;
+ if (HIWORD( b ) && !HIWORD( a ))
+ return 1;
+ return ( a < b ) ? -1 : 1;
+}
+
+struct resource_dir_entry *find_resource_dir_entry( struct list *dir,
LPCWSTR id )
+{
+ struct resource_dir_entry *ent;
+
+ /* match either IDs or strings */
+ LIST_FOR_EACH_ENTRY( ent, dir, struct resource_dir_entry, entry )
+ if (!resource_strcmp( id, ent->id ))
+ return ent;
+
+ return NULL;
+}
+
+struct resource_data *find_resource_data( struct list *dir, LANGID lang )
+{
+ struct resource_data *res_data;
+
+ /* match only languages here */
+ LIST_FOR_EACH_ENTRY( res_data, dir, struct resource_data, entry )
+ if ( lang == res_data->lang )
+ return res_data;
+
+ return NULL;
+}
+
+void add_resource_dir_entry( struct list *dir, struct
resource_dir_entry *resdir )
+{
+ struct resource_dir_entry *ent;
+
+ LIST_FOR_EACH_ENTRY( ent, dir, struct resource_dir_entry, entry )
+ {
+ if (0>resource_strcmp( ent->id, resdir->id ))
+ continue;
+
+ list_add_before( &ent->entry, &resdir->entry );
+ return;
+ }
+ list_add_tail( dir, &resdir->entry );
+}
+
+void add_resource_data_entry( struct list *dir, struct resource_data
*resdata )
+{
+ struct resource_data *ent;
+
+ LIST_FOR_EACH_ENTRY( ent, dir, struct resource_data, entry )
+ {
+ if (ent->lang < resdata->lang)
+ continue;
+
+ list_add_before( &ent->entry, &resdata->entry );
+ return;
+ }
+ list_add_tail( dir, &resdata->entry );
+}
+
+LPWSTR res_strdupW( LPCWSTR str )
+{
+ LPWSTR ret;
+ UINT len;
+
+ if (HIWORD(str) == 0)
+ return (LPWSTR) (UINT_PTR) LOWORD(str);
+ len = (lstrlenW( str ) + 1) * sizeof (WCHAR);
+ ret = HeapAlloc( GetProcessHeap(), 0, len );
+ memcpy( ret, str, len );
+ return ret;
+}
+
+void res_free_str( LPWSTR str )
+{
+ if (HIWORD(str))
+ HeapFree( GetProcessHeap(), 0, str );
+}
+
BOOL update_add_resource( QUEUEDUPDATES *updates, LPCWSTR Type,
LPCWSTR Name,
WORD Language, DWORD codepage, LPCVOID
lpData, DWORD cbData )
{
- FIXME("%p %s %s %04x %p %d bytes\n", updates, debugstr_w(Type),
debugstr_w(Name), Language, lpData, cbData);
- return FALSE;
+ struct resource_dir_entry *restype, *resname;
+ struct resource_data *resdata;
+
+ TRACE("%p %s %s %04x %p %d bytes\n", updates, debugstr_w(Type),
debugstr_w(Name), Language, lpData, cbData);
+
+ if (!lpData || !cbData)
+ return FALSE;
+
+ restype = find_resource_dir_entry( &updates->root, Type );
+ if (!restype)
+ {
+ restype = HeapAlloc( GetProcessHeap(), 0, sizeof *restype );
+ restype->id = res_strdupW( Type );
+ list_init( &restype->children );
+ add_resource_dir_entry( &updates->root, restype );
+ }
+
+ resname = find_resource_dir_entry( &restype->children, Name );
+ if (!resname)
+ {
+ resname = HeapAlloc( GetProcessHeap(), 0, sizeof *resname );
+ resname->id = res_strdupW( Name );
+ list_init( &resname->children );
+ add_resource_dir_entry( &restype->children, resname );
+ }
+
+ /*
+ * If there's an existing resource entry with matching
(Type,Name,Language)
+ * it needs to be removed before adding the new data.
+ */
+ resdata = find_resource_data( &resname->children, Language );
+ if (resdata)
+ {
+ list_remove( &resdata->entry );
+ HeapFree( GetProcessHeap(), 0, resdata );
+ }
+
+ resdata = HeapAlloc( GetProcessHeap(), 0, sizeof *resdata + cbData );
+ resdata->lang = Language;
+ resdata->codepage = codepage;
+ resdata->cbData = cbData;
+ memcpy( resdata->data, lpData, cbData );
+
+ add_resource_data_entry( &resname->children, resdata );
+
+ return TRUE;
+}
+
+void free_resource_directory( struct list *head, int level )
+{
+ struct list *ptr = NULL;
+
+ while ((ptr = list_head( head )))
+ {
+ list_remove( ptr );
+ if (level)
+ {
+ struct resource_dir_entry *ent;
+
+ ent = LIST_ENTRY( ptr, struct resource_dir_entry, entry );
+ res_free_str( ent->id );
+ free_resource_directory( &ent->children, level - 1 );
+ HeapFree(GetProcessHeap(), 0, ent);
+ }
+ else
+ {
+ struct resource_data *data;
+
+ data = LIST_ENTRY( ptr, struct resource_data, entry );
+ HeapFree( GetProcessHeap(), 0, data );
+ }
+ }
}
IMAGE_NT_HEADERS *get_nt_header( void *base, DWORD mapping_size )
@@ -673,13 +856,120 @@ IMAGE_SECTION_HEADER *get_section_header( void
*base, DWORD mapping_size, DWORD
return (void*) ((BYTE*)nt + section_ofs);
}
+const IMAGE_SECTION_HEADER *section_from_rva( void *base, DWORD
mapping_size, DWORD rva )
+{
+ const IMAGE_SECTION_HEADER *sec;
+ DWORD num_sections = 0;
+ int i;
+
+ sec = get_section_header( base, mapping_size, &num_sections );
+ if (!sec)
+ return NULL;
+
+ for (i=num_sections-1; i>=0; i--)
+ {
+ if (sec[i].VirtualAddress <= rva &&
+ rva <= (DWORD)sec[i].VirtualAddress + sec[i].SizeOfRawData)
+ {
+ return &sec[i];
+ }
+ }
+
+ return NULL;
+}
+
+const void *address_from_rva( void *base, DWORD mapping_size, DWORD
rva, DWORD len )
+{
+ const IMAGE_SECTION_HEADER *sec;
+
+ sec = section_from_rva( base, mapping_size, rva );
+ if (!sec)
+ return NULL;
+
+ if (rva + len <= (DWORD)sec->VirtualAddress + sec->SizeOfRawData)
+ return (const void*)((const BYTE*) base +
(sec->PointerToRawData + rva - sec->VirtualAddress));
+
+ return NULL;
+}
+
+LPWSTR resource_dup_string( const IMAGE_RESOURCE_DIRECTORY *root, const
IMAGE_RESOURCE_DIRECTORY_ENTRY *entry )
+{
+ const IMAGE_RESOURCE_DIR_STRING_U* string;
+ LPWSTR s;
+
+ if (!entry->u1.s1.NameIsString)
+ return (LPWSTR) (DWORD) entry->u1.s2.Id;
+
+ string = (const IMAGE_RESOURCE_DIR_STRING_U*) (((const char *)root)
+ entry->u1.s1.NameOffset);
+ s = HeapAlloc(GetProcessHeap(), 0, (string->Length + 1)*sizeof
(WCHAR) );
+ memcpy( s, string->NameString, (string->Length + 1)*sizeof (WCHAR) );
+ s[string->Length] = 0;
+
+ return s;
+}
+
+/* this function is based on the code in winedump's pe.c */
+BOOL enumerate_raw_resouces( QUEUEDUPDATES *updates,
+ void *base, DWORD mapping_size,
+ const IMAGE_RESOURCE_DIRECTORY *root )
+{
+ const IMAGE_RESOURCE_DIRECTORY *namedir, *langdir;
+ const IMAGE_RESOURCE_DIRECTORY_ENTRY *e1, *e2, *e3;
+ const IMAGE_RESOURCE_DATA_ENTRY *data;
+ DWORD i, j, k;
+
+ TRACE("version (%d.%d) %d named %d id entries\n",
+ root->MajorVersion, root->MinorVersion,
root->NumberOfNamedEntries, root->NumberOfIdEntries);
+
+ for (i = 0; i< root->NumberOfNamedEntries +
root->NumberOfIdEntries; i++)
+ {
+ LPWSTR Type;
+
+ e1 = (const IMAGE_RESOURCE_DIRECTORY_ENTRY*)(root + 1) + i;
+
+ Type = resource_dup_string( root, e1 );
+
+ namedir = (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root
+ e1->u2.s3.OffsetToDirectory);
+ for (j = 0; j < namedir->NumberOfNamedEntries +
namedir->NumberOfIdEntries; j++)
+ {
+ LPWSTR Name;
+
+ e2 = (const IMAGE_RESOURCE_DIRECTORY_ENTRY*)(namedir + 1) + j;
+
+ Name = resource_dup_string( root, e2 );
+
+ langdir = (const IMAGE_RESOURCE_DIRECTORY *)((const char
*)root + e2->u2.s3.OffsetToDirectory);
+ for (k = 0; k < langdir->NumberOfNamedEntries +
langdir->NumberOfIdEntries; k++)
+ {
+ LANGID Lang;
+ const void *p;
+
+ e3 = (const IMAGE_RESOURCE_DIRECTORY_ENTRY*)(langdir +
1) + k;
+
+ Lang = e3->u1.s2.Id;
+
+ data = (const IMAGE_RESOURCE_DATA_ENTRY *)((const char
*)root + e3->u2.OffsetToData);
+
+ p = address_from_rva( base, mapping_size,
data->OffsetToData, data->Size );
+
+ update_add_resource( updates, Type, Name, Lang,
data->CodePage, p, data->Size );
+ }
+ res_free_str( Name );
+ }
+ res_free_str( Type );
+ }
+
+ return TRUE;
+}
+
static BOOL load_raw_resources( HANDLE file, QUEUEDUPDATES *updates )
{
+ const IMAGE_RESOURCE_DIRECTORY *root;
const IMAGE_NT_HEADERS *nt;
const IMAGE_SECTION_HEADER *sec;
BOOL ret = FALSE;
HANDLE mapping;
- DWORD mapping_size, num_sections = 0;
+ DWORD mapping_size, num_sections = 0, i;
void *base = NULL;
mapping_size = GetFileSize( file, NULL );
@@ -706,7 +996,22 @@ static BOOL load_raw_resources( HANDLE file,
QUEUEDUPDATES *updates )
ret = TRUE;
- FIXME("not implemented\n");
+ for (i=0; i<num_sections; i++)
+ if (!memcmp(sec[i].Name, ".rsrc", 6))
+ break;
+
+ if (i == num_sections)
+ goto done;
+
+ /* check the resource data is inside the mapping */
+ if (sec[i].PointerToRawData > mapping_size ||
+ (sec[i].PointerToRawData + sec[i].SizeOfRawData) > mapping_size)
+ goto done;
+
+ TRACE("found .rsrc at %08x, size %08x\n", sec[i].PointerToRawData,
sec[i].SizeOfRawData);
+
+ root = (void*) ((BYTE*)base + sec[i].PointerToRawData);
+ enumerate_raw_resouces( updates, base, mapping_size, root );
done:
if (base)
@@ -740,6 +1045,7 @@ HANDLE WINAPI BeginUpdateResourceW( LPCWSTR
pFileName, BOOL bDeleteExistingResou
updates = GlobalLock(hUpdate);
if (updates)
{
+ list_init( &updates->root );
updates->pFileName = HeapAlloc(GetProcessHeap(), 0,
(lstrlenW(pFileName)+1)*sizeof(WCHAR));
if (updates->pFileName)
{
@@ -797,6 +1103,8 @@ BOOL WINAPI EndUpdateResourceW( HANDLE hUpdate,
BOOL fDiscard )
ret = fDiscard || write_raw_resources( updates );
+ free_resource_directory( &updates->root, 2 );
+
HeapFree( GetProcessHeap(), 0, updates->pFileName );
GlobalUnlock( hUpdate );
GlobalFree( hUpdate );
diff --git a/dlls/kernel32/tests/resource.c b/dlls/kernel32/tests/resource.c
index 0c84757..9a26a12 100644
--- a/dlls/kernel32/tests/resource.c
+++ b/dlls/kernel32/tests/resource.c
@@ -178,7 +178,6 @@ void update_resources_version(void)
res = BeginUpdateResource( filename, TRUE );
ok( res != NULL, "BeginUpdateResource failed\n");
- todo_wine {
memset( &hdr, 0, sizeof hdr );
r = UpdateResource( res,
RT_VERSION,
@@ -186,7 +185,6 @@ void update_resources_version(void)
MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL),
&hdr, sizeof hdr );
ok( r, "UpdateResouce failed\n");
- }
r = UpdateResource( res,
MAKEINTRESOURCE(0x1230),
@@ -195,7 +193,6 @@ void update_resources_version(void)
NULL, 0 );
ok( r == FALSE, "UpdateResouce failed\n");
- todo_wine {
r = UpdateResource( res,
MAKEINTRESOURCE(0x1230),
MAKEINTRESOURCE(0x4567),
@@ -204,8 +201,7 @@ void update_resources_version(void)
ok( r == TRUE, "UpdateResouce failed\n");
r = EndUpdateResource( res, FALSE );
- ok( r, "EndUpdateResouce failed\n");
- }
+ todo_wine ok( r, "EndUpdateResouce failed\n");
}
--
1.4.4.1.g431f
More information about the wine-patches
mailing list