New implementation of the win95 registry loader.

diff --git a/misc/registry.c b/misc/registry.c
index cc929b0..cc21f39 100644
--- a/misc/registry.c
+++ b/misc/registry.c
@@ -670,18 +670,16 @@
 #define MAP_FAILED ((LPVOID)-1)
 #endif
 
-#define LONG_DUMP 1
+#define  NT_REG_BLOCK_SIZE		0x1000
 
-#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
+#define NT_REG_HEADER_BLOCK_ID       0x66676572	/* regf */
+#define NT_REG_POOL_BLOCK_ID         0x6E696268	/* hbin */
+#define NT_REG_KEY_BLOCK_ID          0x6b6e
+#define NT_REG_VALUE_BLOCK_ID        0x6b76
+#define NT_REG_HASH_BLOCK_ID         0x666c
+#define NT_REG_NOHASH_BLOCK_ID       0x696c
+#define NT_REG_KEY_BLOCK_TYPE        0x20
+#define NT_REG_ROOT_KEY_BLOCK_TYPE   0x2c
 
 typedef struct 
 {
@@ -778,14 +776,6 @@
 	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;
@@ -797,9 +787,9 @@
     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);
+static int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level);
+static int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk);
+static int _nt_parse_lf(HKEY hkey, char * base, nt_lf * lf, int level);
 
 
 /*
@@ -816,13 +806,13 @@
  *  - 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)
+static int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk)
 {
 	WCHAR name [256];
 	DWORD ret;
 	BYTE * pdata = (BYTE *)(base+vk->data_off+4); /* start of data */
 
-	if(vk->id != REG_VALUE_BLOCK_ID) goto error;
+	if(vk->id != NT_REG_VALUE_BLOCK_ID) goto error;
 
 	lstrcpynAtoW(name, vk->name, vk->nam_len+1);
 
@@ -845,11 +835,11 @@
  * 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)
