Added regedit unit test, a couple minor changes to regedit.
[wine] / loader / ne / resource.c
1 /*
2  * NE resource functions
3  *
4  * Copyright 1993 Robert J. Amstadt
5  * Copyright 1995 Alexandre Julliard
6  * Copyright 1997 Alex Korobka
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 #include "wine/port.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32
33 #include "windef.h"
34 #include "wine/winbase16.h"
35 #include "wine/library.h"
36 #include "module.h"
37 #include "wine/debug.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(resource);
40
41 #define NEXT_TYPEINFO(pTypeInfo) ((NE_TYPEINFO *)((char*)((pTypeInfo) + 1) + \
42                                    (pTypeInfo)->count * sizeof(NE_NAMEINFO)))
43
44 static FARPROC16 DefResourceHandlerProc = (FARPROC16)0xffffffff;
45
46 /* already defined in segment.c glue code */
47 extern WORD CALLBACK NE_CallTo16_word_www(FARPROC16,WORD,WORD,WORD);
48
49 /***********************************************************************
50  *           NE_FindNameTableId
51  *
52  * Find the type and resource id from their names.
53  * Return value is MAKELONG( typeId, resId ), or 0 if not found.
54  */
55 static DWORD NE_FindNameTableId( NE_MODULE *pModule, LPCSTR typeId, LPCSTR resId )
56 {
57     NE_TYPEINFO *pTypeInfo = (NE_TYPEINFO *)((char *)pModule + pModule->res_table + 2);
58     NE_NAMEINFO *pNameInfo;
59     HGLOBAL16 handle;
60     WORD *p;
61     DWORD ret = 0;
62     int count;
63
64     for (; pTypeInfo->type_id != 0;
65            pTypeInfo = (NE_TYPEINFO *)((char*)(pTypeInfo+1) +
66                                         pTypeInfo->count * sizeof(NE_NAMEINFO)))
67     {
68         if (pTypeInfo->type_id != 0x800f) continue;
69         pNameInfo = (NE_NAMEINFO *)(pTypeInfo + 1);
70         for (count = pTypeInfo->count; count > 0; count--, pNameInfo++)
71         {
72             TRACE("NameTable entry: type=%04x id=%04x\n",
73                               pTypeInfo->type_id, pNameInfo->id );
74             handle = LoadResource16( pModule->self,
75                                    (HRSRC16)((int)pNameInfo - (int)pModule) );
76             for(p = (WORD*)LockResource16(handle); p && *p; p = (WORD *)((char*)p+*p))
77             {
78                 TRACE("  type=%04x '%s' id=%04x '%s'\n",
79                                   p[1], (char *)(p+3), p[2],
80                                   (char *)(p+3)+strlen((char *)(p+3))+1 );
81                 /* Check for correct type */
82
83                 if (p[1] & 0x8000)
84                 {
85                     if (!HIWORD(typeId)) continue;
86                     if (strcasecmp( typeId, (char *)(p + 3) )) continue;
87                 }
88                 else if (HIWORD(typeId) || (((DWORD)typeId & ~0x8000)!= p[1]))
89                   continue;
90
91                 /* Now check for the id */
92
93                 if (p[2] & 0x8000)
94                 {
95                     if (!HIWORD(resId)) continue;
96                     if (strcasecmp( resId, (char*)(p+3)+strlen((char*)(p+3))+1 )) continue;
97
98                 }
99                 else if (HIWORD(resId) || (((DWORD)resId & ~0x8000) != p[2]))
100                   continue;
101
102                 /* If we get here, we've found the entry */
103
104                 TRACE("  Found!\n" );
105                 ret = MAKELONG( p[1], p[2] );
106                 break;
107             }
108             FreeResource16( handle );
109             if (ret) return ret;
110         }
111     }
112     return 0;
113 }
114
115 /***********************************************************************
116  *           NE_FindTypeSection
117  *
118  * Find header struct for a particular resource type.
119  */
120 NE_TYPEINFO *NE_FindTypeSection( LPBYTE pResTab,
121                                  NE_TYPEINFO *pTypeInfo, LPCSTR typeId )
122 {
123     /* start from pTypeInfo */
124
125     if (HIWORD(typeId) != 0)  /* Named type */
126     {
127         LPCSTR str = typeId;
128         BYTE len = strlen( str );
129         while (pTypeInfo->type_id)
130         {
131             if (!(pTypeInfo->type_id & 0x8000))
132             {
133                 BYTE *p = pResTab + pTypeInfo->type_id;
134                 if ((*p == len) && !strncasecmp( p+1, str, len ))
135                 {
136                     TRACE("  Found type '%s'\n", str );
137                     return pTypeInfo;
138                 }
139             }
140             TRACE("  Skipping type %04x\n", pTypeInfo->type_id );
141             pTypeInfo = NEXT_TYPEINFO(pTypeInfo);
142         }
143     }
144     else  /* Numeric type id */
145     {
146         WORD id = LOWORD(typeId) | 0x8000;
147         while (pTypeInfo->type_id)
148         {
149             if (pTypeInfo->type_id == id)
150             {
151                 TRACE("  Found type %04x\n", id );
152                 return pTypeInfo;
153             }
154             TRACE("  Skipping type %04x\n", pTypeInfo->type_id );
155             pTypeInfo = NEXT_TYPEINFO(pTypeInfo);
156         }
157     }
158     return NULL;
159 }
160
161 /***********************************************************************
162  *           NE_FindResourceFromType
163  *
164  * Find a resource once the type info structure has been found.
165  */
166 NE_NAMEINFO *NE_FindResourceFromType( LPBYTE pResTab,
167                                       NE_TYPEINFO *pTypeInfo, LPCSTR resId )
168 {
169     BYTE *p;
170     int count;
171     NE_NAMEINFO *pNameInfo = (NE_NAMEINFO *)(pTypeInfo + 1);
172
173     if (HIWORD(resId) != 0)  /* Named resource */
174     {
175         LPCSTR str = resId;
176         BYTE len = strlen( str );
177         for (count = pTypeInfo->count; count > 0; count--, pNameInfo++)
178         {
179             if (pNameInfo->id & 0x8000) continue;
180             p = pResTab + pNameInfo->id;
181             if ((*p == len) && !strncasecmp( p+1, str, len ))
182                 return pNameInfo;
183         }
184     }
185     else  /* Numeric resource id */
186     {
187         WORD id = LOWORD(resId) | 0x8000;
188         for (count = pTypeInfo->count; count > 0; count--, pNameInfo++)
189             if (pNameInfo->id == id)
190                 return pNameInfo;
191     }
192     return NULL;
193 }
194
195
196 /***********************************************************************
197  *           DefResourceHandler (KERNEL.456)
198  *
199  * This is the default LoadProc() function.
200  */
201 HGLOBAL16 WINAPI NE_DefResourceHandler( HGLOBAL16 hMemObj, HMODULE16 hModule,
202                                         HRSRC16 hRsrc )
203 {
204     HANDLE fd;
205     NE_MODULE* pModule = NE_GetPtr( hModule );
206     if (pModule && (pModule->flags & NE_FFLAGS_BUILTIN))
207     {
208         HGLOBAL16 handle;
209         WORD sizeShift = *(WORD *)((char *)pModule + pModule->res_table);
210         NE_NAMEINFO* pNameInfo = (NE_NAMEINFO*)((char*)pModule + hRsrc);
211
212         if ( hMemObj )
213             handle = GlobalReAlloc16( hMemObj, pNameInfo->length << sizeShift, 0 );
214         else
215             handle = AllocResource16( hModule, hRsrc, 0 );
216
217         if ( handle )
218         {
219             /* NOTE: hRsrcMap points to start of built-in resource data */
220             memcpy( GlobalLock16( handle ),
221                     (char *)pModule->hRsrcMap + (pNameInfo->offset << sizeShift),
222                     pNameInfo->length << sizeShift );
223         }
224         return handle;
225     }
226     if (pModule && (fd = NE_OpenFile( pModule )) != INVALID_HANDLE_VALUE)
227     {
228         HGLOBAL16 handle;
229         WORD sizeShift = *(WORD *)((char *)pModule + pModule->res_table);
230         NE_NAMEINFO* pNameInfo = (NE_NAMEINFO*)((char*)pModule + hRsrc);
231
232         TRACE("loading, pos=%d, len=%d\n",
233                      (int)pNameInfo->offset << sizeShift,
234                      (int)pNameInfo->length << sizeShift );
235         if( hMemObj )
236             handle = GlobalReAlloc16( hMemObj, pNameInfo->length << sizeShift, 0 );
237         else
238             handle = AllocResource16( hModule, hRsrc, 0 );
239
240         if( handle )
241         {
242             DWORD res;
243             SetFilePointer( fd, (int)pNameInfo->offset << sizeShift, NULL, SEEK_SET );
244             ReadFile( fd, GlobalLock16( handle ), (int)pNameInfo->length << sizeShift,
245                       &res, NULL );
246         }
247         return handle;
248     }
249     return (HGLOBAL16)0;
250 }
251
252 /***********************************************************************
253  *           NE_InitResourceHandler
254  *
255  * Fill in 'resloader' fields in the resource table.
256  */
257 BOOL NE_InitResourceHandler( HMODULE16 hModule )
258 {
259     NE_MODULE *pModule = NE_GetPtr( hModule );
260     NE_TYPEINFO *pTypeInfo = (NE_TYPEINFO *)((char *)pModule + pModule->res_table + 2);
261
262     if ( DefResourceHandlerProc == (FARPROC16)0xffffffff )
263     {
264         HMODULE16 hModule = GetModuleHandle16( "KERNEL" );
265         int ordinal = hModule? NE_GetOrdinal( hModule, "DefResourceHandler" ) : 0;
266
267         if ( ordinal )
268             DefResourceHandlerProc = NE_GetEntryPointEx( hModule, ordinal, FALSE );
269         else
270             DefResourceHandlerProc = NULL;
271     }
272
273     TRACE("InitResourceHandler[%04x]\n", hModule );
274
275     while(pTypeInfo->type_id)
276     {
277         memcpy( &pTypeInfo->resloader, &DefResourceHandlerProc, sizeof(FARPROC16) );
278         pTypeInfo = NEXT_TYPEINFO(pTypeInfo);
279     }
280     return TRUE;
281 }
282
283
284 /**********************************************************************
285  *      SetResourceHandler      (KERNEL.67)
286  */
287 FARPROC16 WINAPI SetResourceHandler16( HMODULE16 hModule, LPCSTR typeId,
288                                      FARPROC16 resourceHandler )
289 {
290     FARPROC16 prevHandler = NULL;
291     NE_MODULE *pModule = NE_GetPtr( hModule );
292     LPBYTE pResTab = (LPBYTE)pModule + pModule->res_table;
293     NE_TYPEINFO *pTypeInfo = (NE_TYPEINFO *)(pResTab + 2);
294
295     if (!pModule || !pModule->res_table) return NULL;
296
297     TRACE("module=%04x type=%s\n", hModule, debugstr_a(typeId) );
298
299     for (;;)
300     {
301         if (!(pTypeInfo = NE_FindTypeSection( pResTab, pTypeInfo, typeId )))
302             break;
303         memcpy( &prevHandler, &pTypeInfo->resloader, sizeof(FARPROC16) );
304         memcpy( &pTypeInfo->resloader, &resourceHandler, sizeof(FARPROC16) );
305         pTypeInfo = NEXT_TYPEINFO(pTypeInfo);
306     }
307     return prevHandler;
308 }
309
310
311 /**********************************************************************
312  *          NE_FindResource
313  */
314 HRSRC16 NE_FindResource( NE_MODULE *pModule, LPCSTR name, LPCSTR type )
315 {
316     NE_TYPEINFO *pTypeInfo;
317     NE_NAMEINFO *pNameInfo;
318     LPBYTE pResTab;
319
320     if (!pModule || !pModule->res_table) return 0;
321
322     TRACE("module=%04x name=%s type=%s\n", pModule->self, debugstr_a(name), debugstr_a(type) );
323
324     if (HIWORD(name))  /* Check for '#xxx' name */
325     {
326         LPCSTR ptr = name;
327         if (ptr[0] == '#')
328             if (!(name = (LPCSTR)atoi( ptr + 1 )))
329             {
330                 WARN("Incorrect resource name: %s\n", ptr);
331                 return 0;
332             }
333     }
334
335     if (HIWORD(type))  /* Check for '#xxx' type */
336     {
337         LPCSTR ptr = type;
338         if (ptr[0] == '#')
339             if (!(type = (LPCSTR)atoi( ptr + 1 )))
340             {
341                 WARN("Incorrect resource type: %s\n", ptr);
342                 return 0;
343             }
344     }
345
346     if (HIWORD(type) || HIWORD(name))
347     {
348         DWORD id = NE_FindNameTableId( pModule, type, name );
349         if (id)  /* found */
350         {
351             type = (LPCSTR)(int)LOWORD(id);
352             name = (LPCSTR)(int)HIWORD(id);
353         }
354     }
355
356     pResTab = (LPBYTE)pModule + pModule->res_table;
357     pTypeInfo = (NE_TYPEINFO *)( pResTab + 2 );
358
359     for (;;)
360     {
361         if (!(pTypeInfo = NE_FindTypeSection( pResTab, pTypeInfo, type )))
362             break;
363         if ((pNameInfo = NE_FindResourceFromType( pResTab, pTypeInfo, name )))
364         {
365             TRACE("    Found id %08lx\n", (DWORD)name );
366             return (HRSRC16)( (int)pNameInfo - (int)pModule );
367         }
368         TRACE("    Not found, going on\n" );
369         pTypeInfo = NEXT_TYPEINFO(pTypeInfo);
370     }
371
372     WARN("failed!\n");
373     return 0;
374 }
375
376
377 /**********************************************************************
378  *          AllocResource    (KERNEL.66)
379  */
380 HGLOBAL16 WINAPI AllocResource16( HMODULE16 hModule, HRSRC16 hRsrc, DWORD size)
381 {
382     NE_NAMEINFO *pNameInfo=NULL;
383     WORD sizeShift;
384     HGLOBAL16 ret;
385
386     NE_MODULE *pModule = NE_GetPtr( hModule );
387     if (!pModule || !pModule->res_table || !hRsrc) return 0;
388
389     TRACE("module=%04x res=%04x size=%ld\n", hModule, hRsrc, size );
390
391     sizeShift = *(WORD *)((char *)pModule + pModule->res_table);
392     pNameInfo = (NE_NAMEINFO*)((char*)pModule + hRsrc);
393     if (size < (DWORD)pNameInfo->length << sizeShift)
394         size = (DWORD)pNameInfo->length << sizeShift;
395     ret = GlobalAlloc16( GMEM_FIXED, size );
396     if (ret) FarSetOwner16( ret, hModule );
397     return ret;
398 }
399
400
401 /**********************************************************************
402  *      DirectResAlloc    (KERNEL.168)
403  *
404  * Check Schulman, p. 232 for details
405  */
406 HGLOBAL16 WINAPI DirectResAlloc16( HINSTANCE16 hInstance, WORD wType,
407                                  UINT16 wSize )
408 {
409     HGLOBAL16 ret;
410     TRACE("(%04x,%04x,%04x)\n", hInstance, wType, wSize );
411     if (!(hInstance = GetExePtr( hInstance ))) return 0;
412     if(wType != 0x10)   /* 0x10 is the only observed value, passed from
413                            CreateCursorIndirect. */
414         TRACE("(wType=%x)\n", wType);
415     ret = GlobalAlloc16( GMEM_MOVEABLE, wSize );
416     if (ret) FarSetOwner16( ret, hInstance );
417     return ret;
418 }
419
420
421 /**********************************************************************
422  *          AccessResource (KERNEL.64)
423  */
424 INT16 WINAPI AccessResource16( HINSTANCE16 hModule, HRSRC16 hRsrc )
425 {
426     HFILE16 fd;
427     NE_MODULE *pModule = NE_GetPtr( hModule );
428
429     if (!pModule || !pModule->res_table || !hRsrc) return -1;
430
431     TRACE("module=%04x res=%04x\n", pModule->self, hRsrc );
432
433     if ((fd = _lopen16( NE_MODULE_NAME(pModule), OF_READ )) != HFILE_ERROR16)
434     {
435         WORD sizeShift = *(WORD *)((char *)pModule + pModule->res_table);
436         NE_NAMEINFO *pNameInfo = (NE_NAMEINFO*)((char*)pModule + hRsrc);
437         _llseek16( fd, (int)pNameInfo->offset << sizeShift, SEEK_SET );
438     }
439     return fd;
440 }
441
442
443 /**********************************************************************
444  *          NE_SizeofResource
445  */
446 DWORD NE_SizeofResource( NE_MODULE *pModule, HRSRC16 hRsrc )
447 {
448     NE_NAMEINFO *pNameInfo=NULL;
449     WORD sizeShift;
450
451     if (!pModule || !pModule->res_table) return 0;
452
453     TRACE("module=%04x res=%04x\n", pModule->self, hRsrc );
454
455     sizeShift = *(WORD *)((char *)pModule + pModule->res_table);
456     pNameInfo = (NE_NAMEINFO*)((char*)pModule + hRsrc);
457     return (DWORD)pNameInfo->length << sizeShift;
458 }
459
460
461 /**********************************************************************
462  *          NE_LoadResource
463  */
464 HGLOBAL16 NE_LoadResource( NE_MODULE *pModule, HRSRC16 hRsrc )
465 {
466     NE_TYPEINFO *pTypeInfo;
467     NE_NAMEINFO *pNameInfo = NULL;
468     int d;
469
470     TRACE("module=%04x res=%04x\n", pModule->self, hRsrc );
471     if (!hRsrc || !pModule || !pModule->res_table) return 0;
472
473     /* First, verify hRsrc (just an offset from pModule to the needed pNameInfo) */
474
475     d = pModule->res_table + 2;
476     pTypeInfo = (NE_TYPEINFO *)((char *)pModule + d);
477     while( hRsrc > d )
478     {
479         if (pTypeInfo->type_id == 0)
480                 break; /* terminal entry */
481         d += sizeof(NE_TYPEINFO) + pTypeInfo->count * sizeof(NE_NAMEINFO);
482         if (hRsrc < d)
483         {
484             if( ((d - hRsrc)%sizeof(NE_NAMEINFO)) == 0 )
485             {
486                 pNameInfo = (NE_NAMEINFO *)(((char *)pModule) + hRsrc);
487                 break;
488             }
489             else
490                 break; /* NE_NAMEINFO boundary mismatch */
491         }
492         pTypeInfo = (NE_TYPEINFO *)(((char *)pModule) + d);
493     }
494
495     if (pNameInfo)
496     {
497         if (pNameInfo->handle
498             && !(GlobalFlags16(pNameInfo->handle) & GMEM_DISCARDED))
499         {
500             pNameInfo->usage++;
501             TRACE("  Already loaded, new count=%d\n",
502                               pNameInfo->usage );
503         }
504         else
505         {
506             FARPROC16 resloader;
507             memcpy( &resloader, &pTypeInfo->resloader, sizeof(FARPROC16) );
508             if ( resloader && resloader != DefResourceHandlerProc )
509                 pNameInfo->handle = NE_CallTo16_word_www(
510                     resloader, pNameInfo->handle, pModule->self, hRsrc );
511             else
512                 pNameInfo->handle = NE_DefResourceHandler(
513                                          pNameInfo->handle, pModule->self, hRsrc );
514
515             if (pNameInfo->handle)
516             {
517                 pNameInfo->usage++;
518                 pNameInfo->flags |= NE_SEGFLAGS_LOADED;
519             }
520         }
521         return pNameInfo->handle;
522     }
523     return 0;
524 }
525
526
527 /**********************************************************************
528  *          NE_FreeResource
529  */
530 BOOL16 NE_FreeResource( NE_MODULE *pModule, HGLOBAL16 handle )
531 {
532     NE_TYPEINFO *pTypeInfo;
533     NE_NAMEINFO *pNameInfo;
534     WORD count;
535
536     if (!handle || !pModule || !pModule->res_table) return handle;
537
538     TRACE("handle=%04x\n", handle );
539
540     pTypeInfo = (NE_TYPEINFO *)((char *)pModule + pModule->res_table + 2);
541     while (pTypeInfo->type_id)
542     {
543         pNameInfo = (NE_NAMEINFO *)(pTypeInfo + 1);
544         for (count = pTypeInfo->count; count > 0; count--)
545         {
546             if (pNameInfo->handle == handle)
547             {
548                 if (pNameInfo->usage > 0) pNameInfo->usage--;
549                 if (pNameInfo->usage == 0)
550                 {
551                     GlobalFree16( pNameInfo->handle );
552                     pNameInfo->handle = 0;
553                     pNameInfo->flags &= ~NE_SEGFLAGS_LOADED;
554                 }
555                 return 0;
556             }
557             pNameInfo++;
558         }
559         pTypeInfo = (NE_TYPEINFO *)pNameInfo;
560     }
561
562     return handle;
563 }