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