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