Added loader for NT registry files.

diff --git a/misc/registry.c b/misc/registry.c
index a29bcf1..948f464 100644
--- a/misc/registry.c
+++ b/misc/registry.c
@@ -732,7 +732,322 @@
         }
     }
 }
+/* NT REGISTRY LOADER */
+#include <sys/mman.h>
 
+#ifndef MAP_FAILED
+#define MAP_FAILED ((LPVOID)-1)
+#endif
+
+#define LONG_DUMP 1
+
+#define  REG_BLOCK_SIZE		0x1000
+
+#define  REG_HEADER_BLOCK_ID	0x66676572	/* regf */
+#define  REG_POOL_BLOCK_ID	0x6E696268	/* hbin */
+#define  REG_KEY_BLOCK_ID	0x6b6e
+#define	 REG_VALUE_BLOCK_ID	0x6b76
+#define	 REG_HASH_BLOCK_ID	0x666c
+#define  REG_NOHASH_BLOCK_ID	0x696c
+#define  REG_KEY_BLOCK_TYPE	0x20
+#define  REG_ROOT_KEY_BLOCK_TYPE	0x2c
+
+typedef struct 
+{
+	DWORD	id;		/* 0x66676572 'regf'*/
+	DWORD	uk1;		/* 0x04 */
+	DWORD	uk2;		/* 0x08 */
+	FILETIME	DateModified;	/* 0x0c */
+	DWORD	uk3;		/* 0x14 */
+	DWORD	uk4;		/* 0x18 */
+	DWORD	uk5;		/* 0x1c */
+	DWORD	uk6;		/* 0x20 */
+	DWORD	RootKeyBlock;	/* 0x24 */
+	DWORD	BlockSize;	/* 0x28 */
+	DWORD   uk7[116];	
+	DWORD	Checksum; /* at offset 0x1FC */
+} nt_regf;
+
+typedef struct
+{
+	DWORD	blocksize;
+	BYTE	data[1];
+} nt_hbin_sub;
+
+typedef struct
+{
+	DWORD	id;		/* 0x6E696268 'hbin' */
+	DWORD	off_prev;
+	DWORD	off_next;
+	DWORD	uk1;
+	DWORD	uk2;		/* 0x10 */
+	DWORD	uk3;		/* 0x14 */
+	DWORD	uk4;		/* 0x18 */
+	DWORD	size;		/* 0x1C */
+	nt_hbin_sub	hbin_sub;	/* 0x20 */
+} nt_hbin;
+
+/*
+ * the value_list consists of offsets to the values (vk)
+ */
+typedef struct
+{
+	WORD	SubBlockId;		/* 0x00 0x6B6E */
+	WORD	Type;			/* 0x02 for the root-key: 0x2C, otherwise 0x20*/
+	FILETIME	writetime;	/* 0x04 */
+	DWORD	uk1;			/* 0x0C */
+	DWORD	parent_off;		/* 0x10 Offset of Owner/Parent key */
+	DWORD	nr_subkeys;		/* 0x14 number of sub-Keys */
+	DWORD	uk8;			/* 0x18 */
+	DWORD	lf_off;			/* 0x1C Offset of the sub-key lf-Records */
+	DWORD	uk2;			/* 0x20 */
+	DWORD	nr_values;		/* 0x24 number of values */
+	DWORD	valuelist_off;		/* 0x28 Offset of the Value-List */
+	DWORD	off_sk;			/* 0x2c Offset of the sk-Record */
+	DWORD	off_class;		/* 0x30 Offset of the Class-Name */
+	DWORD	uk3;			/* 0x34 */
+	DWORD	uk4;			/* 0x38 */
+	DWORD	uk5;			/* 0x3c */
+	DWORD	uk6;			/* 0x40 */
+	DWORD	uk7;			/* 0x44 */
+	WORD	name_len;		/* 0x48 name-length */
+	WORD	class_len;		/* 0x4a class-name length */
+	char	name[1];		/* 0x4c key-name */
+} nt_nk;
+
+typedef struct
+{
+	DWORD	off_nk;	/* 0x00 */
+	DWORD	name;	/* 0x04 */
+} hash_rec;
+
+typedef struct
+{
+	WORD	id;		/* 0x00 0x666c */
+	WORD	nr_keys;	/* 0x06 */
+	hash_rec	hash_rec[1];
+} nt_lf;
+
+typedef struct
+{
+	WORD	id;		/* 0x00 0x696c */
+	WORD	nr_keys;
+	DWORD	off_nk[1];
+} nt_il;
+
+typedef struct
+{
+	WORD	id;		/* 0x00 'vk' */
+	WORD	nam_len;
+	DWORD	data_len;
+	DWORD	data_off;
+	DWORD	type;
+	WORD	flag;
+	WORD	uk1;
+	char	name[1];
+} nt_vk;
+
+#define vk_sz		0x0001
+#define	vk_expsz	0x0002
+#define	vk_bin		0x0003
+#define vk_dword	0x0004
+#define vk_multisz	0x0007
+#define vk_u2		0x0008
+#define vk_u1		0x000a
+
+LPSTR _strdupnA( LPCSTR str, int len )
+{
+    LPSTR ret;
+
+    if (!str) return NULL;
+    ret = malloc( len + 1 );
+    lstrcpynA( ret, str, len );
+    ret[len] = 0x00;
+    return ret;
+}
+
+int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level);
+int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk, int level);
+int _nt_parse_lf(HKEY hkey, char * base, nt_lf * lf, int level);
+
+
+/*
+ * gets a value
+ *
+ * vk->flag:
+ *  0 value is a default value
+ *  1 the value has a name
+ *
+ * vk->data_len
+ *  len of the whole data block
+ *  - reg_sz (unicode)
+ *    bytes including the terminating \0 = 2*(number_of_chars+1)
+ *  - reg_dword, reg_binary:
+ *    if highest bit of data_len is set data_off contains the value
+ */
+int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk, int level)
+{
+	WCHAR name [256];
+	BYTE * pdata = (BYTE *)(base+vk->data_off+4); /* start of data */
+
+	if(vk->id != REG_VALUE_BLOCK_ID) goto error;
+
+	lstrcpynAtoW(name, vk->name, vk->nam_len+1);
+
+	if (RegSetValueExW( hkey, (vk->flag & 0x00000001) ? name : NULL, 0, vk->type,
+			(vk->data_len & 0x80000000) ? (LPBYTE)&(vk->data_off): pdata,
+			(vk->data_len & 0x7fffffff) )) goto error;
+	return TRUE;
+error:
+	ERR_(reg)("vk block invalid\n");
+	return FALSE;
+}
+
+/*
+ * get the subkeys
+ *
+ * this structure contains the hash of a keyname and points to all
+ * subkeys
+ *
+ * exception: if the id is 'il' there are no hash values and every 
+ * dword is a offset
+ */
+int _nt_parse_lf(HKEY hkey, char * base, nt_lf * lf, int level)
+{
+	int i;
+
+	if (lf->id == REG_HASH_BLOCK_ID)
+	{
+	  for (i=0; i<lf->nr_keys; i++)
+	  {
+	    if (!_nt_parse_nk(hkey, base, (nt_nk*)(base+lf->hash_rec[i].off_nk+4), level)) goto error;
+	  }
+	  
+	}
+	else if (lf->id == REG_NOHASH_BLOCK_ID)
+	{
+	  for (i=0; i<lf->nr_keys; i++)
+	  {
+	    if (!_nt_parse_nk(hkey, base, (nt_nk*)(base+((nt_il*)lf)->off_nk[i]+4), level)) goto error;
+	  }
+	}
+	return TRUE;
+	
+error:	ERR_(reg)("error reading lf block\n");
+	return FALSE;
+}
+
+int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level)
+{
+	char * name;
+	int i;
+	DWORD * vl;
+	HKEY subkey;
+
+	if(nk->SubBlockId != REG_KEY_BLOCK_ID) goto error;
+	if((nk->Type!=REG_ROOT_KEY_BLOCK_TYPE) &&
+	   (((nt_nk*)(base+nk->parent_off+4))->SubBlockId != REG_KEY_BLOCK_ID)) goto error;
+
+	/* create the new key */
+	name = _strdupnA( nk->name, nk->name_len+1);
+	if(RegCreateKeyA( hkey, name, &subkey )) { free(name); goto error; }
+	free(name);
+
+	/* loop through the subkeys */
+	if (nk->nr_subkeys)
+	{
+	  nt_lf * lf = (nt_lf*)(base+nk->lf_off+4);
+	  if (nk->nr_subkeys != lf->nr_keys) goto error1;
+	  if (!_nt_parse_lf(subkey, base, lf, level+1)) goto error1;
+	}
+
+	/* loop trough the value list */
+	vl = (DWORD *)(base+nk->valuelist_off+4);
+	for (i=0; i<nk->nr_values; i++)
+	{
+	  nt_vk * vk = (nt_vk*)(base+vl[i]+4);
+	  if (!_nt_parse_vk(subkey, base, vk, level+1 )) goto error1;
+	}
+
+	RegCloseKey(subkey);
+	return TRUE;
+	
+error1:	RegCloseKey(subkey);
+error:	ERR_(reg)("error reading nk block\n");
+	return FALSE;
+}
+
+/*
+ * this function intentionally uses unix file functions to make it possible
+ * to move it to a seperate registry helper programm
+ */
+static int _nt_loadreg( HKEY hkey, char* fn )
+{
+	void * base;
+	int len, fd;
+	struct stat st;
+	nt_regf * regf;
+	nt_hbin * hbin;
+	nt_hbin_sub * hbin_sub;
+	nt_nk* nk;
+	DOS_FULL_NAME full_name;
+	
+	if (!DOSFS_GetFullName( fn, 0, &full_name ));
+
+	TRACE_(reg)("Loading NT registry database '%s' '%s'\n",fn, full_name.long_name);
+
+	if ((fd = open(full_name.long_name, O_RDONLY | O_NONBLOCK)) == -1) return FALSE;
+	if (fstat(fd, &st ) == -1) goto error1;
+	len = st.st_size;
+	if ((base=mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto error1;
+
+	/* start block */
+	regf = base;
+	if(regf->id != REG_HEADER_BLOCK_ID)	/* 'regf' */
+	{
+	  ERR( "%s is not a nt-registry\n", fn);
+	  goto error;
+	}
+	TRACE_(reg)( "%p [regf] offset=%lx size=%lx\n", regf, regf->RootKeyBlock, regf->BlockSize);
+	
+	/* hbin block */
+	hbin = base + 0x1000;
+	if (hbin->id != REG_POOL_BLOCK_ID)
+	{
+	  ERR_(reg)( "%s hbin block invalid\n", fn);
+	  goto error;
+	}
+	TRACE_(reg)( "%p [hbin]  prev=%lx next=%lx size=%lx\n", hbin, hbin->off_prev, hbin->off_next, hbin->size);
+
+	/* hbin_sub block */
+	hbin_sub = (nt_hbin_sub*)&(hbin->hbin_sub);
+	if ((hbin_sub->data[0] != 'n') || (hbin_sub->data[1] != 'k'))
+	{
+	  ERR_(reg)( "%s hbin_sub block invalid\n", fn);
+	  goto error;
+	}
+ 	TRACE_(reg)( "%p [hbin sub] size=%lx\n", hbin_sub, hbin_sub->blocksize);
+            	
+	/* nk block */
+	nk = (nt_nk*)&(hbin_sub->data[0]);
+	if (nk->Type != REG_ROOT_KEY_BLOCK_TYPE)
+	{
+	  ERR_(reg)( "%s special nk block not found\n", fn);
+	  goto error;
+	}
+
+	_nt_parse_nk (hkey, base+0x1000, nk, 0);
+
+	munmap(base, len);
+	close(fd);
+	return 1;
+
+error:	munmap(base, len);
+error1:	close(fd);
+	ERR_(reg)("error reading registry file\n");
+	return 0;
+}
+/* end nt loader */
 
 /* WINDOWS 95 REGISTRY LOADER */
 /* 
@@ -1302,15 +1617,55 @@
   req->version = 1;
   server_call( REQ_SET_REGISTRY_LEVELS );
 
-  if (PROFILE_GetWineIniBool ("registry", "LoadWindowsRegistryFiles", 1)) 
+  if (PROFILE_GetWineIniBool ("registry", "LoadWin311RegistryFiles", 1)) 
   { 
       /* Load windows 3.1 entries */
       _w31_loadreg();
+  }
+  if (PROFILE_GetWineIniBool ("registry", "LoadWin95RegistryFiles", 1)) 
+  { 
       /* Load windows 95 entries */
       _w95_loadreg("C:\\system.1st", HKEY_LOCAL_MACHINE);
       _w95_loadreg("system.dat", HKEY_LOCAL_MACHINE);
       _w95_loadreg("user.dat", HKEY_USERS);
   }
+  if (PROFILE_GetWineIniBool ("registry", "LoadWinNTRegistryFiles", 1)) 
+  { 
+      fn = xmalloc( MAX_PATHNAME_LEN ); 
+      home = xmalloc ( MAX_PATHNAME_LEN );
+      if ( PROFILE_GetWineIniString( "registry", "NTUser", "", home, MAX_PATHNAME_LEN - 1)) 
+      {
+         GetWindowsDirectoryA( fn, MAX_PATHNAME_LEN );
+	 strncat(fn, "\\Profiles\\", MAX_PATHNAME_LEN - strlen(fn) - 1);
+	 strncat(fn, home, MAX_PATHNAME_LEN - strlen(fn) - 1);
+	 strncat(fn, "\\ntuser.dat", MAX_PATHNAME_LEN - strlen(fn) - 1);
+         _nt_loadreg( HKEY_USERS, fn );
+      }     
+      /*
+      * FIXME
+      *  map HLM\System\ControlSet001 to HLM\System\CurrentControlSet
+      */
+      GetSystemDirectoryA( fn, MAX_PATHNAME_LEN );
+
+      strcpy(home, fn);
+      strncat(home, "\\config\\system", MAX_PATHNAME_LEN - strlen(home) - 1);
+      _nt_loadreg(HKEY_LOCAL_MACHINE, home);
+
+      strcpy(home, fn);
+      strncat(home, "\\config\\software", MAX_PATHNAME_LEN - strlen(home) - 1);
+      _nt_loadreg(HKEY_LOCAL_MACHINE, home);
+
+      strcpy(home, fn);
+      strncat(home, "\\config\\sam", MAX_PATHNAME_LEN - strlen(home) - 1);
+      _nt_loadreg(HKEY_LOCAL_MACHINE, home);
+
+      strcpy(home, fn);
+      strncat(home, "\\config\\security", MAX_PATHNAME_LEN - strlen(home) - 1);
+      _nt_loadreg(HKEY_LOCAL_MACHINE, home);
+
+      free (home);
+      free (fn);
+  }
 
   if (PROFILE_GetWineIniBool ("registry","LoadGlobalRegistryFiles", 1))
   {