New implementation of the win95 registry loader.
[wine] / misc / registry.c
1 /*
2  *      Registry Functions
3  *
4  * Copyright 1996 Marcus Meissner
5  * Copyright 1998 Matthew Becker
6  * Copyright 1999 Sylvain St-Germain
7  *
8  * December 21, 1997 - Kevin Cozens
9  * Fixed bugs in the _w95_loadreg() function. Added extra information
10  * regarding the format of the Windows '95 registry files.
11  *
12  * NOTES
13  *    When changing this file, please re-run the regtest program to ensure
14  *    the conditions are handled properly.
15  *
16  * TODO
17  *    Security access
18  *    Option handling
19  *    Time for RegEnumKey*, RegQueryInfoKey*
20  */
21
22 #include "config.h"
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <ctype.h>
28 #include <errno.h>
29 #ifdef HAVE_SYS_ERRNO_H
30 #include <sys/errno.h>
31 #endif
32 #include <sys/types.h>
33 #include <fcntl.h>
34 #include <sys/fcntl.h>
35 #include <sys/stat.h>
36 #include <assert.h>
37 #include <time.h>
38 #include "windef.h"
39 #include "winbase.h"
40 #include "wine/winbase16.h"
41 #include "wine/winestring.h"
42 #include "winerror.h"
43 #include "file.h"
44 #include "heap.h"
45 #include "debugtools.h"
46 #include "xmalloc.h"
47 #include "options.h"
48 #include "winreg.h"
49 #include "server.h"
50 #include "services.h"
51
52 DEFAULT_DEBUG_CHANNEL(reg)
53
54 static void REGISTRY_Init(void);
55 /* FIXME: following defines should be configured global ... */
56
57 /* NOTE: do not append a /. linux' mkdir() WILL FAIL if you do that */
58 #define WINE_PREFIX                 "/.wine"
59 #define SAVE_USERS_DEFAULT          ETCDIR"/wine.userreg"
60 #define SAVE_LOCAL_MACHINE_DEFAULT  ETCDIR"/wine.systemreg"
61
62 /* relative in ~user/.wine/ : */
63 #define SAVE_CURRENT_USER           "user.reg"
64 #define SAVE_LOCAL_USERS_DEFAULT    "wine.userreg"
65 #define SAVE_LOCAL_MACHINE          "system.reg"
66
67 #define KEY_REGISTRY                "Software\\The WINE team\\WINE\\Registry"
68 #define VAL_SAVEUPDATED             "SaveOnlyUpdatedKeys"
69
70
71 /* what valuetypes do we need to convert? */
72 #define UNICONVMASK     ((1<<REG_SZ)|(1<<REG_MULTI_SZ)|(1<<REG_EXPAND_SZ))
73
74
75
76 /*
77  * QUESTION
78  *   Are these doing the same as HEAP_strdupAtoW and HEAP_strdupWtoA?
79  *   If so, can we remove them?
80  * ANSWER
81  *   No, the memory handling functions are called very often in here, 
82  *   just replacing them by HeapAlloc(SystemHeap,...) makes registry
83  *   loading 100 times slower. -MM
84  */
85 static LPWSTR strdupA2W(LPCSTR src)
86 {
87     if(src) {
88         LPWSTR dest=xmalloc(2*strlen(src)+2);
89         lstrcpyAtoW(dest,src);
90         return dest;
91     }
92     return NULL;
93 }
94
95 LPWSTR strcvtA2W(LPCSTR src, int nchars)
96
97 {
98    LPWSTR dest = xmalloc (2 * nchars + 2);
99
100    lstrcpynAtoW(dest,src,nchars+1);
101    return dest;
102 }
103
104
105
106 /******************************************************************************
107  * REGISTRY_Init [Internal]
108  * Registry initialisation, allocates some default keys. 
109  */
110 static void REGISTRY_Init(void) {
111         HKEY    hkey;
112         char    buf[200];
113
114         TRACE("(void)\n");
115
116         RegCreateKeyA(HKEY_DYN_DATA,"PerfStats\\StatData",&hkey);
117         RegCloseKey(hkey);
118
119         /* This was an Open, but since it is called before the real registries
120            are loaded, it was changed to a Create - MTB 980507*/
121         RegCreateKeyA(HKEY_LOCAL_MACHINE,"HARDWARE\\DESCRIPTION\\System",&hkey);
122         RegSetValueExA(hkey,"Identifier",0,REG_SZ,"SystemType WINE",strlen("SystemType WINE"));
123         RegCloseKey(hkey);
124
125         /* \\SOFTWARE\\Microsoft\\Window NT\\CurrentVersion
126          *                                              CurrentVersion
127          *                                              CurrentBuildNumber
128          *                                              CurrentType
129          *                                      string  RegisteredOwner
130          *                                      string  RegisteredOrganization
131          *
132          */
133         /* System\\CurrentControlSet\\Services\\SNMP\\Parameters\\RFC1156Agent
134          *                                      string  SysContact
135          *                                      string  SysLocation
136          *                                              SysServices
137          */
138         if (-1!=gethostname(buf,200)) {
139                 RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\CurrentControlSet\\Control\\ComputerName\\ComputerName",&hkey);
140                 RegSetValueExA(hkey,"ComputerName",0,REG_SZ,buf,strlen(buf)+1);
141                 RegCloseKey(hkey);
142         }
143 }
144
145
146 /************************ SAVE Registry Function ****************************/
147
148 #define REGISTRY_SAVE_VERSION   0x00000001
149
150 /* Registry saveformat:
151  * If you change it, increase above number by 1, which will flush
152  * old registry database files.
153  * 
154  * Global:
155  *      "WINE REGISTRY Version %d"
156  *      subkeys....
157  * Subkeys:
158  *      keyname
159  *              valuename=lastmodified,type,data
160  *              ...
161  *              subkeys
162  *      ...
163  * keyname,valuename,stringdata:
164  *      the usual ascii characters from 0x00-0xff (well, not 0x00)
165  *      and \uXXXX as UNICODE value XXXX with XXXX>0xff
166  *      ( "=\\\t" escaped in \uXXXX form.)
167  * type,lastmodified: 
168  *      int
169  * 
170  * FIXME: doesn't save 'class' (what does it mean anyway?), nor flags.
171  *
172  * [HKEY_CURRENT_USER\\Software\\The WINE team\\WINE\\Registry]
173  * SaveOnlyUpdatedKeys=yes
174  */
175
176 /* Same as RegSaveKey but with Unix pathnames */
177 static void save_key( HKEY hkey, const char *filename )
178 {
179     struct save_registry_request *req = get_req_buffer();
180     int count = 0;
181     DWORD ret;
182     HANDLE handle;
183     char *p;
184     char *name = HeapAlloc( GetProcessHeap(), 0, strlen(filename) + 20 );
185
186     if (!name) return;
187     strcpy( name, filename );
188     if ((p = strrchr( name, '/' ))) p++;
189     else p = name;
190
191     for (;;)
192     {
193         sprintf( p, "reg%04x.tmp", count++ );
194         handle = FILE_CreateFile( name, GENERIC_WRITE, 0, NULL,
195                                   CREATE_NEW, FILE_ATTRIBUTE_NORMAL, -1 );
196         if (handle != INVALID_HANDLE_VALUE) break;
197         if ((ret = GetLastError()) != ERROR_FILE_EXISTS) break;
198     }
199
200     if (handle != INVALID_HANDLE_VALUE)
201     {
202         req->hkey = hkey;
203         req->file = handle;
204         ret = server_call_noerr( REQ_SAVE_REGISTRY );
205         CloseHandle( handle );
206         if (ret) unlink( name );
207         else if (rename( name, filename ) == -1)
208         {
209             ERR( "Failed to move %s to %s: ", name, filename );
210             perror( "rename" );
211             unlink( name );
212         }
213     }
214     HeapFree( GetProcessHeap(), 0, name );
215 }
216
217
218 /******************************************************************************
219  * SHELL_SaveRegistryBranch [Internal]
220  *
221  * Saves main registry branch specified by hkey.
222  */
223 static void SHELL_SaveRegistryBranch(HKEY hkey)
224 {
225     char   *fn, *home;
226
227     /* Find out what to save to, get from config file */
228     BOOL writeToHome = PROFILE_GetWineIniBool("registry","WritetoHomeRegistries",1);
229     BOOL writeToAlt = PROFILE_GetWineIniBool("registry","WritetoAltRegistries",1);
230
231     /* FIXME: does this check apply to all keys written below ? */
232     if (!(home = getenv( "HOME" )))
233         ERR("Failed to get homedirectory of UID %ld.\n",(long) getuid());
234
235     /* HKEY_LOCAL_MACHINE contains the HKEY_CLASSES_ROOT branch */
236     if (hkey == HKEY_CLASSES_ROOT) hkey = HKEY_LOCAL_MACHINE;
237
238     switch (hkey)
239     {
240     case HKEY_CURRENT_USER:
241         fn = xmalloc( MAX_PATHNAME_LEN ); 
242         if (writeToAlt && PROFILE_GetWineIniString( "registry", "AltCurrentUserFile", "",
243                                                     fn, MAX_PATHNAME_LEN - 1))
244             save_key( HKEY_CURRENT_USER, fn );
245         free (fn);
246
247         if (home && writeToHome)
248         {
249             fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
250                                strlen(SAVE_CURRENT_USER) + 2 );
251             strcpy(fn,home);
252             strcat(fn,WINE_PREFIX);
253   
254             /* create the directory. don't care about errorcodes. */
255             mkdir(fn,0755); /* drwxr-xr-x */
256             strcat(fn,"/"SAVE_CURRENT_USER);
257             save_key( HKEY_CURRENT_USER, fn );
258             free(fn);
259         }
260         break;
261     case HKEY_LOCAL_MACHINE:
262         /* Try first saving according to the defined location in .winerc */
263         fn = xmalloc ( MAX_PATHNAME_LEN);
264         if (writeToAlt && PROFILE_GetWineIniString( "Registry", "AltLocalMachineFile", "", 
265                                                     fn, MAX_PATHNAME_LEN - 1))
266             save_key( HKEY_LOCAL_MACHINE, fn );
267         free (fn);
268
269         if (home && writeToHome)
270         {
271             fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
272                                strlen(SAVE_LOCAL_MACHINE) + 2);
273             strcpy(fn,home);
274             strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE);
275             save_key( HKEY_LOCAL_MACHINE, fn );
276             free(fn);
277         }
278         break;
279     case HKEY_USERS:
280         fn = xmalloc( MAX_PATHNAME_LEN );
281         if (writeToAlt && PROFILE_GetWineIniString( "Registry", "AltUserFile", "", 
282                                                     fn, MAX_PATHNAME_LEN - 1))
283             save_key( HKEY_USERS, fn );
284         free (fn);
285
286         if (home && writeToHome)
287         {
288             fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
289                                strlen(SAVE_LOCAL_USERS_DEFAULT) + 2);
290             strcpy(fn,home);
291             strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
292             save_key( HKEY_USERS, fn );
293             free(fn);
294         }
295         break;
296     default:
297         ERR("unknown/invalid key handle !\n");
298         break;
299     }
300 }
301
302
303 /******************************************************************************
304  * SHELL_SaveRegistry [Internal]
305  */
306 void SHELL_SaveRegistry( void )
307 {
308     struct set_registry_levels_request *req = get_req_buffer();
309     char   buf[4];
310     HKEY   hkey;
311     int    all;
312
313     TRACE("(void)\n");
314
315     all=0;
316     if (RegOpenKeyA(HKEY_CURRENT_USER,KEY_REGISTRY,&hkey)!=ERROR_SUCCESS) 
317     {
318         strcpy(buf,"yes");
319     } 
320     else 
321     {
322         DWORD len,junk,type;
323
324         len=4;
325         if ((ERROR_SUCCESS!=RegQueryValueExA( hkey,
326                                               VAL_SAVEUPDATED,
327                                               &junk,
328                                               &type,
329                                               buf,
330                                               &len)) || (type!=REG_SZ))
331         {
332             strcpy(buf,"yes");
333         }
334         RegCloseKey(hkey);
335     }
336
337     if (lstrcmpiA(buf,"yes")) all = 1;
338
339     /* set saving level (0 for saving everything, 1 for saving only modified keys) */
340     req->current = 1;
341     req->saving  = !all;
342     req->version = PROFILE_GetWineIniBool( "registry", "UseNewFormat", 0 ) ? 2 : 1;
343     server_call( REQ_SET_REGISTRY_LEVELS );
344
345     SHELL_SaveRegistryBranch(HKEY_CURRENT_USER);
346     SHELL_SaveRegistryBranch(HKEY_LOCAL_MACHINE);
347     SHELL_SaveRegistryBranch(HKEY_USERS);
348 }
349
350 /* Periodic save callback */
351 static void CALLBACK periodic_save( ULONG_PTR dummy )
352 {
353     SHELL_SaveRegistry();
354 }
355
356 /************************ LOAD Registry Function ****************************/
357
358
359
360 /******************************************************************************
361  * _find_or_add_key [Internal]
362  */
363 static inline HKEY _find_or_add_key( HKEY hkey, LPWSTR keyname )
364 {
365     HKEY subkey;
366     if (RegCreateKeyW( hkey, keyname, &subkey ) != ERROR_SUCCESS) subkey = 0;
367     if (keyname) free( keyname );
368     return subkey;
369 }
370
371 /******************************************************************************
372  * _find_or_add_value [Internal]
373  */
374 static void _find_or_add_value( HKEY hkey, LPWSTR name, DWORD type, LPBYTE data, DWORD len )
375 {
376     RegSetValueExW( hkey, name, 0, type, data, len );
377     if (name) free( name );
378     if (data) free( data );
379 }
380
381
382 /******************************************************************************
383  * _wine_read_line [Internal]
384  *
385  * reads a line including dynamically enlarging the readbuffer and throwing
386  * away comments
387  */
388 static int _wine_read_line( FILE *F, char **buf, int *len )
389 {
390         char    *s,*curread;
391         int     mylen,curoff;
392
393         curread = *buf;
394         mylen   = *len;
395         **buf   = '\0';
396         while (1) {
397                 while (1) {
398                         s=fgets(curread,mylen,F);
399                         if (s==NULL)
400                                 return 0; /* EOF */
401                         if (NULL==(s=strchr(curread,'\n'))) {
402                                 /* buffer wasn't large enough */
403                                 curoff  = strlen(*buf);
404                                 *buf    = xrealloc(*buf,*len*2);
405                                 curread = *buf + curoff;
406                                 mylen   = *len; /* we filled up the buffer and 
407                                                  * got new '*len' bytes to fill
408                                                  */
409                                 *len    = *len * 2;
410                         } else {
411                                 *s='\0';
412                                 break;
413                         }
414                 }
415                 /* throw away comments */
416                 if (**buf=='#' || **buf==';') {
417                         curread = *buf;
418                         mylen   = *len;
419                         continue;
420                 }
421                 if (s)  /* got end of line */
422                         break;
423         }
424         return 1;
425 }
426
427
428 /******************************************************************************
429  * _wine_read_USTRING [Internal]
430  *
431  * converts a char* into a UNICODE string (up to a special char)
432  * and returns the position exactly after that string
433  */
434 static char* _wine_read_USTRING( char *buf, LPWSTR *str )
435 {
436         char    *s;
437         LPWSTR  ws;
438
439         /* read up to "=" or "\0" or "\n" */
440         s       = buf;
441         *str    = (LPWSTR)xmalloc(2*strlen(buf)+2);
442         ws      = *str;
443         while (*s && (*s!='\n') && (*s!='=')) {
444                 if (*s!='\\')
445                         *ws++=*((unsigned char*)s++);
446                 else {
447                         s++;
448                         if (!*s) {
449                                 /* Dangling \ ... may only happen if a registry
450                                  * write was short. FIXME: What do to?
451                                  */
452                                  break;
453                         }
454                         if (*s=='\\') {
455                                 *ws++='\\';
456                                 s++;
457                                 continue;
458                         }
459                         if (*s!='u') {
460                                 WARN("Non unicode escape sequence \\%c found in |%s|\n",*s,buf);
461                                 *ws++='\\';
462                                 *ws++=*s++;
463                         } else {
464                                 char    xbuf[5];
465                                 int     wc;
466
467                                 s++;
468                                 memcpy(xbuf,s,4);xbuf[4]='\0';
469                                 if (!sscanf(xbuf,"%x",&wc))
470                                         WARN("Strange escape sequence %s found in |%s|\n",xbuf,buf);
471                                 s+=4;
472                                 *ws++   =(unsigned short)wc;
473                         }
474                 }
475         }
476         *ws     = 0;
477         return s;
478 }
479
480
481 /******************************************************************************
482  * _wine_loadsubkey [Internal]
483  *
484  * NOTES
485  *    It seems like this is returning a boolean.  Should it?
486  *
487  * RETURNS
488  *    Success: 1
489  *    Failure: 0
490  */
491 static int _wine_loadsubkey( FILE *F, HKEY hkey, int level, char **buf, int *buflen )
492 {
493         HKEY subkey;
494         int             i;
495         char            *s;
496         LPWSTR          name;
497
498     TRACE("(%p,%x,%d,%s,%d)\n", F, hkey, level, debugstr_a(*buf), *buflen);
499
500     /* Good.  We already got a line here ... so parse it */
501     subkey = 0;
502     while (1) {
503         i=0;s=*buf;
504         while (*s=='\t') {
505             s++;
506             i++;
507         }
508         if (i>level) {
509             if (!subkey) {
510                 WARN("Got a subhierarchy without resp. key?\n");
511                 return 0;
512             }
513             if (!_wine_loadsubkey(F,subkey,level+1,buf,buflen))
514                if (!_wine_read_line(F,buf,buflen))
515                   goto done;
516             continue;
517         }
518
519                 /* let the caller handle this line */
520                 if (i<level || **buf=='\0')
521                         goto done;
522
523                 /* it can be: a value or a keyname. Parse the name first */
524                 s=_wine_read_USTRING(s,&name);
525
526                 /* switch() default: hack to avoid gotos */
527                 switch (0) {
528                 default:
529                         if (*s=='\0') {
530                                 if (subkey) RegCloseKey( subkey );
531                                 subkey=_find_or_add_key(hkey,name);
532                         } else {
533                                 LPBYTE          data;
534                                 int             len,lastmodified,type;
535
536                                 if (*s!='=') {
537                                         WARN("Unexpected character: %c\n",*s);
538                                         break;
539                                 }
540                                 s++;
541                                 if (2!=sscanf(s,"%d,%d,",&type,&lastmodified)) {
542                                         WARN("Haven't understood possible value in |%s|, skipping.\n",*buf);
543                                         break;
544                                 }
545                                 /* skip the 2 , */
546                                 s=strchr(s,',');s++;
547                                 s=strchr(s,',');
548                                 if (!s++) {
549                                         WARN("Haven't understood possible value in |%s|, skipping.\n",*buf);
550                                         break;
551                                 }
552                                 if (type == REG_SZ || type == REG_EXPAND_SZ) {
553                                         s=_wine_read_USTRING(s,(LPWSTR*)&data);
554                                         len = lstrlenW((LPWSTR)data)*2+2;
555                                 } else {
556                                         len=strlen(s)/2;
557                                         data = (LPBYTE)xmalloc(len+1);
558                                         for (i=0;i<len;i++) {
559                                                 data[i]=0;
560                                                 if (*s>='0' && *s<='9')
561                                                         data[i]=(*s-'0')<<4;
562                                                 if (*s>='a' && *s<='f')
563                                                         data[i]=(*s-'a'+'\xa')<<4;
564                                                 if (*s>='A' && *s<='F')
565                                                         data[i]=(*s-'A'+'\xa')<<4;
566                                                 s++;
567                                                 if (*s>='0' && *s<='9')
568                                                         data[i]|=*s-'0';
569                                                 if (*s>='a' && *s<='f')
570                                                         data[i]|=*s-'a'+'\xa';
571                                                 if (*s>='A' && *s<='F')
572                                                         data[i]|=*s-'A'+'\xa';
573                                                 s++;
574                                         }
575                                 }
576                                 _find_or_add_value(hkey,name,type,data,len);
577                         }
578                 }
579                 /* read the next line */
580                 if (!_wine_read_line(F,buf,buflen))
581                         goto done;
582     }
583  done:
584     if (subkey) RegCloseKey( subkey );
585     return 1;
586 }
587
588
589 /******************************************************************************
590  * _wine_loadsubreg [Internal]
591  */
592 static int _wine_loadsubreg( FILE *F, HKEY hkey, const char *fn )
593 {
594         int     ver;
595         char    *buf;
596         int     buflen;
597
598         buf=xmalloc(10);buflen=10;
599         if (!_wine_read_line(F,&buf,&buflen)) {
600                 free(buf);
601                 return 0;
602         }
603         if (!sscanf(buf,"WINE REGISTRY Version %d",&ver)) {
604                 free(buf);
605                 return 0;
606         }
607         if (ver!=REGISTRY_SAVE_VERSION) {
608             if (ver == 2)  /* new version */
609             {
610                 HANDLE file;
611                 if ((file = FILE_CreateFile( fn, GENERIC_READ, 0, NULL, OPEN_EXISTING,
612                                              FILE_ATTRIBUTE_NORMAL, -1 )) != INVALID_HANDLE_VALUE)
613                 {
614                     struct load_registry_request *req = get_req_buffer();
615                     req->hkey    = hkey;
616                     req->file    = file;
617                     req->name[0] = 0;
618                     server_call( REQ_LOAD_REGISTRY );
619                     CloseHandle( file );
620                 }
621                 free( buf );
622                 return 1;
623             }
624             else
625             {
626                 TRACE("Old format (%d) registry found, ignoring it. (buf was %s).\n",ver,buf);
627                 free(buf);
628                 return 0;
629             }
630         }
631         if (!_wine_read_line(F,&buf,&buflen)) {
632                 free(buf);
633                 return 0;
634         }
635         if (!_wine_loadsubkey(F,hkey,0,&buf,&buflen)) {
636                 free(buf);
637                 return 0;
638         }
639         free(buf);
640         return 1;
641 }
642
643
644 /******************************************************************************
645  * _wine_loadreg [Internal]
646  */
647 static void _wine_loadreg( HKEY hkey, char *fn )
648 {
649     FILE *F;
650
651     TRACE("(%x,%s)\n",hkey,debugstr_a(fn));
652
653     F = fopen(fn,"rb");
654     if (F==NULL) {
655         WARN("Couldn't open %s for reading: %s\n",fn,strerror(errno) );
656         return;
657     }
658     _wine_loadsubreg(F,hkey,fn);
659     fclose(F);
660 }
661
662
663 /* NT REGISTRY LOADER */
664
665 #ifdef HAVE_SYS_MMAN_H
666 # include <sys/mman.h>
667 #endif
668
669 #ifndef MAP_FAILED
670 #define MAP_FAILED ((LPVOID)-1)
671 #endif
672
673 #define  NT_REG_BLOCK_SIZE              0x1000
674
675 #define NT_REG_HEADER_BLOCK_ID       0x66676572 /* regf */
676 #define NT_REG_POOL_BLOCK_ID         0x6E696268 /* hbin */
677 #define NT_REG_KEY_BLOCK_ID          0x6b6e
678 #define NT_REG_VALUE_BLOCK_ID        0x6b76
679 #define NT_REG_HASH_BLOCK_ID         0x666c
680 #define NT_REG_NOHASH_BLOCK_ID       0x696c
681 #define NT_REG_KEY_BLOCK_TYPE        0x20
682 #define NT_REG_ROOT_KEY_BLOCK_TYPE   0x2c
683
684 typedef struct 
685 {
686         DWORD   id;             /* 0x66676572 'regf'*/
687         DWORD   uk1;            /* 0x04 */
688         DWORD   uk2;            /* 0x08 */
689         FILETIME        DateModified;   /* 0x0c */
690         DWORD   uk3;            /* 0x14 */
691         DWORD   uk4;            /* 0x18 */
692         DWORD   uk5;            /* 0x1c */
693         DWORD   uk6;            /* 0x20 */
694         DWORD   RootKeyBlock;   /* 0x24 */
695         DWORD   BlockSize;      /* 0x28 */
696         DWORD   uk7[116];       
697         DWORD   Checksum; /* at offset 0x1FC */
698 } nt_regf;
699
700 typedef struct
701 {
702         DWORD   blocksize;
703         BYTE    data[1];
704 } nt_hbin_sub;
705
706 typedef struct
707 {
708         DWORD   id;             /* 0x6E696268 'hbin' */
709         DWORD   off_prev;
710         DWORD   off_next;
711         DWORD   uk1;
712         DWORD   uk2;            /* 0x10 */
713         DWORD   uk3;            /* 0x14 */
714         DWORD   uk4;            /* 0x18 */
715         DWORD   size;           /* 0x1C */
716         nt_hbin_sub     hbin_sub;       /* 0x20 */
717 } nt_hbin;
718
719 /*
720  * the value_list consists of offsets to the values (vk)
721  */
722 typedef struct
723 {
724         WORD    SubBlockId;             /* 0x00 0x6B6E */
725         WORD    Type;                   /* 0x02 for the root-key: 0x2C, otherwise 0x20*/
726         FILETIME        writetime;      /* 0x04 */
727         DWORD   uk1;                    /* 0x0C */
728         DWORD   parent_off;             /* 0x10 Offset of Owner/Parent key */
729         DWORD   nr_subkeys;             /* 0x14 number of sub-Keys */
730         DWORD   uk8;                    /* 0x18 */
731         DWORD   lf_off;                 /* 0x1C Offset of the sub-key lf-Records */
732         DWORD   uk2;                    /* 0x20 */
733         DWORD   nr_values;              /* 0x24 number of values */
734         DWORD   valuelist_off;          /* 0x28 Offset of the Value-List */
735         DWORD   off_sk;                 /* 0x2c Offset of the sk-Record */
736         DWORD   off_class;              /* 0x30 Offset of the Class-Name */
737         DWORD   uk3;                    /* 0x34 */
738         DWORD   uk4;                    /* 0x38 */
739         DWORD   uk5;                    /* 0x3c */
740         DWORD   uk6;                    /* 0x40 */
741         DWORD   uk7;                    /* 0x44 */
742         WORD    name_len;               /* 0x48 name-length */
743         WORD    class_len;              /* 0x4a class-name length */
744         char    name[1];                /* 0x4c key-name */
745 } nt_nk;
746
747 typedef struct
748 {
749         DWORD   off_nk; /* 0x00 */
750         DWORD   name;   /* 0x04 */
751 } hash_rec;
752
753 typedef struct
754 {
755         WORD    id;             /* 0x00 0x666c */
756         WORD    nr_keys;        /* 0x06 */
757         hash_rec        hash_rec[1];
758 } nt_lf;
759
760 typedef struct
761 {
762         WORD    id;             /* 0x00 0x696c */
763         WORD    nr_keys;
764         DWORD   off_nk[1];
765 } nt_il;
766
767 typedef struct
768 {
769         WORD    id;             /* 0x00 'vk' */
770         WORD    nam_len;
771         DWORD   data_len;
772         DWORD   data_off;
773         DWORD   type;
774         WORD    flag;
775         WORD    uk1;
776         char    name[1];
777 } nt_vk;
778
779 LPSTR _strdupnA( LPCSTR str, int len )
780 {
781     LPSTR ret;
782
783     if (!str) return NULL;
784     ret = malloc( len + 1 );
785     lstrcpynA( ret, str, len );
786     ret[len] = 0x00;
787     return ret;
788 }
789
790 static int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level);
791 static int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk);
792 static int _nt_parse_lf(HKEY hkey, char * base, nt_lf * lf, int level);
793
794
795 /*
796  * gets a value
797  *
798  * vk->flag:
799  *  0 value is a default value
800  *  1 the value has a name
801  *
802  * vk->data_len
803  *  len of the whole data block
804  *  - reg_sz (unicode)
805  *    bytes including the terminating \0 = 2*(number_of_chars+1)
806  *  - reg_dword, reg_binary:
807  *    if highest bit of data_len is set data_off contains the value
808  */
809 static int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk)
810 {
811         WCHAR name [256];
812         DWORD ret;
813         BYTE * pdata = (BYTE *)(base+vk->data_off+4); /* start of data */
814
815         if(vk->id != NT_REG_VALUE_BLOCK_ID) goto error;
816
817         lstrcpynAtoW(name, vk->name, vk->nam_len+1);
818
819         ret = RegSetValueExW( hkey, (vk->flag & 0x00000001) ? name : NULL, 0, vk->type,
820                         (vk->data_len & 0x80000000) ? (LPBYTE)&(vk->data_off): pdata,
821                         (vk->data_len & 0x7fffffff) );
822         if (ret) ERR("RegSetValueEx failed (0x%08lx)\n", ret);
823         return TRUE;
824 error:
825         ERR_(reg)("vk block invalid\n");
826         return FALSE;
827 }
828
829 /*
830  * get the subkeys
831  *
832  * this structure contains the hash of a keyname and points to all
833  * subkeys
834  *
835  * exception: if the id is 'il' there are no hash values and every 
836  * dword is a offset
837  */
838 static int _nt_parse_lf(HKEY hkey, char * base, nt_lf * lf, int level)
839 {
840         int i;
841
842         if (lf->id == NT_REG_HASH_BLOCK_ID)
843         {
844           for (i=0; i<lf->nr_keys; i++)
845           {
846             if (!_nt_parse_nk(hkey, base, (nt_nk*)(base+lf->hash_rec[i].off_nk+4), level)) goto error;
847           }
848           
849         }
850         else if (lf->id == NT_REG_NOHASH_BLOCK_ID)
851         {
852           for (i=0; i<lf->nr_keys; i++)
853           {
854             if (!_nt_parse_nk(hkey, base, (nt_nk*)(base+((nt_il*)lf)->off_nk[i]+4), level)) goto error;
855           }
856         }
857         return TRUE;
858         
859 error:  ERR_(reg)("error reading lf block\n");
860         return FALSE;
861 }
862
863 static int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level)
864 {
865         char * name;
866         int i;
867         DWORD * vl;
868         HKEY subkey = hkey;
869
870         if(nk->SubBlockId != NT_REG_KEY_BLOCK_ID) goto error;
871         if((nk->Type!=NT_REG_ROOT_KEY_BLOCK_TYPE) &&
872            (((nt_nk*)(base+nk->parent_off+4))->SubBlockId != NT_REG_KEY_BLOCK_ID)) goto error;
873
874         /* create the new key */
875         if(level <= 0)
876         {
877           name = _strdupnA( nk->name, nk->name_len+1);
878           if(RegCreateKeyA( hkey, name, &subkey )) { free(name); goto error; }
879           free(name);
880         }
881
882         /* loop through the subkeys */
883         if (nk->nr_subkeys)
884         {
885           nt_lf * lf = (nt_lf*)(base+nk->lf_off+4);
886           if (nk->nr_subkeys != lf->nr_keys) goto error1;
887           if (!_nt_parse_lf(subkey, base, lf, level-1)) goto error1;
888         }
889
890         /* loop trough the value list */
891         vl = (DWORD *)(base+nk->valuelist_off+4);
892         for (i=0; i<nk->nr_values; i++)
893         {
894           nt_vk * vk = (nt_vk*)(base+vl[i]+4);
895           if (!_nt_parse_vk(subkey, base, vk)) goto error1;
896         }
897
898         RegCloseKey(subkey);
899         return TRUE;
900         
901 error1: RegCloseKey(subkey);
902 error:  ERR_(reg)("error reading nk block\n");
903         return FALSE;
904 }
905
906 /* end nt loader */
907
908 /* windows 95 registry loader */
909
910 /* SECTION 1: main header
911  *
912  * once at offset 0
913  */
914 #define W95_REG_CREG_ID 0x47455243
915
916 typedef struct 
917 {
918         DWORD   id;             /* "CREG" = W95_REG_CREG_ID */
919         DWORD   version;        /* ???? 0x00010000 */
920         DWORD   rgdb_off;       /* 0x08 Offset of 1st RGDB-block */
921         DWORD   uk2;            /* 0x0c */
922         WORD    rgdb_num;       /* 0x10 # of RGDB-blocks */
923         WORD    uk3;
924         DWORD   uk[3];
925         /* rgkn */
926 } _w95creg;
927
928 /* SECTION 2: Directory information (tree structure)
929  *
930  * once on offset 0x20
931  *
932  * structure: [rgkn][dke]*      (repeat till rgkn->size is reached)
933  */
934 #define W95_REG_RGKN_ID 0x4e4b4752
935
936 typedef struct
937 {
938         DWORD   id;             /*"RGKN" = W95_REG_RGKN_ID */
939         DWORD   size;           /* Size of the RGKN-block */
940         DWORD   root_off;       /* Rel. Offset of the root-record */
941         DWORD   uk[5];
942 } _w95rgkn;
943
944 /* Disk Key Entry Structure
945  *
946  * the 1st entry in a "usual" registry file is a nul-entry with subkeys: the
947  * hive itself. It looks the same like other keys. Even the ID-number can
948  * be any value.
949  *
950  * The "hash"-value is a value representing the key's name. Windows will not
951  * search for the name, but for a matching hash-value. if it finds one, it
952  * will compare the actual string info, otherwise continue with the next key.
953  * To calculate the hash initialize a D-Word with 0 and add all ASCII-values 
954  * of the string which are smaller than 0x80 (128) to this D-Word.   
955  *
956  * If you want to modify key names, also modify the hash-values, since they
957  * cannot be found again (although they would be displayed in REGEDIT)
958  * End of list-pointers are filled with 0xFFFFFFFF
959  *
960  * Disk keys are layed out flat ... But, sometimes, nrLS and nrHS are both
961  * 0xFFFF, which means skipping over nextkeyoffset bytes (including this
962  * structure) and reading another RGDB_section.
963  *
964  * there is a one to one relationship between dke and dkh
965  */
966  /* key struct, once per key */
967 typedef struct
968 {
969         DWORD   x1;             /* Free entry indicator(?) */
970         DWORD   hash;           /* sum of bytes of keyname */
971         DWORD   x3;             /* Root key indicator? usually 0xFFFFFFFF */
972         DWORD   prevlvl;        /* offset of previous key */
973         DWORD   nextsub;        /* offset of child key */
974         DWORD   next;           /* offset of sibling key */
975         WORD    nrLS;           /* id inside the rgdb block */
976         WORD    nrMS;           /* number of the rgdb block */
977 } _w95dke;
978
979 /* SECTION 3: key information, values and data
980  *
981  * structure:
982  *  section:    [blocks]*               (repeat creg->rgdb_num times)
983  *  blocks:     [rgdb] [subblocks]*     (repeat till block size reached )
984  *  subblocks:  [dkh] [dkv]*            (repeat dkh->values times )
985  *
986  * An interesting relationship exists in RGDB_section. The value at offset
987  * 10 equals the value at offset 4 minus the value at offset 8. I have no
988  * idea at the moment what this means.  (Kevin Cozens)
989  */
990
991 /* block header, once per block */
992 #define W95_REG_RGDB_ID 0x42444752
993
994 typedef struct
995 {
996         DWORD   id;     /* 0x00 'rgdb' = W95_REG_RGDB_ID */
997         DWORD   size;   /* 0x04 */
998         DWORD   uk1;    /* 0x08 */
999         DWORD   uk2;    /* 0x0c */
1000         DWORD   uk3;    /* 0x10 */
1001         DWORD   uk4;    /* 0x14 */
1002         DWORD   uk5;    /* 0x18 */
1003         DWORD   uk6;    /* 0x1c */
1004         /* dkh */
1005 } _w95rgdb;
1006
1007 /* Disk Key Header structure (RGDB part), once per key */
1008 typedef struct 
1009 {
1010         DWORD   nextkeyoff;     /* 0x00 offset to next dkh*/
1011         WORD    nrLS;           /* 0x04 id inside the rgdb block */
1012         WORD    nrMS;           /* 0x06 number of the rgdb block */
1013         DWORD   bytesused;      /* 0x08 */
1014         WORD    keynamelen;     /* 0x0c len of name */
1015         WORD    values;         /* 0x0e number of values */
1016         DWORD   xx1;            /* 0x10 */
1017         char    name[1];        /* 0x14 */
1018         /* dkv */               /* 0x14 + keynamelen */
1019 } _w95dkh;
1020
1021 /* Disk Key Value structure, once per value */
1022 typedef struct
1023 {
1024         DWORD   type;           /* 0x00 */
1025         DWORD   x1;             /* 0x04 */
1026         WORD    valnamelen;     /* 0x08 length of name, 0 is default key */
1027         WORD    valdatalen;     /* 0x0A length of data */
1028         char    name[1];        /* 0x0c */
1029         /* raw data */          /* 0x0c + valnamelen */
1030 } _w95dkv;
1031
1032 /******************************************************************************
1033  * _w95_lookup_dkh [Internal]
1034  *
1035  * seeks the dkh belonging to a dke
1036  */
1037 static _w95dkh * _w95_lookup_dkh (_w95creg *creg, int nrLS, int nrMS)
1038 {
1039         _w95rgdb * rgdb;
1040         _w95dkh * dkh;
1041         int i;
1042         
1043         rgdb = (_w95rgdb*)((char*)creg+creg->rgdb_off);         /* get the beginning of the rgdb datastore */
1044         assert (creg->rgdb_num > nrMS);                         /* check: requested block < last_block) */
1045         
1046         /* find the right block */
1047         for(i=0; i<nrMS ;i++)
1048         {
1049           assert(rgdb->id == W95_REG_RGDB_ID);                          /* check the magic */
1050           rgdb = (_w95rgdb*) ((char*)rgdb+rgdb->size);          /* find next block */
1051         }
1052
1053         dkh = (_w95dkh*)(rgdb + 1);                             /* first sub block within the rgdb */
1054
1055         do
1056         {
1057           if(nrLS==dkh->nrLS ) return dkh;
1058           dkh = (_w95dkh*)((char*)dkh + dkh->nextkeyoff);       /* find next subblock */
1059         } while ((char *)dkh < ((char*)rgdb+rgdb->size));
1060
1061         return NULL;
1062 }       
1063  
1064 /******************************************************************************
1065  * _w95_parse_dkv [Internal]
1066  */
1067 static int _w95_parse_dkv (
1068         HKEY hkey,
1069         _w95dkh * dkh,
1070         int nrLS,
1071         int nrMS )
1072 {
1073         _w95dkv * dkv;
1074         int i;
1075         DWORD ret;
1076         char * name;
1077                         
1078         /* first value block */
1079         dkv = (_w95dkv*)((char*)dkh+dkh->keynamelen+0x14);
1080
1081         /* loop trought the values */
1082         for (i=0; i< dkh->values; i++)
1083         {
1084           name = _strdupnA(dkv->name, dkv->valnamelen+1);
1085           ret = RegSetValueExA(hkey, name, 0, dkv->type, &(dkv->name[dkv->valnamelen]),dkv->valdatalen); 
1086           if (ret) ERR("RegSetValueEx failed (0x%08lx)\n", ret);
1087           free (name);
1088
1089           /* next value */
1090           dkv = (_w95dkv*)((char*)dkv+dkv->valnamelen+dkv->valdatalen+0x0c);
1091         }
1092         return TRUE;
1093 }
1094
1095 /******************************************************************************
1096  * _w95_parse_dke [Internal]
1097  */
1098 static int _w95_parse_dke( 
1099         HKEY hkey,
1100         _w95creg * creg,
1101         _w95rgkn *rgkn,
1102         _w95dke * dke,
1103         int level )
1104 {
1105         _w95dkh * dkh;
1106         HKEY hsubkey = hkey;
1107         char * name;
1108         int ret = FALSE;
1109
1110         /* get start address of root key block */
1111         if (!dke) dke = (_w95dke*)((char*)rgkn + rgkn->root_off);
1112         
1113         /* create key */
1114         if (dke->nrLS != 0xffff && dke->nrMS!=0xffff)           /* eg. the root key has no name */
1115         {
1116           if (!(dkh = _w95_lookup_dkh(creg, dke->nrLS, dke->nrMS)))
1117           {
1118             fprintf(stderr, "dke pointing to missing dkh !\n");
1119             goto error;
1120           }
1121           /* subblock found */
1122           if ( level <= 0 )
1123           {
1124             name = _strdupnA( dkh->name, dkh->keynamelen+1);
1125             if (RegCreateKeyA(hkey, name, &hsubkey)) { free(name); goto error; }
1126             free(name);
1127           }
1128           _w95_parse_dkv(hsubkey, dkh, dke->nrLS, dke->nrMS);
1129         }
1130         else 
1131         {
1132           level++; /* we have to skip the root-block anyway */
1133         }
1134         
1135         /* walk sibling keys */
1136         if (dke->next != 0xffffffff )
1137         {
1138           _w95_parse_dke(hkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->next), level);
1139         }
1140
1141         /* next sub key */
1142         if (dke->nextsub != 0xffffffff) 
1143         {
1144           _w95_parse_dke(hsubkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub), level-1);
1145         }
1146   
1147         ret = TRUE;
1148         if (hsubkey != hkey) RegCloseKey(hsubkey);
1149 error:  return ret;
1150 }
1151 /* end windows 95 loader */
1152
1153 /******************************************************************************
1154  *      NativeRegLoadKey [Internal]
1155  *
1156  * Loads a native registry file (win95/nt)
1157  *      hkey    root key
1158  *      fn      filename
1159  *      level   number of levels to cut away (eg. ".Default" in user.dat)
1160  *
1161  * this function intentionally uses unix file functions to make it possible
1162  * to move it to a seperate registry helper programm
1163  */
1164 static int NativeRegLoadKey( HKEY hkey, char* fn, int level )
1165 {
1166         int fd = 0;
1167         struct stat st;
1168         DOS_FULL_NAME full_name;
1169         int ret = FALSE;
1170         void * base;
1171                         
1172         if (!DOSFS_GetFullName( fn, 0, &full_name )) return FALSE;
1173         
1174         /* map the registry into the memory */
1175         if ((fd = open(full_name.long_name, O_RDONLY | O_NONBLOCK)) == -1) return FALSE;
1176         if ((fstat(fd, &st) == -1)) goto error;
1177         if ((base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto error;
1178
1179         switch (*(LPDWORD)base)
1180         {
1181           /* windows 95 'creg' */
1182           case W95_REG_CREG_ID:
1183             {
1184               _w95creg * creg;
1185               _w95rgkn * rgkn;
1186               creg = base;
1187               TRACE_(reg)("Loading win95 registry '%s' '%s'\n",fn, full_name.long_name);
1188
1189               /* load the header (rgkn) */
1190               rgkn = (_w95rgkn*)(creg + 1);
1191               if (rgkn->id != W95_REG_RGKN_ID) 
1192               {
1193                 ERR("second IFF header not RGKN, but %lx\n", rgkn->id);
1194                 goto error1;
1195               }
1196
1197               ret = _w95_parse_dke(hkey, creg, rgkn, NULL, level);
1198             }
1199             break;
1200           /* nt 'regf'*/
1201           case NT_REG_HEADER_BLOCK_ID:
1202             {
1203               nt_regf * regf;
1204               nt_hbin * hbin;
1205               nt_hbin_sub * hbin_sub;
1206               nt_nk* nk;
1207
1208               TRACE_(reg)("Loading nt registry '%s' '%s'\n",fn, full_name.long_name);
1209
1210               /* start block */
1211               regf = base;
1212
1213               /* hbin block */
1214               hbin = base + 0x1000;
1215               if (hbin->id != NT_REG_POOL_BLOCK_ID)
1216               {
1217                 ERR_(reg)( "%s hbin block invalid\n", fn);
1218                 goto error1;
1219               }
1220
1221               /* hbin_sub block */
1222               hbin_sub = (nt_hbin_sub*)&(hbin->hbin_sub);
1223               if ((hbin_sub->data[0] != 'n') || (hbin_sub->data[1] != 'k'))
1224               {
1225                 ERR_(reg)( "%s hbin_sub block invalid\n", fn);
1226                 goto error1;
1227               }
1228
1229               /* nk block */
1230               nk = (nt_nk*)&(hbin_sub->data[0]);
1231               if (nk->Type != NT_REG_ROOT_KEY_BLOCK_TYPE)
1232               {
1233                 ERR_(reg)( "%s special nk block not found\n", fn);
1234                 goto error1;
1235               }
1236
1237               ret = _nt_parse_nk (hkey, base+0x1000, nk, level);
1238             }
1239             break;
1240           default:
1241             {
1242               ERR("unknown signature in registry file %s.\n",fn);
1243               goto error1;
1244             }
1245         }
1246
1247 error1: munmap(base, st.st_size);
1248 error:  close(fd);
1249         return ret;     
1250 }
1251
1252 /* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjøwall, tor@sn.no */
1253 /*
1254     reghack - windows 3.11 registry data format demo program.
1255
1256     The reg.dat file has 3 parts, a header, a table of 8-byte entries that is
1257     a combined hash table and tree description, and finally a text table.
1258
1259     The header is obvious from the struct header. The taboff1 and taboff2
1260     fields are always 0x20, and their usage is unknown.
1261
1262     The 8-byte entry table has various entry types.
1263
1264     tabent[0] is a root index. The second word has the index of the root of
1265             the directory.
1266     tabent[1..hashsize] is a hash table. The first word in the hash entry is
1267             the index of the key/value that has that hash. Data with the same
1268             hash value are on a circular list. The other three words in the
1269             hash entry are always zero.
1270     tabent[hashsize..tabcnt] is the tree structure. There are two kinds of
1271             entry: dirent and keyent/valent. They are identified by context.
1272     tabent[freeidx] is the first free entry. The first word in a free entry
1273             is the index of the next free entry. The last has 0 as a link.
1274             The other three words in the free list are probably irrelevant.
1275
1276     Entries in text table are preceeded by a word at offset-2. This word
1277     has the value (2*index)+1, where index is the referring keyent/valent
1278     entry in the table. I have no suggestion for the 2* and the +1.
1279     Following the word, there are N bytes of data, as per the keyent/valent
1280     entry length. The offset of the keyent/valent entry is from the start
1281     of the text table to the first data byte.
1282
1283     This information is not available from Microsoft. The data format is
1284     deduced from the reg.dat file by me. Mistakes may
1285     have been made. I claim no rights and give no guarantees for this program.
1286
1287     Tor Sjøwall, tor@sn.no
1288 */
1289
1290 /* reg.dat header format */
1291 struct _w31_header {
1292         char            cookie[8];      /* 'SHCC3.10' */
1293         unsigned long   taboff1;        /* offset of hash table (??) = 0x20 */
1294         unsigned long   taboff2;        /* offset of index table (??) = 0x20 */
1295         unsigned long   tabcnt;         /* number of entries in index table */
1296         unsigned long   textoff;        /* offset of text part */
1297         unsigned long   textsize;       /* byte size of text part */
1298         unsigned short  hashsize;       /* hash size */
1299         unsigned short  freeidx;        /* free index */
1300 };
1301
1302 /* generic format of table entries */
1303 struct _w31_tabent {
1304         unsigned short w0, w1, w2, w3;
1305 };
1306
1307 /* directory tabent: */
1308 struct _w31_dirent {
1309         unsigned short  sibling_idx;    /* table index of sibling dirent */
1310         unsigned short  child_idx;      /* table index of child dirent */
1311         unsigned short  key_idx;        /* table index of key keyent */
1312         unsigned short  value_idx;      /* table index of value valent */
1313 };
1314
1315 /* key tabent: */
1316 struct _w31_keyent {
1317         unsigned short  hash_idx;       /* hash chain index for string */
1318         unsigned short  refcnt;         /* reference count */
1319         unsigned short  length;         /* length of string */
1320         unsigned short  string_off;     /* offset of string in text table */
1321 };
1322
1323 /* value tabent: */
1324 struct _w31_valent {
1325         unsigned short  hash_idx;       /* hash chain index for string */
1326         unsigned short  refcnt;         /* reference count */
1327         unsigned short  length;         /* length of string */
1328         unsigned short  string_off;     /* offset of string in text table */
1329 };
1330
1331 /* recursive helper function to display a directory tree */
1332 void
1333 __w31_dumptree( unsigned short idx,
1334                 unsigned char *txt,
1335                 struct _w31_tabent *tab,
1336                 struct _w31_header *head,
1337                 HKEY hkey,
1338                 time_t          lastmodified,
1339                 int             level
1340 ) {
1341         struct _w31_dirent      *dir;
1342         struct _w31_keyent      *key;
1343         struct _w31_valent      *val;
1344         HKEY subkey = 0;
1345         static char             tail[400];
1346
1347         while (idx!=0) {
1348                 dir=(struct _w31_dirent*)&tab[idx];
1349
1350                 if (dir->key_idx) {
1351                         key = (struct _w31_keyent*)&tab[dir->key_idx];
1352
1353                         memcpy(tail,&txt[key->string_off],key->length);
1354                         tail[key->length]='\0';
1355                         /* all toplevel entries AND the entries in the 
1356                          * toplevel subdirectory belong to \SOFTWARE\Classes
1357                          */
1358                         if (!level && !lstrcmpA(tail,".classes")) {
1359                                 __w31_dumptree(dir->child_idx,txt,tab,head,hkey,lastmodified,level+1);
1360                                 idx=dir->sibling_idx;
1361                                 continue;
1362                         }
1363                         if (subkey) RegCloseKey( subkey );
1364                         if (RegCreateKeyA( hkey, tail, &subkey ) != ERROR_SUCCESS) subkey = 0;
1365                         /* only add if leaf node or valued node */
1366                         if (dir->value_idx!=0||dir->child_idx==0) {
1367                                 if (dir->value_idx) {
1368                                         val=(struct _w31_valent*)&tab[dir->value_idx];
1369                                         memcpy(tail,&txt[val->string_off],val->length);
1370                                         tail[val->length]='\0';
1371                                         RegSetValueA( subkey, NULL, REG_SZ, tail, 0 );
1372                                 }
1373                         }
1374                 } else {
1375                         TRACE("strange: no directory key name, idx=%04x\n", idx);
1376                 }
1377                 __w31_dumptree(dir->child_idx,txt,tab,head,subkey,lastmodified,level+1);
1378                 idx=dir->sibling_idx;
1379         }
1380         if (subkey) RegCloseKey( subkey );
1381 }
1382
1383
1384 /******************************************************************************
1385  * _w31_loadreg [Internal]
1386  */
1387 void _w31_loadreg(void) {
1388         HFILE                   hf;
1389         struct _w31_header      head;
1390         struct _w31_tabent      *tab;
1391         unsigned char           *txt;
1392         int                     len;
1393         OFSTRUCT                ofs;
1394         BY_HANDLE_FILE_INFORMATION hfinfo;
1395         time_t                  lastmodified;
1396
1397         TRACE("(void)\n");
1398
1399         hf = OpenFile("reg.dat",&ofs,OF_READ);
1400         if (hf==HFILE_ERROR)
1401                 return;
1402
1403         /* read & dump header */
1404         if (sizeof(head)!=_lread(hf,&head,sizeof(head))) {
1405                 ERR("reg.dat is too short.\n");
1406                 _lclose(hf);
1407                 return;
1408         }
1409         if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie))!=0) {
1410                 ERR("reg.dat has bad signature.\n");
1411                 _lclose(hf);
1412                 return;
1413         }
1414
1415         len = head.tabcnt * sizeof(struct _w31_tabent);
1416         /* read and dump index table */
1417         tab = xmalloc(len);
1418         if (len!=_lread(hf,tab,len)) {
1419                 ERR("couldn't read %d bytes.\n",len); 
1420                 free(tab);
1421                 _lclose(hf);
1422                 return;
1423         }
1424
1425         /* read text */
1426         txt = xmalloc(head.textsize);
1427         if (-1==_llseek(hf,head.textoff,SEEK_SET)) {
1428                 ERR("couldn't seek to textblock.\n"); 
1429                 free(tab);
1430                 free(txt);
1431                 _lclose(hf);
1432                 return;
1433         }
1434         if (head.textsize!=_lread(hf,txt,head.textsize)) {
1435                 ERR("textblock too short (%d instead of %ld).\n",len,head.textsize); 
1436                 free(tab);
1437                 free(txt);
1438                 _lclose(hf);
1439                 return;
1440         }
1441
1442         if (!GetFileInformationByHandle(hf,&hfinfo)) {
1443                 ERR("GetFileInformationByHandle failed?.\n"); 
1444                 free(tab);
1445                 free(txt);
1446                 _lclose(hf);
1447                 return;
1448         }
1449         lastmodified = DOSFS_FileTimeToUnixTime(&hfinfo.ftLastWriteTime,NULL);
1450         __w31_dumptree(tab[0].w1,txt,tab,&head,HKEY_CLASSES_ROOT,lastmodified,0);
1451         free(tab);
1452         free(txt);
1453         _lclose(hf);
1454         return;
1455 }
1456
1457 /**********************************************************************************
1458  * SetLoadLevel [Internal]
1459  *
1460  * set level to 0 for loading system files
1461  * set level to 1 for loading user files
1462  */
1463 static void SetLoadLevel(int level)
1464 {
1465         struct set_registry_levels_request *req = get_req_buffer();
1466
1467         req->current = level;
1468         req->saving  = 0;
1469         req->version = 1;
1470         server_call( REQ_SET_REGISTRY_LEVELS );
1471 }
1472
1473 /**********************************************************************************
1474  * SHELL_LoadRegistry [Internal]
1475  */
1476 void SHELL_LoadRegistry( void )
1477 {
1478   int   save_timeout;
1479   char  *fn, *home;
1480   HKEY  hkey;
1481
1482   TRACE("(void)\n");
1483
1484   REGISTRY_Init();
1485   SetLoadLevel(0);
1486
1487   if (PROFILE_GetWineIniBool ("registry", "LoadWin311RegistryFiles", 1)) 
1488   { 
1489       /* Load windows 3.1 entries */
1490       _w31_loadreg();
1491   }
1492   if (PROFILE_GetWineIniBool ("registry", "LoadWin95RegistryFiles", 1)) 
1493   { 
1494       /* Load windows 95 entries */
1495       NativeRegLoadKey(HKEY_LOCAL_MACHINE, "C:\\system.1st", 0);
1496       NativeRegLoadKey(HKEY_LOCAL_MACHINE, "system.dat", 0);
1497       NativeRegLoadKey(HKEY_CURRENT_USER, "user.dat", 1);
1498   }
1499   if (PROFILE_GetWineIniBool ("registry", "LoadWinNTRegistryFiles", 1)) 
1500   { 
1501       fn = xmalloc( MAX_PATHNAME_LEN ); 
1502       home = xmalloc ( MAX_PATHNAME_LEN );
1503       if ( PROFILE_GetWineIniString( "registry", "NTUser", "", home, MAX_PATHNAME_LEN - 1)) 
1504       {
1505          GetWindowsDirectoryA( fn, MAX_PATHNAME_LEN );
1506          strncat(fn, "\\Profiles\\", MAX_PATHNAME_LEN - strlen(fn) - 1);
1507          strncat(fn, home, MAX_PATHNAME_LEN - strlen(fn) - 1);
1508          strncat(fn, "\\ntuser.dat", MAX_PATHNAME_LEN - strlen(fn) - 1);
1509          NativeRegLoadKey( HKEY_CURRENT_USER, fn, 1 );
1510       }     
1511       /*
1512       * FIXME
1513       *  map HLM\System\ControlSet001 to HLM\System\CurrentControlSet
1514       */
1515       GetSystemDirectoryA( fn, MAX_PATHNAME_LEN );
1516
1517       strcpy(home, fn);
1518       strncat(home, "\\config\\system", MAX_PATHNAME_LEN - strlen(home) - 1);
1519       NativeRegLoadKey(HKEY_LOCAL_MACHINE, home, 0);
1520
1521       strcpy(home, fn);
1522       strncat(home, "\\config\\software", MAX_PATHNAME_LEN - strlen(home) - 1);
1523       NativeRegLoadKey(HKEY_LOCAL_MACHINE, home, 0);
1524
1525       strcpy(home, fn);
1526       strncat(home, "\\config\\sam", MAX_PATHNAME_LEN - strlen(home) - 1);
1527       NativeRegLoadKey(HKEY_LOCAL_MACHINE, home, 0);
1528
1529       strcpy(home, fn);
1530       strncat(home, "\\config\\security", MAX_PATHNAME_LEN - strlen(home) - 1);
1531       NativeRegLoadKey(HKEY_LOCAL_MACHINE, home, 0);
1532
1533       free (home);
1534       free (fn);
1535       /* this key is generated when the nt-core booted successfully */
1536       if (!RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\Clone",&hkey))
1537         RegCloseKey(hkey);
1538   }
1539
1540   if (PROFILE_GetWineIniBool ("registry","LoadGlobalRegistryFiles", 1))
1541   {
1542       /* 
1543        * Load the global HKU hive directly from sysconfdir
1544        */ 
1545       _wine_loadreg( HKEY_USERS, SAVE_USERS_DEFAULT );
1546
1547       /* 
1548        * Load the global machine defaults directly form sysconfdir
1549        */
1550       _wine_loadreg( HKEY_LOCAL_MACHINE, SAVE_LOCAL_MACHINE_DEFAULT );
1551   }
1552
1553   SetLoadLevel(1);
1554
1555   /*
1556    * Load the user saved registries 
1557    */
1558   if (!(home = getenv( "HOME" )))
1559       WARN("Failed to get homedirectory of UID %ld.\n",(long) getuid());
1560   else if (PROFILE_GetWineIniBool("registry", "LoadHomeRegistryFiles", 1))
1561   {
1562       /* 
1563        * Load user's personal versions of global HKU/.Default keys
1564        */
1565       fn=(char*)xmalloc( strlen(home)+ strlen(WINE_PREFIX) +
1566                          strlen(SAVE_LOCAL_USERS_DEFAULT)+2);
1567       strcpy(fn, home);
1568       strcat(fn, WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
1569       _wine_loadreg( HKEY_USERS, fn ); 
1570       free(fn);
1571
1572       fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) + strlen(SAVE_CURRENT_USER)+2);
1573       strcpy(fn, home);
1574       strcat(fn, WINE_PREFIX"/"SAVE_CURRENT_USER);
1575       _wine_loadreg( HKEY_CURRENT_USER, fn );
1576       free(fn);
1577
1578       /* 
1579        * Load HKLM, attempt to get the registry location from the config 
1580        * file first, if exist, load and keep going.
1581        */
1582       fn=(char*)xmalloc( strlen(home)+ strlen(WINE_PREFIX)+ strlen(SAVE_LOCAL_MACHINE)+2);
1583       strcpy(fn,home);
1584       strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE);
1585       _wine_loadreg( HKEY_LOCAL_MACHINE, fn );
1586       free(fn);
1587   }
1588   
1589   /* 
1590    * Load HKCU, get the registry location from the config 
1591    * file, if exist, load and keep going.
1592    */      
1593   if (PROFILE_GetWineIniBool ( "registry", "LoadAltRegistryFiles", 1))
1594   {
1595       fn = xmalloc( MAX_PATHNAME_LEN ); 
1596       if ( PROFILE_GetWineIniString( "registry", "AltCurrentUserFile", "", 
1597                                      fn, MAX_PATHNAME_LEN - 1)) 
1598        {
1599          _wine_loadreg( HKEY_CURRENT_USER, fn );
1600        }
1601       free (fn);
1602       /*
1603        * Load HKU, get the registry location from the config
1604        * file, if exist, load and keep going.
1605        */
1606       fn = xmalloc ( MAX_PATHNAME_LEN );
1607       if ( PROFILE_GetWineIniString ( "registry", "AltUserFile", "",
1608                                       fn, MAX_PATHNAME_LEN - 1))
1609        {
1610          _wine_loadreg( HKEY_USERS, fn );
1611        }
1612       free (fn);
1613       /*
1614        * Load HKLM, get the registry location from the config
1615        * file, if exist, load and keep going.
1616        */
1617       fn = xmalloc ( MAX_PATHNAME_LEN );
1618       if (PROFILE_GetWineIniString ( "registry", "AltLocalMachineFile", "",
1619                                      fn, MAX_PATHNAME_LEN - 1))
1620        {
1621          _wine_loadreg( HKEY_LOCAL_MACHINE, fn );
1622        }
1623       free (fn);
1624   }
1625   
1626   /* 
1627    * Make sure the update mode is there
1628    */
1629   if (ERROR_SUCCESS==RegCreateKey16(HKEY_CURRENT_USER,KEY_REGISTRY,&hkey)) 
1630   {
1631     DWORD       junk,type,len;
1632     char        data[5];
1633
1634     len=4;
1635     if ((       RegQueryValueExA(
1636             hkey,
1637             VAL_SAVEUPDATED,
1638             &junk,
1639             &type,
1640             data,
1641             &len) != ERROR_SUCCESS) || (type != REG_SZ))
1642     {
1643       RegSetValueExA(hkey,VAL_SAVEUPDATED,0,REG_SZ,"yes",4);
1644     }
1645
1646     RegCloseKey(hkey);
1647   }
1648
1649   if ((save_timeout = PROFILE_GetWineIniInt( "registry", "PeriodicSave", 0 )))
1650   {
1651       SERVICE_AddTimer( save_timeout * 1000000, periodic_save, 0 );
1652   }
1653 }
1654
1655
1656 /********************* API FUNCTIONS ***************************************/
1657
1658
1659
1660
1661 /******************************************************************************
1662  * RegFlushKey [KERNEL.227] [ADVAPI32.143]
1663  * Immediately writes key to registry.
1664  * Only returns after data has been written to disk.
1665  *
1666  * FIXME: does it really wait until data is written ?
1667  *
1668  * PARAMS
1669  *    hkey [I] Handle of key to write
1670  *
1671  * RETURNS
1672  *    Success: ERROR_SUCCESS
1673  *    Failure: Error code
1674  */
1675 DWORD WINAPI RegFlushKey( HKEY hkey )
1676 {
1677     FIXME( "(%x): stub\n", hkey );
1678     return ERROR_SUCCESS;
1679 }
1680
1681 /******************************************************************************
1682  * RegConnectRegistry32W [ADVAPI32.128]
1683  *
1684  * PARAMS
1685  *    lpMachineName [I] Address of name of remote computer
1686  *    hHey          [I] Predefined registry handle
1687  *    phkResult     [I] Address of buffer for remote registry handle
1688  */
1689 LONG WINAPI RegConnectRegistryW( LPCWSTR lpMachineName, HKEY hKey, 
1690                                    LPHKEY phkResult )
1691 {
1692     TRACE("(%s,%x,%p): stub\n",debugstr_w(lpMachineName),hKey,phkResult);
1693
1694     if (!lpMachineName || !*lpMachineName) {
1695         /* Use the local machine name */
1696         return RegOpenKey16( hKey, "", phkResult );
1697     }
1698
1699     FIXME("Cannot connect to %s\n",debugstr_w(lpMachineName));
1700     return ERROR_BAD_NETPATH;
1701 }
1702
1703
1704 /******************************************************************************
1705  * RegConnectRegistry32A [ADVAPI32.127]
1706  */
1707 LONG WINAPI RegConnectRegistryA( LPCSTR machine, HKEY hkey, LPHKEY reskey )
1708 {
1709     DWORD ret;
1710     LPWSTR machineW = strdupA2W(machine);
1711     ret = RegConnectRegistryW( machineW, hkey, reskey );
1712     free(machineW);
1713     return ret;
1714 }
1715
1716
1717 /******************************************************************************
1718  * RegGetKeySecurity [ADVAPI32.144]
1719  * Retrieves a copy of security descriptor protecting the registry key
1720  *
1721  * PARAMS
1722  *    hkey                   [I]   Open handle of key to set
1723  *    SecurityInformation    [I]   Descriptor contents
1724  *    pSecurityDescriptor    [O]   Address of descriptor for key
1725  *    lpcbSecurityDescriptor [I/O] Address of size of buffer and description
1726  *
1727  * RETURNS
1728  *    Success: ERROR_SUCCESS
1729  *    Failure: Error code
1730  */
1731 LONG WINAPI RegGetKeySecurity( HKEY hkey, 
1732                                SECURITY_INFORMATION SecurityInformation,
1733                                PSECURITY_DESCRIPTOR pSecurityDescriptor,
1734                                LPDWORD lpcbSecurityDescriptor )
1735 {
1736     TRACE("(%x,%ld,%p,%ld)\n",hkey,SecurityInformation,pSecurityDescriptor,
1737           lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
1738
1739     /* FIXME: Check for valid SecurityInformation values */
1740
1741     if (*lpcbSecurityDescriptor < sizeof(SECURITY_DESCRIPTOR))
1742         return ERROR_INSUFFICIENT_BUFFER;
1743
1744     FIXME("(%x,%ld,%p,%ld): stub\n",hkey,SecurityInformation,
1745           pSecurityDescriptor,lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
1746
1747     return ERROR_SUCCESS;
1748 }
1749
1750
1751 /******************************************************************************
1752  * RegNotifyChangeKeyValue [ADVAPI32.???]
1753  *
1754  * PARAMS
1755  *    hkey            [I] Handle of key to watch
1756  *    fWatchSubTree   [I] Flag for subkey notification
1757  *    fdwNotifyFilter [I] Changes to be reported
1758  *    hEvent          [I] Handle of signaled event
1759  *    fAsync          [I] Flag for asynchronous reporting
1760  */
1761 LONG WINAPI RegNotifyChangeKeyValue( HKEY hkey, BOOL fWatchSubTree, 
1762                                      DWORD fdwNotifyFilter, HANDLE hEvent,
1763                                      BOOL fAsync )
1764 {
1765     FIXME("(%x,%i,%ld,%x,%i): stub\n",hkey,fWatchSubTree,fdwNotifyFilter,
1766           hEvent,fAsync);
1767     return ERROR_SUCCESS;
1768 }
1769
1770
1771 /******************************************************************************
1772  * RegUnLoadKey32W [ADVAPI32.173]
1773  *
1774  * PARAMS
1775  *    hkey     [I] Handle of open key
1776  *    lpSubKey [I] Address of name of subkey to unload
1777  */
1778 LONG WINAPI RegUnLoadKeyW( HKEY hkey, LPCWSTR lpSubKey )
1779 {
1780     FIXME("(%x,%s): stub\n",hkey, debugstr_w(lpSubKey));
1781     return ERROR_SUCCESS;
1782 }
1783
1784
1785 /******************************************************************************
1786  * RegUnLoadKey32A [ADVAPI32.172]
1787  */
1788 LONG WINAPI RegUnLoadKeyA( HKEY hkey, LPCSTR lpSubKey )
1789 {
1790     LONG ret;
1791     LPWSTR lpSubKeyW = strdupA2W(lpSubKey);
1792     ret = RegUnLoadKeyW( hkey, lpSubKeyW );
1793     if(lpSubKeyW) free(lpSubKeyW);
1794     return ret;
1795 }
1796
1797
1798 /******************************************************************************
1799  * RegSetKeySecurity [ADVAPI32.167]
1800  *
1801  * PARAMS
1802  *    hkey          [I] Open handle of key to set
1803  *    SecurityInfo  [I] Descriptor contents
1804  *    pSecurityDesc [I] Address of descriptor for key
1805  */
1806 LONG WINAPI RegSetKeySecurity( HKEY hkey, SECURITY_INFORMATION SecurityInfo,
1807                                PSECURITY_DESCRIPTOR pSecurityDesc )
1808 {
1809     TRACE("(%x,%ld,%p)\n",hkey,SecurityInfo,pSecurityDesc);
1810
1811     /* It seems to perform this check before the hkey check */
1812     if ((SecurityInfo & OWNER_SECURITY_INFORMATION) ||
1813         (SecurityInfo & GROUP_SECURITY_INFORMATION) ||
1814         (SecurityInfo & DACL_SECURITY_INFORMATION) ||
1815         (SecurityInfo & SACL_SECURITY_INFORMATION)) {
1816         /* Param OK */
1817     } else
1818         return ERROR_INVALID_PARAMETER;
1819
1820     if (!pSecurityDesc)
1821         return ERROR_INVALID_PARAMETER;
1822
1823     FIXME(":(%x,%ld,%p): stub\n",hkey,SecurityInfo,pSecurityDesc);
1824
1825     return ERROR_SUCCESS;
1826 }
1827
1828
1829 /******************************************************************************
1830  * RegRestoreKey32W [ADVAPI32.164]
1831  *
1832  * PARAMS
1833  *    hkey    [I] Handle of key where restore begins
1834  *    lpFile  [I] Address of filename containing saved tree
1835  *    dwFlags [I] Optional flags
1836  */
1837 LONG WINAPI RegRestoreKeyW( HKEY hkey, LPCWSTR lpFile, DWORD dwFlags )
1838 {
1839     TRACE("(%x,%s,%ld)\n",hkey,debugstr_w(lpFile),dwFlags);
1840
1841     /* It seems to do this check before the hkey check */
1842     if (!lpFile || !*lpFile)
1843         return ERROR_INVALID_PARAMETER;
1844
1845     FIXME("(%x,%s,%ld): stub\n",hkey,debugstr_w(lpFile),dwFlags);
1846
1847     /* Check for file existence */
1848
1849     return ERROR_SUCCESS;
1850 }
1851
1852
1853 /******************************************************************************
1854  * RegRestoreKey32A [ADVAPI32.163]
1855  */
1856 LONG WINAPI RegRestoreKeyA( HKEY hkey, LPCSTR lpFile, DWORD dwFlags )
1857 {
1858     LONG ret;
1859     LPWSTR lpFileW = strdupA2W(lpFile);
1860     ret = RegRestoreKeyW( hkey, lpFileW, dwFlags );
1861     if(lpFileW) free(lpFileW);
1862     return ret;
1863 }
1864
1865
1866 /******************************************************************************
1867  * RegReplaceKey32W [ADVAPI32.162]
1868  *
1869  * PARAMS
1870  *    hkey      [I] Handle of open key
1871  *    lpSubKey  [I] Address of name of subkey
1872  *    lpNewFile [I] Address of filename for file with new data
1873  *    lpOldFile [I] Address of filename for backup file
1874  */
1875 LONG WINAPI RegReplaceKeyW( HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpNewFile,
1876                               LPCWSTR lpOldFile )
1877 {
1878     FIXME("(%x,%s,%s,%s): stub\n", hkey, debugstr_w(lpSubKey), 
1879           debugstr_w(lpNewFile),debugstr_w(lpOldFile));
1880     return ERROR_SUCCESS;
1881 }
1882
1883
1884 /******************************************************************************
1885  * RegReplaceKey32A [ADVAPI32.161]
1886  */
1887 LONG WINAPI RegReplaceKeyA( HKEY hkey, LPCSTR lpSubKey, LPCSTR lpNewFile,
1888                               LPCSTR lpOldFile )
1889 {
1890     LONG ret;
1891     LPWSTR lpSubKeyW = strdupA2W(lpSubKey);
1892     LPWSTR lpNewFileW = strdupA2W(lpNewFile);
1893     LPWSTR lpOldFileW = strdupA2W(lpOldFile);
1894     ret = RegReplaceKeyW( hkey, lpSubKeyW, lpNewFileW, lpOldFileW );
1895     free(lpOldFileW);
1896     free(lpNewFileW);
1897     free(lpSubKeyW);
1898     return ret;
1899 }
1900
1901
1902
1903
1904
1905
1906 /* 16-bit functions */
1907
1908 /* 0 and 1 are valid rootkeys in win16 shell.dll and are used by
1909  * some programs. Do not remove those cases. -MM
1910  */
1911 static inline void fix_win16_hkey( HKEY *hkey )
1912 {
1913     if (*hkey == 0 || *hkey == 1) *hkey = HKEY_CLASSES_ROOT;
1914 }
1915
1916 /******************************************************************************
1917  *           RegEnumKey16   [KERNEL.216] [SHELL.7]
1918  */
1919 DWORD WINAPI RegEnumKey16( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
1920 {
1921     fix_win16_hkey( &hkey );
1922     return RegEnumKeyA( hkey, index, name, name_len );
1923 }
1924
1925 /******************************************************************************
1926  *           RegOpenKey16   [KERNEL.217] [SHELL.1]
1927  */
1928 DWORD WINAPI RegOpenKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
1929 {
1930     fix_win16_hkey( &hkey );
1931     return RegOpenKeyA( hkey, name, retkey );
1932 }
1933
1934 /******************************************************************************
1935  *           RegCreateKey16   [KERNEL.218] [SHELL.2]
1936  */
1937 DWORD WINAPI RegCreateKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
1938 {
1939     fix_win16_hkey( &hkey );
1940     return RegCreateKeyA( hkey, name, retkey );
1941 }
1942
1943 /******************************************************************************
1944  *           RegDeleteKey16   [KERNEL.219] [SHELL.4]
1945  */
1946 DWORD WINAPI RegDeleteKey16( HKEY hkey, LPCSTR name )
1947 {
1948     fix_win16_hkey( &hkey );
1949     return RegDeleteKeyA( hkey, name );
1950 }
1951
1952 /******************************************************************************
1953  *           RegCloseKey16   [KERNEL.220] [SHELL.3]
1954  */
1955 DWORD WINAPI RegCloseKey16( HKEY hkey )
1956 {
1957     fix_win16_hkey( &hkey );
1958     return RegCloseKey( hkey );
1959 }
1960
1961 /******************************************************************************
1962  *           RegSetValue16   [KERNEL.221] [SHELL.5]
1963  */
1964 DWORD WINAPI RegSetValue16( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
1965 {
1966     fix_win16_hkey( &hkey );
1967     return RegSetValueA( hkey, name, type, data, count );
1968 }
1969
1970 /******************************************************************************
1971  *           RegDeleteValue16  [KERNEL.222]
1972  */
1973 DWORD WINAPI RegDeleteValue16( HKEY hkey, LPSTR name )
1974 {
1975     fix_win16_hkey( &hkey );
1976     return RegDeleteValueA( hkey, name );
1977 }
1978
1979 /******************************************************************************
1980  *           RegEnumValue16   [KERNEL.223]
1981  */
1982 DWORD WINAPI RegEnumValue16( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
1983                              LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
1984 {
1985     fix_win16_hkey( &hkey );
1986     return RegEnumValueA( hkey, index, value, val_count, reserved, type, data, count );
1987 }
1988
1989 /******************************************************************************
1990  *           RegQueryValue16   [KERNEL.224] [SHELL.6]
1991  *
1992  * NOTES
1993  *    Is this HACK still applicable?
1994  *
1995  * HACK
1996  *    The 16bit RegQueryValue doesn't handle selectorblocks anyway, so we just
1997  *    mask out the high 16 bit.  This (not so much incidently) hopefully fixes
1998  *    Aldus FH4)
1999  */
2000 DWORD WINAPI RegQueryValue16( HKEY hkey, LPCSTR name, LPSTR data, LPDWORD count )
2001 {
2002     fix_win16_hkey( &hkey );
2003     if (count) *count &= 0xffff;
2004     return RegQueryValueA( hkey, name, data, count );
2005 }
2006
2007 /******************************************************************************
2008  *           RegQueryValueEx16   [KERNEL.225]
2009  */
2010 DWORD WINAPI RegQueryValueEx16( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
2011                                 LPBYTE data, LPDWORD count )
2012 {
2013     fix_win16_hkey( &hkey );
2014     return RegQueryValueExA( hkey, name, reserved, type, data, count );
2015 }
2016
2017 /******************************************************************************
2018  *           RegSetValueEx16   [KERNEL.226]
2019  */
2020 DWORD WINAPI RegSetValueEx16( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
2021                               CONST BYTE *data, DWORD count )
2022 {
2023     fix_win16_hkey( &hkey );
2024     return RegSetValueExA( hkey, name, reserved, type, data, count );
2025 }