[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