+static int _nt_parse_lf(HKEY hkey, char * base, nt_lf * lf, int level)
 {
 	int i;
 
-	if (lf->id == REG_HASH_BLOCK_ID)
+	if (lf->id == NT_REG_HASH_BLOCK_ID)
 	{
 	  for (i=0; i<lf->nr_keys; i++)
 	  {
@@ -857,7 +847,7 @@
 	  }
 	  
 	}
-	else if (lf->id == REG_NOHASH_BLOCK_ID)
+	else if (lf->id == NT_REG_NOHASH_BLOCK_ID)
 	{
 	  for (i=0; i<lf->nr_keys; i++)
 	  {
@@ -870,28 +860,31 @@
 	return FALSE;
 }
 
-int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level)
+static int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level)
 {
 	char * name;
 	int i;
 	DWORD * vl;
-	HKEY subkey;
+	HKEY subkey = hkey;
 
-	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;
+	if(nk->SubBlockId != NT_REG_KEY_BLOCK_ID) goto error;
+	if((nk->Type!=NT_REG_ROOT_KEY_BLOCK_TYPE) &&
+	   (((nt_nk*)(base+nk->parent_off+4))->SubBlockId != NT_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);
+	if(level <= 0)
+	{
+	  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;
+	  if (!_nt_parse_lf(subkey, base, lf, level-1)) goto error1;
 	}
 
 	/* loop trough the value list */
@@ -899,7 +892,7 @@
 	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;
+	  if (!_nt_parse_vk(subkey, base, vk)) goto error1;
 	}
 
 	RegCloseKey(subkey);
@@ -910,421 +903,353 @@
 	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 */
-/* 
- * Structure of a win95 registry database.
- * main header:
- * 0 :	"CREG"	- magic
- * 4 :	DWORD version
- * 8 :	DWORD offset_of_RGDB_part
- * 0C..0F:	? (someone fill in please)
- * 10:  WORD	number of RGDB blocks
- * 12:  WORD	?
- * 14:  WORD	always 0000?
- * 16:  WORD	always 0001?
- * 18..1F:	? (someone fill in please)
+/* windows 95 registry loader */
+
+/* SECTION 1: main header
  *
- * 20: RGKN_section:
- *   header:
- * 	0 :		"RGKN"	- magic
- *      4 : DWORD	offset to first RGDB section
- *      8 : DWORD	offset to the root record
- * 	C..0x1B: 	? (fill in)
- *      0x20 ... offset_of_RGDB_part: Disk Key Entry structures
+ * once at offset 0
+ */
+#define	W95_REG_CREG_ID	0x47455243
+
+typedef struct 
+{
+	DWORD	id;		/* "CREG" = W95_REG_CREG_ID */
+	DWORD	version;	/* ???? 0x00010000 */
+	DWORD	rgdb_off;	/* 0x08 Offset of 1st RGDB-block */
+	DWORD	uk2;		/* 0x0c */
+	WORD	rgdb_num;	/* 0x10 # of RGDB-blocks */
+	WORD	uk3;
+	DWORD	uk[3];
+	/* rgkn */
+} _w95creg;
+
+/* SECTION 2: Directory information (tree structure)
  *
- *   Disk Key Entry Structure:
- *	00: DWORD	- Free entry indicator(?)
- *	04: DWORD	- Hash = sum of bytes of keyname
- *	08: DWORD	- Root key indicator? unknown, but usually 0xFFFFFFFF on win95 systems
- *	0C: DWORD	- disk address of PreviousLevel Key.
- *	10: DWORD	- disk address of Next Sublevel Key.
- *	14: DWORD	- disk address of Next Key (on same level).
- * DKEP>18: WORD	- Nr, Low Significant part.
- *	1A: WORD	- Nr, High Significant part.
+ * once on offset 0x20
  *
- * The disk address always points to the nr part of the previous key entry 
- * of the referenced key. Don't ask me why, or even if I got this correct
- * from staring at 1kg of hexdumps. (DKEP)
+ * structure: [rgkn][dke]*	(repeat till rgkn->size is reached)
+ */
+#define	W95_REG_RGKN_ID	0x4e4b4752
+
+typedef struct
+{
+	DWORD	id;		/*"RGKN" = W95_REG_RGKN_ID */
+	DWORD	size;		/* Size of the RGKN-block */
+	DWORD	root_off;	/* Rel. Offset of the root-record */
+	DWORD	uk[5];
+} _w95rgkn;
+
+/* Disk Key Entry Structure
  *
- * The High significant part of the structure seems to equal the number
- * of the RGDB section. The low significant part is a unique ID within
- * that RGDB section
+ * the 1st entry in a "usual" registry file is a nul-entry with subkeys: the
+ * hive itself. It looks the same like other keys. Even the ID-number can
+ * be any value.
  *
- * There are two minor corrections to the position of that structure.
- * 1. If the address is xxx014 or xxx018 it will be aligned to xxx01c AND 
- *    the DKE reread from there.
- * 2. If the address is xxxFFx it will be aligned to (xxx+1)000.
- * CPS - I have not experienced the above phenomenon in my registry files
+ * The "hash"-value is a value representing the key's name. Windows will not
+ * search for the name, but for a matching hash-value. if it finds one, it
+ * will compare the actual string info, otherwise continue with the next key.
+ * To calculate the hash initialize a D-Word with 0 and add all ASCII-values 
+ * of the string which are smaller than 0x80 (128) to this D-Word.   
  *
- * RGDB_section:
- * 	00:		"RGDB"	- magic
- *	04: DWORD	offset to next RGDB section
- *	08: DWORD	?
- *	0C: WORD	always 000d?
- *	0E: WORD	RGDB block number
- *	10:	DWORD	? (equals value at offset 4 - value at offset 8)
- *	14..1F:		?
- *	20.....:	disk keys
- *
- * disk key:
- * 	00: 	DWORD	nextkeyoffset	- offset to the next disk key structure
- *	08: 	WORD	nrLS		- low significant part of NR
- *	0A: 	WORD	nrHS		- high significant part of NR
- *	0C: 	DWORD	bytesused	- bytes used in this structure.
- *	10: 	WORD	name_len	- length of name in bytes. without \0
- *	12: 	WORD	nr_of_values	- number of values.
- *	14: 	char	name[name_len]	- name string. No \0.
- *	14+name_len: disk values
- *	nextkeyoffset: ... next disk key
- *
- * disk value:
- *	00:	DWORD	type		- value type (hmm, could be WORD too)
- *	04:	DWORD			- unknown, usually 0
- *	08:	WORD	namelen		- length of Name. 0 means name=NULL
- *	0C:	WORD	datalen		- length of Data.
- *	10:	char	name[namelen]	- name, no \0
- *	10+namelen: BYTE	data[datalen] - data, without \0 if string
- *	10+namelen+datalen: next values or disk key
+ * If you want to modify key names, also modify the hash-values, since they
+ * cannot be found again (although they would be displayed in REGEDIT)
+ * End of list-pointers are filled with 0xFFFFFFFF
  *
  * Disk keys are layed out flat ... But, sometimes, nrLS and nrHS are both
  * 0xFFFF, which means skipping over nextkeyoffset bytes (including this
  * structure) and reading another RGDB_section.
- * repeat until end of file.
+ *
+ * there is a one to one relationship between dke and dkh
+ */
+ /* key struct, once per key */
+typedef struct
+{
+	DWORD	x1;		/* Free entry indicator(?) */
+	DWORD	hash;		/* sum of bytes of keyname */
+	DWORD	x3;		/* Root key indicator? usually 0xFFFFFFFF */
+	DWORD	prevlvl;	/* offset of previous key */
+	DWORD	nextsub;	/* offset of child key */
+	DWORD	next;		/* offset of sibling key */
+	WORD	nrLS;		/* id inside the rgdb block */
+	WORD	nrMS;		/* number of the rgdb block */
+} _w95dke;
+
+/* SECTION 3: key information, values and data
+ *
+ * structure:
+ *  section:	[blocks]*		(repeat creg->rgdb_num times)
+ *  blocks:	[rgdb] [subblocks]* 	(repeat till block size reached )
+ *  subblocks:	[dkh] [dkv]*		(repeat dkh->values times )
  *
  * An interesting relationship exists in RGDB_section. The value at offset
  * 10 equals the value at offset 4 minus the value at offset 8. I have no
  * idea at the moment what this means.  (Kevin Cozens)
+ */
+
+/* block header, once per block */
+#define W95_REG_RGDB_ID	0x42444752
+
+typedef struct
+{
+	DWORD	id;	/* 0x00 'rgdb' = W95_REG_RGDB_ID */
+	DWORD	size;	/* 0x04 */
+	DWORD	uk1;	/* 0x08 */
+	DWORD	uk2;	/* 0x0c */
+	DWORD	uk3;	/* 0x10 */
+	DWORD	uk4;	/* 0x14 */
+	DWORD	uk5;	/* 0x18 */
+	DWORD	uk6;	/* 0x1c */
+	/* dkh */
+} _w95rgdb;
+
+/* Disk Key Header structure (RGDB part), once per key */
+typedef	struct 
+{
+	DWORD	nextkeyoff; 	/* 0x00 offset to next dkh*/
+	WORD	nrLS;		/* 0x04 id inside the rgdb block */
+	WORD	nrMS;		/* 0x06 number of the rgdb block */
+	DWORD	bytesused;	/* 0x08 */
+	WORD	keynamelen;	/* 0x0c len of name */
+	WORD	values;		/* 0x0e number of values */
+	DWORD	xx1;		/* 0x10 */
+	char	name[1];	/* 0x14 */
+	/* dkv */		/* 0x14 + keynamelen */
+} _w95dkh;
+
+/* Disk Key Value structure, once per value */
+typedef	struct
+{
+	DWORD	type;		/* 0x00 */
+	DWORD	x1;		/* 0x04 */
+	WORD	valnamelen;	/* 0x08 length of name, 0 is default key */
+	WORD	valdatalen;	/* 0x0A length of data */
+	char	name[1];	/* 0x0c */
+	/* raw data */		/* 0x0c + valnamelen */
+} _w95dkv;
+
+/******************************************************************************
+ * _w95_lookup_dkh [Internal]
  *
- * FIXME: this description needs some serious help, yes.
+ * seeks the dkh belonging to a dke
  */
-
-struct	_w95keyvalue {
-	unsigned long		type;
-	unsigned short		datalen;
-	char			*name;
-	unsigned char		*data;
-	unsigned long		x1;
-	int			lastmodified;
-};
-
-struct 	_w95key {
-	char			*name;
-	int			nrofvals;
-	struct	_w95keyvalue	*values;
-	struct _w95key		*prevlvl;
-	struct _w95key		*nextsub;
-	struct _w95key		*next;
-};
-
-
-struct _w95_info {
-  char *rgknbuffer;
-  int  rgknsize;
-  char *rgdbbuffer;
-  int  rgdbsize;
-  int  depth;
-  int  lastmodified;
-};
-
-
-/******************************************************************************
- * _w95_processKey [Internal]
- */
-static HKEY _w95_processKey ( HKEY hkey, int nrLS, int nrMS, struct _w95_info *info )
-
+static _w95dkh * _w95_lookup_dkh (_w95creg *creg, int nrLS, int nrMS)
 {
-  /* Disk Key Header structure (RGDB part) */
-	struct	dkh {
-                unsigned long		nextkeyoff; 
-		unsigned short		nrLS;
-		unsigned short		nrMS;
-		unsigned long		bytesused;
-		unsigned short		keynamelen;
-		unsigned short		values;
-		unsigned long		xx1;
-		/* keyname */
-		/* disk key values or nothing */
-	};
-	/* Disk Key Value structure */
-	struct	dkv {
-		unsigned long		type;
-		unsigned long		x1;
-		unsigned short		valnamelen;
-		unsigned short		valdatalen;
-		/* valname, valdata */
-	};
-
+	_w95rgdb * rgdb;
+	_w95dkh * dkh;
+	int i;
 	
-	struct	dkh dkh;
-	int	bytesread = 0;
-	char    *rgdbdata = info->rgdbbuffer;
-	int     nbytes = info->rgdbsize;
-	char    *curdata = rgdbdata;
-	char    *end = rgdbdata + nbytes;
-	int     off_next_rgdb;
-	char    *next = rgdbdata;
-	int     nrgdb, i;
-        HKEY subkey;
+	rgdb = (_w95rgdb*)((char*)creg+creg->rgdb_off);		/* get the beginning of the rgdb datastore */
+	assert (creg->rgdb_num > nrMS);				/* check: requested block < last_block) */
 	
-	do {
-	  curdata = next;
-	  if (strncmp(curdata, "RGDB", 4)) return 0;
-	    
-	  memcpy(&off_next_rgdb,curdata+4,4);
-	  next = curdata + off_next_rgdb;
-	  nrgdb = (int) *((short *)curdata + 7);
-
-	} while (nrgdb != nrMS && (next < end));
-
-	/* curdata now points to the start of the right RGDB section */
-	curdata += 0x20;
-
-#define XREAD(whereto,len) \
-	if ((curdata + len) <= end) {\
-		memcpy(whereto,curdata,len);\
-		curdata+=len;\
-		bytesread+=len;\
+	/* find the right block */
+	for(i=0; i<nrMS ;i++)
+	{
+	  assert(rgdb->id == W95_REG_RGDB_ID);				/* check the magic */
+	  rgdb = (_w95rgdb*) ((char*)rgdb+rgdb->size);		/* find next block */
 	}
 
-	while (curdata < next) {
-	  struct	dkh *xdkh = (struct dkh*)curdata;
+	dkh = (_w95dkh*)(rgdb + 1);				/* first sub block within the rgdb */
 
-	  bytesread += sizeof(dkh); /* FIXME... nextkeyoff? */
-	  if (xdkh->nrLS == nrLS) {
-	  	memcpy(&dkh,xdkh,sizeof(dkh));
-	  	curdata += sizeof(dkh);
-	  	break;
-	  }
-	  curdata += xdkh->nextkeyoff;
-	};
+	do
+	{
+	  if(nrLS==dkh->nrLS ) return dkh;
+	  dkh = (_w95dkh*)((char*)dkh + dkh->nextkeyoff);	/* find next subblock */
+	} while ((char *)dkh < ((char*)rgdb+rgdb->size));
 
-	if (dkh.nrLS != nrLS) return 0;
+	return NULL;
+}	
+ 
+/******************************************************************************
+ * _w95_parse_dkv [Internal]
+ */
+static int _w95_parse_dkv (
+	HKEY hkey,
+	_w95dkh * dkh,
+	int nrLS,
+	int nrMS )
+{
+	_w95dkv * dkv;
+	int i;
+	DWORD ret;
+	char * name;
+			
+	/* first value block */
+	dkv = (_w95dkv*)((char*)dkh+dkh->keynamelen+0x14);
 
-	if (nrgdb != dkh.nrMS)
-	  return 0;
+	/* loop trought the values */
+	for (i=0; i< dkh->values; i++)
+	{
+	  name = _strdupnA(dkv->name, dkv->valnamelen+1);
+	  ret = RegSetValueExA(hkey, name, 0, dkv->type, &(dkv->name[dkv->valnamelen]),dkv->valdatalen); 
+	  if (ret) ERR("RegSetValueEx failed (0x%08lx)\n", ret);
+	  free (name);
 
-        assert((dkh.keynamelen<2) || curdata[0]);
-	subkey=_find_or_add_key(hkey,strcvtA2W(curdata, dkh.keynamelen));
-	curdata += dkh.keynamelen;
-
-	for (i=0;i< dkh.values; i++) {
-	  struct dkv dkv;
-	  LPBYTE data;
-	  int len;
-	  LPWSTR name;
-
-	  XREAD(&dkv,sizeof(dkv));
-
-	  name = strcvtA2W(curdata, dkv.valnamelen);
-	  curdata += dkv.valnamelen;
-
-	  if ((1 << dkv.type) & UNICONVMASK) {
-	    data = (LPBYTE) strcvtA2W(curdata, dkv.valdatalen);
-	    len = 2*(dkv.valdatalen + 1);
-	  } else {
-	    /* I don't think we want to NULL terminate all data */
-	    data = xmalloc(dkv.valdatalen);
-	    memcpy (data, curdata, dkv.valdatalen);
-	    len = dkv.valdatalen;
-	  }
-
-	  curdata += dkv.valdatalen;
-	  
-	  _find_or_add_value( subkey, name, dkv.type, data, len );
+	  /* next value */
+	  dkv = (_w95dkv*)((char*)dkv+dkv->valnamelen+dkv->valdatalen+0x0c);
 	}
-	return subkey;
+	return TRUE;
 }
 
 /******************************************************************************
- * _w95_walkrgkn [Internal]
+ * _w95_parse_dke [Internal]
  */
-static void _w95_walkrgkn( HKEY prevkey, char *off, 
-                           struct _w95_info *info )
-
+static int _w95_parse_dke( 
+	HKEY hkey,
+	_w95creg * creg,
+	_w95rgkn *rgkn,
+	_w95dke * dke,
+	int level )
 {
-  /* Disk Key Entry structure (RGKN part) */
-  struct	dke {
-    unsigned long		x1;
-    unsigned long		x2;
-    unsigned long		x3;/*usually 0xFFFFFFFF */
-    unsigned long		prevlvl;
-    unsigned long		nextsub;
-    unsigned long		next;
-    unsigned short		nrLS;
-    unsigned short		nrMS;
-  } *dke = (struct dke *)off;
-  HKEY subkey;
+	_w95dkh * dkh;
+	HKEY hsubkey = hkey;
+	char * name;
+	int ret = FALSE;
 
-  if (dke == NULL) {
-    dke = (struct dke *) ((char *)info->rgknbuffer);
-  }
+	/* get start address of root key block */
+	if (!dke) dke = (_w95dke*)((char*)rgkn + rgkn->root_off);
+	
+	/* create key */
+	if (dke->nrLS != 0xffff && dke->nrMS!=0xffff)		/* eg. the root key has no name */
+	{
+	  if (!(dkh = _w95_lookup_dkh(creg, dke->nrLS, dke->nrMS)))
+	  {
+	    fprintf(stderr, "dke pointing to missing dkh !\n");
+	    goto error;
+	  }
+	  /* subblock found */
+	  if ( level <= 0 )
+	  {
+	    name = _strdupnA( dkh->name, dkh->keynamelen+1);
+	    if (RegCreateKeyA(hkey, name, &hsubkey)) { free(name); goto error; }
+	    free(name);
+	  }
+	  _w95_parse_dkv(hsubkey, dkh, dke->nrLS, dke->nrMS);
+	}
+	else 
+	{
+	  level++; /* we have to skip the root-block anyway */
+	}
+	
+	/* walk sibling keys */
+	if (dke->next != 0xffffffff )
+	{
+    	  _w95_parse_dke(hkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->next), level);
+	}
 
-  subkey = _w95_processKey(prevkey, dke->nrLS, dke->nrMS, info);
-
-  if (dke->nextsub != -1 && 
-      ((dke->nextsub - 0x20) < info->rgknsize) 
-      && (dke->nextsub > 0x20)) {
-    
-    _w95_walkrgkn(subkey ? subkey : prevkey, /* XXX <-- This is a hack*/
-		  info->rgknbuffer + dke->nextsub - 0x20, 
-		  info);
-  }
-  if (subkey) RegCloseKey( subkey );
-
-  if (dke->next != -1 && 
-      ((dke->next - 0x20) < info->rgknsize) && 
-      (dke->next > 0x20)) {
-    _w95_walkrgkn(prevkey,  
-		  info->rgknbuffer + dke->next - 0x20,
-		  info);
-  }
+	/* next sub key */
+	if (dke->nextsub != 0xffffffff) 
+	{
+    	  _w95_parse_dke(hsubkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub), level-1);
+	}
+  
+	ret = TRUE;
+	if (hsubkey != hkey) RegCloseKey(hsubkey);
+error:	return ret;
 }
-
+/* end windows 95 loader */
 
 /******************************************************************************
- * _w95_loadreg [Internal]
+ *	NativeRegLoadKey [Internal]
+ *
+ * Loads a native registry file (win95/nt)
+ * 	hkey	root key
+ *	fn	filename
+ *	level	number of levels to cut away (eg. ".Default" in user.dat)
+ *
+ * this function intentionally uses unix file functions to make it possible
+ * to move it to a seperate registry helper programm
  */
-static void _w95_loadreg( char* fn, HKEY hkey )
+static int NativeRegLoadKey( HKEY hkey, char* fn, int level )
 {
-	HFILE		hfd;
-	char		magic[5];
-	unsigned long	where,version,rgdbsection,end;
-	struct          _w95_info info;
-	OFSTRUCT	ofs;
-	BY_HANDLE_FILE_INFORMATION hfdinfo;
+	int fd = 0;
+	struct stat st;
+        DOS_FULL_NAME full_name;
+	int ret = FALSE;
+	void * base;
+			
+        if (!DOSFS_GetFullName( fn, 0, &full_name )) return FALSE;
+	
+	/* map the registry into the memory */
+	if ((fd = open(full_name.long_name, O_RDONLY | O_NONBLOCK)) == -1) return FALSE;
+	if ((fstat(fd, &st) == -1)) goto error;
+	if ((base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto error;
 
-	TRACE("Loading Win95 registry database '%s'\n",fn);
-	hfd=OpenFile(fn,&ofs,OF_READ);
-	if (hfd==HFILE_ERROR)
-		return;
-	magic[4]=0;
-	if (4!=_lread(hfd,magic,4))
-		return;
-	if (strcmp(magic,"CREG")) {
-		WARN("%s is not a w95 registry.\n",fn);
-		return;
-	}
-	if (4!=_lread(hfd,&version,4))
-		return;
-	if (4!=_lread(hfd,&rgdbsection,4))
-		return;
-	if (-1==_llseek(hfd,0x20,SEEK_SET))
-		return;
-	if (4!=_lread(hfd,magic,4))
-		return;
-	if (strcmp(magic,"RGKN")) {
-		WARN("second IFF header not RGKN, but %s\n", magic);
-		return;
+	switch (*(LPDWORD)base)
+	{
+	  /* windows 95 'creg' */
+	  case W95_REG_CREG_ID:
+	    {
+	      _w95creg * creg;
+	      _w95rgkn * rgkn;
+	      creg = base;
+	      TRACE_(reg)("Loading win95 registry '%s' '%s'\n",fn, full_name.long_name);
+
+	      /* load the header (rgkn) */
+	      rgkn = (_w95rgkn*)(creg + 1);
+	      if (rgkn->id != W95_REG_RGKN_ID) 
+	      {
+		ERR("second IFF header not RGKN, but %lx\n", rgkn->id);
+		goto error1;
+	      }
+
+	      ret = _w95_parse_dke(hkey, creg, rgkn, NULL, level);
+	    }
+	    break;
+	  /* nt 'regf'*/
+	  case NT_REG_HEADER_BLOCK_ID:
+	    {
+	      nt_regf * regf;
+	      nt_hbin * hbin;
+	      nt_hbin_sub * hbin_sub;
+	      nt_nk* nk;
+
+	      TRACE_(reg)("Loading nt registry '%s' '%s'\n",fn, full_name.long_name);
+
+	      /* start block */
+	      regf = base;
+
+	      /* hbin block */
+	      hbin = base + 0x1000;
+	      if (hbin->id != NT_REG_POOL_BLOCK_ID)
+	      {
+	        ERR_(reg)( "%s hbin block invalid\n", fn);
+	        goto error1;
+	      }
+
+	      /* 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 error1;
+	      }
+
+	      /* nk block */
+	      nk = (nt_nk*)&(hbin_sub->data[0]);
+	      if (nk->Type != NT_REG_ROOT_KEY_BLOCK_TYPE)
+	      {
+	        ERR_(reg)( "%s special nk block not found\n", fn);
+	        goto error1;
+	      }
+
+	      ret = _nt_parse_nk (hkey, base+0x1000, nk, level);
+	    }
+	    break;
+	  default:
+	    {
+	      ERR("unknown signature in registry file %s.\n",fn);
+	      goto error1;
+	    }
 	}
 
-	/* STEP 1: Keylink structures */
-	if (-1==_llseek(hfd,0x40,SEEK_SET))
-		return;
-	where	= 0x40;
-	end	= rgdbsection;
-
-	info.rgknsize = end - where;
-	info.rgknbuffer = (char*)xmalloc(info.rgknsize);
-	if (info.rgknsize != _lread(hfd,info.rgknbuffer,info.rgknsize))
-		return;
-
-	if (!GetFileInformationByHandle(hfd,&hfdinfo))
-		return;
-
-	end = hfdinfo.nFileSizeLow;
-	info.lastmodified = DOSFS_FileTimeToUnixTime(&hfdinfo.ftLastWriteTime,NULL);
-
-	if (-1==_llseek(hfd,rgdbsection,SEEK_SET))
-		return;
-
-	info.rgdbbuffer = (char*)xmalloc(end-rgdbsection);
-	info.rgdbsize = end - rgdbsection;
-
-	if (info.rgdbsize !=_lread(hfd,info.rgdbbuffer,info.rgdbsize))
-		return;
-	_lclose(hfd);
-
-	_w95_walkrgkn(hkey, NULL, &info);
-
-	free (info.rgdbbuffer);
-	free (info.rgknbuffer);
+error1:	munmap(base, st.st_size);
+error:	close(fd);
+	return ret;	
 }
 
-
 /* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjøwall, tor@sn.no */
-
 /*
     reghack - windows 3.11 registry data format demo program.
 
@@ -1529,26 +1454,35 @@
 	return;
 }
 
+/**********************************************************************************
+ * SetLoadLevel [Internal]
+ *
+ * set level to 0 for loading system files
+ * set level to 1 for loading user files
+ */
+static void SetLoadLevel(int level)
+{
+	struct set_registry_levels_request *req = get_req_buffer();
+
+	req->current = level;
+	req->saving  = 0;
+	req->version = 1;
+	server_call( REQ_SET_REGISTRY_LEVELS );
+}
 
 /**********************************************************************************
  * SHELL_LoadRegistry [Internal]
  */
 void SHELL_LoadRegistry( void )
 {
-  struct set_registry_levels_request *req = get_req_buffer();
-  int save_timeout;
-  char	      *fn, *home;
-  HKEY		    hkey;
+  int	save_timeout;
+  char	*fn, *home;
+  HKEY	hkey;
 
   TRACE("(void)\n");
 
   REGISTRY_Init();
-
-  /* set level to 0 for loading system files */
-  req->current = 0;
-  req->saving  = 0;
-  req->version = 1;
-  server_call( REQ_SET_REGISTRY_LEVELS );
+  SetLoadLevel(0);
 
   if (PROFILE_GetWineIniBool ("registry", "LoadWin311RegistryFiles", 1)) 
   { 
@@ -1558,9 +1492,9 @@
   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);
+      NativeRegLoadKey(HKEY_LOCAL_MACHINE, "C:\\system.1st", 0);
+      NativeRegLoadKey(HKEY_LOCAL_MACHINE, "system.dat", 0);
+      NativeRegLoadKey(HKEY_CURRENT_USER, "user.dat", 1);
   }
   if (PROFILE_GetWineIniBool ("registry", "LoadWinNTRegistryFiles", 1)) 
   { 
@@ -1572,7 +1506,7 @@
 	 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 );
+         NativeRegLoadKey( HKEY_CURRENT_USER, fn, 1 );
       }     
       /*
       * FIXME
@@ -1582,19 +1516,19 @@
 
       strcpy(home, fn);
       strncat(home, "\\config\\system", MAX_PATHNAME_LEN - strlen(home) - 1);
-      _nt_loadreg(HKEY_LOCAL_MACHINE, home);
+      NativeRegLoadKey(HKEY_LOCAL_MACHINE, home, 0);
 
       strcpy(home, fn);
       strncat(home, "\\config\\software", MAX_PATHNAME_LEN - strlen(home) - 1);
-      _nt_loadreg(HKEY_LOCAL_MACHINE, home);
+      NativeRegLoadKey(HKEY_LOCAL_MACHINE, home, 0);
 
       strcpy(home, fn);
       strncat(home, "\\config\\sam", MAX_PATHNAME_LEN - strlen(home) - 1);
-      _nt_loadreg(HKEY_LOCAL_MACHINE, home);
+      NativeRegLoadKey(HKEY_LOCAL_MACHINE, home, 0);
 
       strcpy(home, fn);
       strncat(home, "\\config\\security", MAX_PATHNAME_LEN - strlen(home) - 1);
-      _nt_loadreg(HKEY_LOCAL_MACHINE, home);
+      NativeRegLoadKey(HKEY_LOCAL_MACHINE, home, 0);
 
       free (home);
       free (fn);
@@ -1616,11 +1550,7 @@
       _wine_loadreg( HKEY_LOCAL_MACHINE, SAVE_LOCAL_MACHINE_DEFAULT );
   }
 
-  /* set level to 1 for loading user files */
-  req->current = 1;
-  req->saving  = 0;
-  req->version = 1;
-  server_call( REQ_SET_REGISTRY_LEVELS );
+  SetLoadLevel(1);
 
   /*
    * Load the user saved registries