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