Added regedit unit test, a couple minor changes to regedit.
[wine] / dlls / version / resource.c
1 /*
2  * Implementation of VERSION.DLL - Resource Access routines
3  *
4  * Copyright 1996,1997 Marcus Meissner
5  * Copyright 1997 David Cuthbert
6  * Copyright 1999 Ulrich Weigand
7  *
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.
12  *
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.
17  *
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29
30 #include "winbase.h"
31 #include "lzexpand.h"
32
33 #include "wine/unicode.h"
34 #include "wine/winbase16.h"
35 #include "wine/winuser16.h"
36 #include "winver.h"
37
38 #include "wine/debug.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(ver);
41
42
43 /**********************************************************************
44  *  find_entry_by_id
45  *
46  * Find an entry by id in a resource directory
47  * Copied from loader/pe_resource.c
48  */
49 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
50                                                          WORD id, const void *root )
51 {
52     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
53     int min, max, pos;
54
55     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
56     min = dir->NumberOfNamedEntries;
57     max = min + dir->NumberOfIdEntries - 1;
58     while (min <= max)
59     {
60         pos = (min + max) / 2;
61         if (entry[pos].u1.s2.Id == id)
62             return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s3.OffsetToDirectory);
63         if (entry[pos].u1.s2.Id > id) max = pos - 1;
64         else min = pos + 1;
65     }
66     return NULL;
67 }
68
69
70 /**********************************************************************
71  *  find_entry_default
72  *
73  * Find a default entry in a resource directory
74  * Copied from loader/pe_resource.c
75  */
76 static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir,
77                                                            const void *root )
78 {
79     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
80
81     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
82     return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry->u2.s3.OffsetToDirectory);
83 }
84
85
86 /**********************************************************************
87  *  find_entry_by_name
88  *
89  * Find an entry by name in a resource directory
90  * Copied from loader/pe_resource.c
91  */
92 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_name( const IMAGE_RESOURCE_DIRECTORY *dir,
93                                                            LPCSTR name, const void *root )
94 {
95     const IMAGE_RESOURCE_DIRECTORY *ret = NULL;
96     LPWSTR nameW;
97     DWORD namelen;
98
99     if (!HIWORD(name)) return find_entry_by_id( dir, LOWORD(name), root );
100     if (name[0] == '#')
101     {
102         return find_entry_by_id( dir, atoi(name+1), root );
103     }
104
105     namelen = MultiByteToWideChar( CP_ACP, 0, name, -1, NULL, 0 );
106     if ((nameW = HeapAlloc( GetProcessHeap(), 0, namelen * sizeof(WCHAR) )))
107     {
108         const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
109         const IMAGE_RESOURCE_DIR_STRING_U *str;
110         int min, max, res, pos;
111
112         MultiByteToWideChar( CP_ACP, 0, name, -1, nameW, namelen );
113         namelen--;  /* remove terminating null */
114         entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
115         min = 0;
116         max = dir->NumberOfNamedEntries - 1;
117         while (min <= max)
118         {
119             pos = (min + max) / 2;
120             str = (IMAGE_RESOURCE_DIR_STRING_U *)((char *)root + entry[pos].u1.s1.NameOffset);
121             res = strncmpiW( nameW, str->NameString, str->Length );
122             if (!res && namelen == str->Length)
123             {
124                 ret = (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s3.OffsetToDirectory);
125                 break;
126             }
127             if (res < 0) max = pos - 1;
128             else min = pos + 1;
129         }
130         HeapFree( GetProcessHeap(), 0, nameW );
131     }
132     return ret;
133 }
134
135
136 /***********************************************************************
137  *           read_xx_header         [internal]
138  */
139 static int read_xx_header( HFILE lzfd )
140 {
141     IMAGE_DOS_HEADER mzh;
142     char magic[3];
143
144     LZSeek( lzfd, 0, SEEK_SET );
145     if ( sizeof(mzh) != LZRead( lzfd, &mzh, sizeof(mzh) ) )
146         return 0;
147     if ( mzh.e_magic != IMAGE_DOS_SIGNATURE )
148         return 0;
149
150     LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
151     if ( 2 != LZRead( lzfd, magic, 2 ) )
152         return 0;
153
154     LZSeek( lzfd, mzh.e_lfanew, SEEK_SET );
155
156     if ( magic[0] == 'N' && magic[1] == 'E' )
157         return IMAGE_OS2_SIGNATURE;
158     if ( magic[0] == 'P' && magic[1] == 'E' )
159         return IMAGE_NT_SIGNATURE;
160
161     magic[2] = '\0';
162     WARN("Can't handle %s files.\n", magic );
163     return 0;
164 }
165
166 /***********************************************************************
167  *           load_ne_resource         [internal]
168  */
169 static BOOL find_ne_resource( HFILE lzfd, LPCSTR typeid, LPCSTR resid,
170                                 DWORD *resLen, DWORD *resOff )
171 {
172     IMAGE_OS2_HEADER nehd;
173     NE_TYPEINFO *typeInfo;
174     NE_NAMEINFO *nameInfo;
175     DWORD nehdoffset;
176     LPBYTE resTab;
177     DWORD resTabSize;
178     int count;
179
180     /* Read in NE header */
181     nehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
182     if ( sizeof(nehd) != LZRead( lzfd, &nehd, sizeof(nehd) ) ) return 0;
183
184     resTabSize = nehd.ne_restab - nehd.ne_rsrctab;
185     if ( !resTabSize )
186     {
187         TRACE("No resources in NE dll\n" );
188         return FALSE;
189     }
190
191     /* Read in resource table */
192     resTab = HeapAlloc( GetProcessHeap(), 0, resTabSize );
193     if ( !resTab ) return FALSE;
194
195     LZSeek( lzfd, nehd.ne_rsrctab + nehdoffset, SEEK_SET );
196     if ( resTabSize != LZRead( lzfd, resTab, resTabSize ) )
197     {
198         HeapFree( GetProcessHeap(), 0, resTab );
199         return FALSE;
200     }
201
202     /* Find resource */
203     typeInfo = (NE_TYPEINFO *)(resTab + 2);
204
205     if (HIWORD(typeid) != 0)  /* named type */
206     {
207         BYTE len = strlen( typeid );
208         while (typeInfo->type_id)
209         {
210             if (!(typeInfo->type_id & 0x8000))
211             {
212                 BYTE *p = resTab + typeInfo->type_id;
213                 if ((*p == len) && !strncasecmp( p+1, typeid, len )) goto found_type;
214             }
215             typeInfo = (NE_TYPEINFO *)((char *)(typeInfo + 1) +
216                                        typeInfo->count * sizeof(NE_NAMEINFO));
217         }
218     }
219     else  /* numeric type id */
220     {
221         WORD id = LOWORD(typeid) | 0x8000;
222         while (typeInfo->type_id)
223         {
224             if (typeInfo->type_id == id) goto found_type;
225             typeInfo = (NE_TYPEINFO *)((char *)(typeInfo + 1) +
226                                        typeInfo->count * sizeof(NE_NAMEINFO));
227         }
228     }
229     TRACE("No typeid entry found for %p\n", typeid );
230     HeapFree( GetProcessHeap(), 0, resTab );
231     return FALSE;
232
233  found_type:
234     nameInfo = (NE_NAMEINFO *)(typeInfo + 1);
235
236     if (HIWORD(resid) != 0)  /* named resource */
237     {
238         BYTE len = strlen( resid );
239         for (count = typeInfo->count; count > 0; count--, nameInfo++)
240         {
241             BYTE *p = resTab + nameInfo->id;
242             if (nameInfo->id & 0x8000) continue;
243             if ((*p == len) && !strncasecmp( p+1, resid, len )) goto found_name;
244         }
245     }
246     else  /* numeric resource id */
247     {
248         WORD id = LOWORD(resid) | 0x8000;
249         for (count = typeInfo->count; count > 0; count--, nameInfo++)
250             if (nameInfo->id == id) goto found_name;
251     }
252     TRACE("No resid entry found for %p\n", typeid );
253     HeapFree( GetProcessHeap(), 0, resTab );
254     return FALSE;
255
256  found_name:
257     /* Return resource data */
258     if ( resLen ) *resLen = nameInfo->length << *(WORD *)resTab;
259     if ( resOff ) *resOff = nameInfo->offset << *(WORD *)resTab;
260
261     HeapFree( GetProcessHeap(), 0, resTab );
262     return TRUE;
263 }
264
265 /***********************************************************************
266  *           load_pe_resource         [internal]
267  */
268 static BOOL find_pe_resource( HFILE lzfd, LPCSTR typeid, LPCSTR resid,
269                                 DWORD *resLen, DWORD *resOff )
270 {
271     IMAGE_NT_HEADERS pehd;
272     DWORD pehdoffset;
273     PIMAGE_DATA_DIRECTORY resDataDir;
274     PIMAGE_SECTION_HEADER sections;
275     LPBYTE resSection;
276     DWORD resSectionSize;
277     const void *resDir;
278     const IMAGE_RESOURCE_DIRECTORY *resPtr;
279     const IMAGE_RESOURCE_DATA_ENTRY *resData;
280     int i, nSections;
281     BOOL ret = FALSE;
282
283     /* Read in PE header */
284     pehdoffset = LZSeek( lzfd, 0, SEEK_CUR );
285     if ( sizeof(pehd) != LZRead( lzfd, &pehd, sizeof(pehd) ) ) return 0;
286
287     resDataDir = pehd.OptionalHeader.DataDirectory+IMAGE_FILE_RESOURCE_DIRECTORY;
288     if ( !resDataDir->Size )
289     {
290         TRACE("No resources in PE dll\n" );
291         return FALSE;
292     }
293
294     /* Read in section table */
295     nSections = pehd.FileHeader.NumberOfSections;
296     sections = HeapAlloc( GetProcessHeap(), 0,
297                           nSections * sizeof(IMAGE_SECTION_HEADER) );
298     if ( !sections ) return FALSE;
299
300     LZSeek( lzfd, pehdoffset +
301                     sizeof(DWORD) + /* Signature */
302                     sizeof(IMAGE_FILE_HEADER) +
303                     pehd.FileHeader.SizeOfOptionalHeader, SEEK_SET );
304
305     if ( nSections * sizeof(IMAGE_SECTION_HEADER) !=
306          LZRead( lzfd, sections, nSections * sizeof(IMAGE_SECTION_HEADER) ) )
307     {
308         HeapFree( GetProcessHeap(), 0, sections );
309         return FALSE;
310     }
311
312     /* Find resource section */
313     for ( i = 0; i < nSections; i++ )
314         if (    resDataDir->VirtualAddress >= sections[i].VirtualAddress
315              && resDataDir->VirtualAddress <  sections[i].VirtualAddress +
316                                               sections[i].SizeOfRawData )
317             break;
318
319     if ( i == nSections )
320     {
321         HeapFree( GetProcessHeap(), 0, sections );
322         TRACE("Couldn't find resource section\n" );
323         return FALSE;
324     }
325
326     /* Read in resource section */
327     resSectionSize = sections[i].SizeOfRawData;
328     resSection = HeapAlloc( GetProcessHeap(), 0, resSectionSize );
329     if ( !resSection )
330     {
331         HeapFree( GetProcessHeap(), 0, sections );
332         return FALSE;
333     }
334
335     LZSeek( lzfd, sections[i].PointerToRawData, SEEK_SET );
336     if ( resSectionSize != LZRead( lzfd, resSection, resSectionSize ) ) goto done;
337
338     /* Find resource */
339     resDir = resSection + (resDataDir->VirtualAddress - sections[i].VirtualAddress);
340
341     resPtr = (PIMAGE_RESOURCE_DIRECTORY)resDir;
342     resPtr = find_entry_by_name( resPtr, typeid, resDir );
343     if ( !resPtr )
344     {
345         TRACE("No typeid entry found for %p\n", typeid );
346         goto done;
347     }
348     resPtr = find_entry_by_name( resPtr, resid, resDir );
349     if ( !resPtr )
350     {
351         TRACE("No resid entry found for %p\n", resid );
352         goto done;
353     }
354     resPtr = find_entry_default( resPtr, resDir );
355     if ( !resPtr )
356     {
357         TRACE("No default language entry found for %p\n", resid );
358         goto done;
359     }
360
361     /* Find resource data section */
362     resData = (PIMAGE_RESOURCE_DATA_ENTRY)resPtr;
363     for ( i = 0; i < nSections; i++ )
364         if (    resData->OffsetToData >= sections[i].VirtualAddress
365              && resData->OffsetToData <  sections[i].VirtualAddress +
366                                          sections[i].SizeOfRawData )
367             break;
368
369     if ( i == nSections )
370     {
371         TRACE("Couldn't find resource data section\n" );
372         goto done;
373     }
374
375     /* Return resource data */
376     if ( resLen ) *resLen = resData->Size;
377     if ( resOff ) *resOff = resData->OffsetToData - sections[i].VirtualAddress
378                             + sections[i].PointerToRawData;
379     ret = TRUE;
380
381  done:
382     HeapFree( GetProcessHeap(), 0, resSection );
383     HeapFree( GetProcessHeap(), 0, sections );
384     return ret;
385 }
386
387
388 /*************************************************************************
389  * GetFileResourceSize                     [VER.2]
390  */
391 DWORD WINAPI GetFileResourceSize16( LPCSTR lpszFileName, LPCSTR lpszResType,
392                                     LPCSTR lpszResId, LPDWORD lpdwFileOffset )
393 {
394     BOOL retv = FALSE;
395     HFILE lzfd;
396     OFSTRUCT ofs;
397     DWORD reslen;
398
399     TRACE("(%s,type=0x%lx,id=0x%lx,off=%p)\n",
400                 debugstr_a(lpszFileName), (LONG)lpszResType, (LONG)lpszResId,
401                 lpszResId );
402
403     lzfd = LZOpenFileA( lpszFileName, &ofs, OF_READ );
404     if ( lzfd < 0 ) return 0;
405
406     switch ( read_xx_header( lzfd ) )
407     {
408     case IMAGE_OS2_SIGNATURE:
409         retv = find_ne_resource( lzfd, lpszResType, lpszResId,
410                                  &reslen, lpdwFileOffset );
411         break;
412
413     case IMAGE_NT_SIGNATURE:
414         retv = find_pe_resource( lzfd, lpszResType, lpszResId,
415                                  &reslen, lpdwFileOffset );
416         break;
417     }
418
419     LZClose( lzfd );
420     return retv? reslen : 0;
421 }
422
423
424 /*************************************************************************
425  * GetFileResource                         [VER.3]
426  */
427 DWORD WINAPI GetFileResource16( LPCSTR lpszFileName, LPCSTR lpszResType,
428                                 LPCSTR lpszResId, DWORD dwFileOffset,
429                                 DWORD dwResLen, LPVOID lpvData )
430 {
431     BOOL retv = FALSE;
432     HFILE lzfd;
433     OFSTRUCT ofs;
434     DWORD reslen = dwResLen;
435
436     TRACE("(%s,type=0x%lx,id=0x%lx,off=%ld,len=%ld,data=%p)\n",
437                 debugstr_a(lpszFileName), (LONG)lpszResType, (LONG)lpszResId,
438                 dwFileOffset, dwResLen, lpvData );
439
440     lzfd = LZOpenFileA( lpszFileName, &ofs, OF_READ );
441     if ( lzfd < 0 ) return 0;
442
443     if ( !dwFileOffset )
444     {
445         switch ( read_xx_header( lzfd ) )
446         {
447         case IMAGE_OS2_SIGNATURE:
448             retv = find_ne_resource( lzfd, lpszResType, lpszResId,
449                                      &reslen, &dwFileOffset );
450             break;
451
452         case IMAGE_NT_SIGNATURE:
453             retv = find_pe_resource( lzfd, lpszResType, lpszResId,
454                                      &reslen, &dwFileOffset );
455             break;
456         }
457
458         if ( !retv )
459         {
460             LZClose( lzfd );
461             return 0;
462         }
463     }
464
465     LZSeek( lzfd, dwFileOffset, SEEK_SET );
466     reslen = LZRead( lzfd, lpvData, min( reslen, dwResLen ) );
467     LZClose( lzfd );
468
469     return reslen;
470 }
471