[Forensics-changes] [SCM] debian-forensics/reglookup branch, upstream, updated. upstream/0.11.0-1-g1295e55

Christophe Monniez christophe.monniez at fccu.be
Mon Mar 22 08:18:46 UTC 2010


The following commit has been merged in the upstream branch:
commit 1295e556f9c820bed6fdb39f6995dbd979081b07
Author: Christophe Monniez <christophe.monniez at fccu.be>
Date:   Sat Mar 20 11:26:02 2010 +0100

    Adding uptream version 0.12.0.

diff --git a/Makefile b/Makefile
index e22d83c..a19a61f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile 147 2009-02-22 19:31:52Z tim $
+# $Id: Makefile 159 2009-12-06 20:09:01Z tim $
 
 # Installation prefixes.  Change to install elsewhere.
 
@@ -10,8 +10,8 @@ MAN_PREFIX=$(PREFIX)/man
 ################################################################################
 
 CC=gcc
-OPTS=-std=gnu89 -pedantic -Wall -ggdb
-#OPTS=-std=gnu89 -pedantic -Wall
+OPTS=-std=gnu99 -pedantic -Wall -ggdb
+#OPTS=-std=gnu99 -pedantic -Wall
 INC:=-I$(PWD)/include -I/usr/local/include 
 LIB=-L/usr/local/lib -lm
 BIN_EXT=
diff --git a/bin/reglookup-timeline b/bin/reglookup-timeline
index cd8ab4d..67c7eaa 100755
--- a/bin/reglookup-timeline
+++ b/bin/reglookup-timeline
@@ -4,7 +4,7 @@
 # files to produce an MTIME sorted output.  This is helpful when building
 # timelines for investigations.
 #
-# Copyright (C) 2005-2007 Timothy D. Morgan
+# Copyright (C) 2005-2007,2010 Timothy D. Morgan
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -19,13 +19,14 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
 #
-# $Id: reglookup-timeline 91 2007-03-28 19:26:37Z tim $
+# $Id: reglookup-timeline 170 2010-03-06 04:40:25Z tim $
 
 
 usage()
 {
-  echo "Usage: $0 [-H] <REGISTRY_FILE> [<REGISTRY_FILE> ...]" 1>&2
+  echo "Usage: $0 [-H] [-V] <REGISTRY_FILE> [<REGISTRY_FILE> ...]" 1>&2
   echo "   -H  Omit header line" 1>&2
+  echo "   -V  Include values with parent timestamps" 1>&2
 }
 
 if [ $# -eq 0 ]; then
@@ -40,10 +41,16 @@ if [ "$1" = "-H" ]; then
   shift
 fi
 
+OPTS='-t KEY'
+if [ "$1" = "-V" ]; then
+  OPTS='-i'
+  shift
+fi
+
 if [ "$PRINT_HEADER" = "true" ]; then
   echo "MTIME,FILE,PATH"
 fi
 
 for F in $@; do
-  reglookup -t KEY -H "$F" | awk -F',' '{ printf "%s,'"$F"',%s\n",$4,$1; }'
+  reglookup $OPTS -H "$F" | awk -F',' '{ printf "%s,'"$F"',%s\n",$4,$1; }'
 done | sort
diff --git a/doc/devel/README b/doc/devel/README
new file mode 100644
index 0000000..2960276
--- /dev/null
+++ b/doc/devel/README
@@ -0,0 +1,4 @@
+Developer's documentation previously stored here has been removed from the
+main software distribution trunk.  It is still available in the Subversion
+repository, however, and may be obtained here:
+  http://code.google.com/p/reglookup/source/browse/
diff --git a/doc/devel/TODO b/doc/devel/TODO
deleted file mode 100644
index 549fac8..0000000
--- a/doc/devel/TODO
+++ /dev/null
@@ -1,57 +0,0 @@
-$Id: TODO 153 2009-06-02 22:28:07Z tim $
-
-If you are interested in contributing to this project, here's a few
-things you could look into:
-
- - Currently there is no way on the command line to search for exotic 
-   paths/types.  For instance, if reglookup encounters an unknown VK
-   type, it just prints it out in Hex.  However, if you wanted to search
-   specifically for that type, there is no way to do it.  Similarly, it
-   isn't possible to specify certain binary or weird characters in
-   paths.  Reglookup should take the user path and unquote each path
-   component using the \xQQ syntax prior to searching.
-
- - It might be nice to have a way to filter results by security 
-   descriptor information.  Maybe by MTIME as well.
-
- - Testing, testing, and more testing.  reglookup needs to be tested on 
-   NT/XP/2k3/Vista.  A regression test suite would be nice too.  Some 
-   thoughts on this include a script which randomly fuzzes an existing
-   registry file, and tries to detect crashes of reglookup when parsing
-   it.  Another test script might randomly truncate an existing registry
-   file, which will help improve reglookup's parsing on fragmentary
-   files.
-
- - Build system.  I do not wish to use automake/autoconf in this
-   project.  I have also now decided that make is painful to use for
-   everything.  I'd like to switch to a suite of shell scripts driven by
-   minimal make rules.  If you got any ideas on this, shoot them my way.
-
- - Unicode support still needs improvement.  While parsing strings seems
-   to be decent, UTF-8 output would be nice.  Need support for
-   UTF-16LE key and value names.  To do this, the UTF conversion
-   functions need to be moved to regfi.
-
- - The interface between reglookup.c and regfi.c is much better than it
-   used to be, but the parsing of data objects needs to be moved into the
-   library.  The quoting syntax should stay in reglookup/reglookup-recover
-   but the basic parsing of data types into proper structures should 
-   happen in the library so that they are accessible to other users of the
-   library.
-
- - Develop and solidify regfi API.  Regfi should be better documented and 
-   eventually needs a set of Python wrappers.
-
- - Documentation.  The security descriptor output format needs to be 
-   documented.  Also, function contracts should be added to the 
-   lower-level functions of regfi.c.
-
- - The smb_deps.h and smb_deps.c content is almost eliminated.  Just need
-   to integrate parts that are being kept into regfi or other modules.
-
- - Need to figure out a reasonably correct way to convert UTF-16LE charaters
-   to ASCII under Windows/MingW or other platforms that don't have proper 
-   libiconv support yet.  Then a build-time option or autodetection can 
-   dictate which version of conversion function is used.
-
- - Grep through the source for 'XXX', and you'll find more.
diff --git a/doc/devel/references.txt b/doc/devel/references.txt
deleted file mode 100644
index 457bfa9..0000000
--- a/doc/devel/references.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-- The Windows NT Registry File Format
-  (A work in progress to support this tool.)
-  http://sentinelchicken.com/research/registry_format/
-
-- Recovering Deleted Data From the Windows Registry
-  (The research that is implemented as a PoC in reglookup-recover.)
-  http://sentinelchicken.com/research/registry_recovery/
-
-- Petter Nordahl-Hagen.  Windows NT registry file format description.
-  (The file 'winntreg.txt' included in this distribution is derived from this.)
-  http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt
-
-- Some useful information on how Windows reads from and writes to registry
-  hives:
-  http://www.microsoft.com/technet/archive/winntas/tips/winntmag/inreg.mspx
-
-- Registry key, value, and depth limits:
-  http://msdn2.microsoft.com/en-us/library/ms724872.aspx
-
-- Misc references for windows registry permissions and ownership:
-  http://msdn2.microsoft.com/en-gb/library/ms724878.aspx
-  http://technet2.microsoft.com/WindowsServer/en/library/86cf2457-4f17-43f8-a2ab-7f4e2e5659091033.mspx?mfr=true
-  http://msdn2.microsoft.com/en-gb/library/aa374892.aspx
-
-- ACL/ACE flags information
-  http://support.microsoft.com/kb/220167
-  http://msdn2.microsoft.com/en-us/library/aa772242.aspx
-
-- Info on SAM hive, syskey, and hash extraction (with tools bkhive and samdump2):
-  http://www.studenti.unina.it/~ncuomo/syskey/
diff --git a/doc/devel/winntreg.txt b/doc/devel/winntreg.txt
deleted file mode 100644
index 31bbdf1..0000000
--- a/doc/devel/winntreg.txt
+++ /dev/null
@@ -1,272 +0,0 @@
-The windows NT registry has 2 different blocks, where one can occur many
-times...
-
-the "regf"-Block
-================
- 
-"regf" is obviosly the abbreviation for "Registry file". "regf" is the
-signature of the header-block which is always 4kb in size, although only
-the first 64 bytes seem to be used and a checksum is calculated over
-the first 0x200 bytes only!
-
-Offset            Size      Contents
-0x00000000      D-Word      ID: ASCII-"regf" = 0x66676572
-0x00000004      D-Word      ???? //see struct REGF
-0x00000008      D-Word      ???? Always the same value as at 0x00000004
-0x0000000C      Q-Word      last modify date in WinNT date-format
-0x00000014      D-Word      1
-0x00000018      D-Word      3
-0x0000001C      D-Word      0
-0x00000020      D-Word      1
-0x00000024      D-Word      Offset of 1st key record
-0x00000028      D-Word      Size of the data-blocks (Filesize-4kb)
-0x0000002C      D-Word      1
-0x000001FC      D-Word      Sum of all D-Words from 0x00000000 to
-                            0x000001FB  //XOR of all words. Nigel
-
-I have analyzed more registry files (from multiple machines running
-NT 4.0 german version) and could not find an explanation for the values
-marked with ???? the rest of the first 4kb page is not important...
-
-the "hbin"-Block
-================
-I dont know what "hbin" stands for, but this block is always a multiple
-of 4kb in size.
-
-Inside these hbin-blocks the different records are placed. The memory-
-management looks like a C-compiler heap management to me...
-
-hbin-Header
-===========
-Offset      Size      Contents
-0x0000      D-Word      ID: ASCII-"hbin" = 0x6E696268
-0x0004      D-Word      Offset from the 1st hbin-Block
-0x0008      D-Word      Offset to the next hbin-Block
-0x001C      D-Word      Block-size
-
-The values in 0x0008 and 0x001C should be the same, so I dont know
-if they are correct or swapped...
-
-From offset 0x0020 inside a hbin-block data is stored with the following
-format:
-
-Offset      Size      Contents
-0x0000      D-Word      Data-block size    //this size must be a
-multiple of 8. Nigel
-0x0004      ????      Data
- 
-If the size field is negative (bit 31 set), the corresponding block
-is free and has a size of -blocksize!
-
-That does not seem to be true. All block lengths seem to be negative! 
-(Richard Sharpe) 
-
-The data is stored as one record per block. Block size is a multiple
-of 4 and the last block reaches the next hbin-block, leaving no room.
-
-(That also seems incorrect, in that the block size if a multiple of 8.
-That is, the block, including the 4 byte header, is always a multiple of
-8 bytes. Richard Sharpe.)
-
-Records in the hbin-blocks
-==========================
-
-nk-Record
-
-      The nk-record can be treated as a kombination of tree-record and
-      key-record of the win 95 registry.
-
-lf-Record
-
-      The lf-record is the counterpart to the RGKN-record (the
-      hash-function)
-
-vk-Record
-
-      The vk-record consists information to a single value.
-
-sk-Record
-
-      sk (? Security Key ?) is the ACL of the registry.
-
-Value-Lists
-
-      The value-lists contain information about which values are inside a
-      sub-key and dont have a header.
-
-Datas
-
-      The datas of the registry are (like the value-list) stored without a
-      header.
-
-All offset-values are relative to the first hbin-block and point to the
-block-size field of the record-entry. to get the file offset, you have to add
-the header size (4kb) and the size field (4 bytes)...
-
-the nk-Record
-=============
-Offset      Size      Contents
-0x0000      Word      ID: ASCII-"nk" = 0x6B6E
-0x0002      Word      for the root-key: 0x2C, otherwise 0x20  //key symbolic links 0x10. Nigel
-0x0004      Q-Word      write-date/time in windows nt notation
-0x000C      D-Word      UNKNOWN // added by TDM
-0x0010      D-Word      Offset of Owner/Parent key
-0x0014      D-Word      number of sub-Keys
-0x0018      D-Word      UNKNOWN // added by TDM
-0x001C      D-Word      Offset of the sub-key lf-Records
-0x0020      D-Word      UNKNOWN // added by TDM
-0x0024      D-Word      number of values
-0x0028      D-Word      Offset of the Value-List
-0x002C      D-Word      Offset of the sk-Record
-
-0x0030      D-Word      Offset of the Class-Name //see NK structure for the use of these fields. Nigel
-0x0044      D-Word      Unused (data-trash)  //some kind of run time index. Does not appear to be important. Nigel
-0x0048      Word      name-length
-0x004A      Word      class-name length
-0x004C      ????      key-name
-
-the Value-List
-==============
-Offset      Size      Contents
-0x0000      D-Word      Offset 1st Value
-0x0004      D-Word      Offset 2nd Value
-0x????      D-Word      Offset nth Value
-
-To determine the number of values, you have to look at the owner-nk-record!
-
-Der vk-Record
-=============
-Offset      Size      Contents
-0x0000      Word      ID: ASCII-"vk" = 0x6B76
-0x0002      Word      name length
-0x0004      D-Word      length of the data   //if top bit is set when offset contains data. Nigel
-0x0008      D-Word      Offset of Data
-0x000C      D-Word      Type of value
-0x0010      Word      Flag
-0x0012      Word      Unused (data-trash)
-0x0014      ????      Name
-
-If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
-
-If the data-size is lower 5, the data-offset value is used to store the data itself!
-
-The data-types
-==============
-Wert      Beteutung
-0x0001      RegSZ:             character string (in UNICODE!)
-0x0002      ExpandSZ:   string with "%var%" expanding (UNICODE!)
-0x0003      RegBin:           raw-binary value
-0x0004      RegDWord:   Dword
-0x0007      RegMultiSZ:      multiple strings, seperated with 0
-                  (UNICODE!)
-
-The "lf"/"lh"/"ri"-record (hash list header)
-===============
-Offset      Size      Contents
-0x0000      Word      ID: ASCII-"lf" = 0x666C  (or "lh" or "ri")
-0x0002      Word      number of keys
-0x0004      ????      Hash-Records
-
-Hash-Record
-===========
-Offset      Size      Contents
-0x0000      D-Word      Offset of corresponding "nk"-Record
-0x0004      D-Word      ASCII: the first 4 characters of the key-name, padded with 0-s. Case sensitive! 
-			(the hash value may be computed differently for the various header types 
-			 "lf"/"lh"/"ri"/etc)
-Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the 
-key-name you have to change the hash-value too!
-
-//These hashrecords must be sorted low to high within the lf record. Nigel.
-
-The "sk"-block
-==============
-(due to the complexity of the SAM-info, not clear jet)
-(This is just a self-relative security descriptor in the data. R Sharpe.) 
-
-
-Offset      Size      Contents
-0x0000      Word      ID: ASCII-"sk" = 0x6B73
-0x0002      Word      Unused
-0x0004      D-Word      Offset of previous "sk"-Record
-0x0008      D-Word      Offset of next "sk"-Record
-0x000C      D-Word      usage-counter
-0x0010      D-Word      Size of "sk"-record in bytes
-????                                             //standard self
-relative security desciptor. Nigel
-????  ????      Security and auditing settings...
-????
-
-The usage counter counts the number of references to this
-"sk"-record. You can use one "sk"-record for the entire registry!
-
-Windows nt date/time format
-===========================
-The time-format is a 64-bit integer which is incremented every
-0,0000001 seconds by 1 (I dont know how accurate it really is!)
-It starts with 0 at the 1st of january 1601 0:00! All values are
-stored in GMT time! The time-zone is important to get the real
-time!
-
-Common values for win95 and win-nt
-==================================
-Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
-If a value has no name (length=0, flag(bit 0)=0), it is treated as the
-"Default" entry...
-If a value has no data (length=0), it is displayed as empty.
-
-simplyfied win-3.?? registry:
-=============================
-
-+-----------+
-| next rec. |---+                      +----->+------------+
-| first sub |   |                      |      | Usage cnt. |
-| name      |   |  +-->+------------+  |      | length     |
-| value     |   |  |   | next rec.  |  |      | text       |------->+-------+
-+-----------+   |  |   | name rec.  |--+      +------------+        | xxxxx |
-   +------------+  |   | value rec. |-------->+------------+        +-------+
-   v               |   +------------+         | Usage cnt. |
-+-----------+      |                          | length     |
-| next rec. |      |                          | text       |------->+-------+
-| first sub |------+                          +------------+        | xxxxx |
-| name      |                                                       +-------+
-| value     |
-+-----------+    
-
-Greatly simplyfied structure of the nt-registry:
-================================================
-   
-+---------------------------------------------------------------+
-|                                                               |
-v                                                               |
-+---------+     +---------->+-----------+  +----->+---------+   |
-| "nk"    |     |           | lf-rec.   |  |      | nk-rec. |   |
-| ID      |     |           | # of keys |  |      | parent  |---+
-| Date    |     |           | 1st key   |--+      | ....    |
-| parent  |     |           +-----------+         +---------+
-| suk-keys|-----+
-| values  |--------------------->+----------+
-| SK-rec. |---------------+      | 1. value |--> +----------+
-| class   |--+            |      +----------+    | vk-rec.  |
-+---------+  |            |                      | ....     |
-             v            |                      | data     |--> +-------+
-      +------------+      |                      +----------+    | xxxxx |
-      | Class name |      |                                      +-------+
-      +------------+      |
-                          v
-          +---------+    +---------+
-   +----->| next sk |--->| Next sk |--+
-   |  +---| prev sk |<---| prev sk |  |
-   |  |   | ....    |    | ...     |  |
-   |  |   +---------+    +---------+  |
-   |  |                    ^          |
-   |  |                    |          |
-   |  +--------------------+          |
-   +----------------------------------+
-
----------------------------------------------------------------------------
-
-Hope this helps....  (Although it was *fun* for me to uncover this things,
-                  it took me several sleepless nights ;)
-
-            B.D.
diff --git a/doc/man/man1/reglookup-recover.1.gz b/doc/man/man1/reglookup-recover.1.gz
index b885411..82e8ad0 100644
Binary files a/doc/man/man1/reglookup-recover.1.gz and b/doc/man/man1/reglookup-recover.1.gz differ
diff --git a/doc/man/man1/reglookup-timeline.1.gz b/doc/man/man1/reglookup-timeline.1.gz
index c1c4d3f..ddbb3df 100644
Binary files a/doc/man/man1/reglookup-timeline.1.gz and b/doc/man/man1/reglookup-timeline.1.gz differ
diff --git a/doc/man/man1/reglookup.1.gz b/doc/man/man1/reglookup.1.gz
index 9f14fd4..872fb55 100644
Binary files a/doc/man/man1/reglookup.1.gz and b/doc/man/man1/reglookup.1.gz differ
diff --git a/doc/reglookup.1.docbook b/doc/reglookup.1.docbook
index 9f208ff..7bf3a83 100644
--- a/doc/reglookup.1.docbook
+++ b/doc/reglookup.1.docbook
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <refentry id='reglookup.1'>
-  <!--  $Id: reglookup.1.docbook 138 2009-02-08 19:53:48Z tim $ -->
+  <!--  $Id: reglookup.1.docbook 176 2010-03-09 03:10:10Z tim $ -->
   <refmeta>
     <refentrytitle>reglookup</refentrytitle>
     <manvolnum>1</manvolnum>
@@ -85,6 +85,24 @@
         </listitem>
       </varlistentry>
     </variablelist>
+    
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-i</option>
+        </term>
+        <listitem>
+	  <para>
+	    Printed values inherit the timestamp of their parent key, which is
+            printed along with them.  Note that this timestamp is not
+            necessarily meaningful for any given value values because timestamps
+            are saved on keys only and you cannot tell which value has been
+            modified since a change to any value of a given key would update the
+            time stamp.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
 
     <variablelist remap='IP'>
       <varlistentry>
diff --git a/include/byteorder.h b/include/byteorder.h
index 9ae20d7..87cb61b 100644
--- a/include/byteorder.h
+++ b/include/byteorder.h
@@ -1,4 +1,4 @@
-/* 
+/*
  * Branched from Samba project Subversion repository, version #2:
  *  http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/byteorder.h
  *
@@ -21,15 +21,19 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: byteorder.h 111 2008-05-01 04:06:22Z tim $
+ * $Id: byteorder.h 168 2010-03-03 00:08:42Z tim $
  */
 
 #ifndef _BYTEORDER_H
 #define _BYTEORDER_H
 
-/*
-   This file implements macros for machine independent short and 
-   int manipulation
+/**
+ * @file
+ *
+ * This file implements macros for machine independent short and 
+ * int manipulation
+
+ at verbatim
 
 Here is a description of this file that I emailed to the samba list once:
 
@@ -53,8 +57,8 @@ CAREFUL_ALIGNMENT=0 on those processors as well.
 
 Ok, now to the macros themselves. I'll take a simple example, say we
 want to extract a 2 byte integer from a SMB packet and put it into a
-type called uint16 that is in the local machines byte order, and you
-want to do it with only the assumption that uint16 is _at_least_ 16
+type called uint16_t that is in the local machines byte order, and you
+want to do it with only the assumption that uint16_t is _at_least_ 16
 bits long (this last condition is very important for architectures
 that don't have any int types that are 2 bytes long)
 
@@ -64,10 +68,10 @@ You do this:
 #define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
 #define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
 
-then to extract a uint16 value at offset 25 in a buffer you do this:
+then to extract a uint16_t value at offset 25 in a buffer you do this:
 
 char *buffer = foo_bar();
-uint16 xx = SVAL(buffer,25);
+uint16_t xx = SVAL(buffer,25);
 
 We are using the byteoder independence of the ANSI C bitshifts to do
 the work. A good optimising compiler should turn this into efficient
@@ -97,6 +101,8 @@ RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering
 
 it also defines lots of intermediate macros, just ignore those :-)
 
+ at endverbatim
+
 */
 
 #undef CAREFUL_ALIGNMENT
@@ -123,37 +129,37 @@ it also defines lots of intermediate macros, just ignore those :-)
 #define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
 #define SSVALX(buf,pos,val) (CVAL_NC(buf,pos)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos+1)=(unsigned char)((val)>>8))
 #define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
-#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
-#define IVALS(buf,pos) ((int32)IVAL(buf,pos))
-#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16)(val)))
-#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32)(val)))
-#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
-#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32)(val)))
+#define SVALS(buf,pos) ((int16_t)SVAL(buf,pos))
+#define IVALS(buf,pos) ((int32_t)IVAL(buf,pos))
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16_t)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32_t)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16_t)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32_t)(val)))
 
 #else /* CAREFUL_ALIGNMENT */
 
 /* this handles things for architectures like the 386 that can handle
    alignment errors */
 /*
-   WARNING: This section is dependent on the length of int16 and int32
+   WARNING: This section is dependent on the length of int16_t and int32_t
    being correct 
 */
 
 /* get single value from an SMB buffer */
-#define SVAL(buf,pos) (*(const uint16 *)((const char *)(buf) + (pos)))
-#define SVAL_NC(buf,pos) (*(uint16 *)((char *)(buf) + (pos))) /* Non const version of above. */
-#define IVAL(buf,pos) (*(const uint32 *)((const char *)(buf) + (pos)))
-#define IVAL_NC(buf,pos) (*(uint32 *)((char *)(buf) + (pos))) /* Non const version of above. */
-#define SVALS(buf,pos) (*(const int16 *)((const char *)(buf) + (pos)))
-#define SVALS_NC(buf,pos) (*(int16 *)((char *)(buf) + (pos))) /* Non const version of above. */
-#define IVALS(buf,pos) (*(const int32 *)((const char *)(buf) + (pos)))
-#define IVALS_NC(buf,pos) (*(int32 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define SVAL(buf,pos) (*(const uint16_t *)((const char *)(buf) + (pos)))
+#define SVAL_NC(buf,pos) (*(uint16_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVAL(buf,pos) (*(const uint32_t *)((const char *)(buf) + (pos)))
+#define IVAL_NC(buf,pos) (*(uint32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define SVALS(buf,pos) (*(const int16_t *)((const char *)(buf) + (pos)))
+#define SVALS_NC(buf,pos) (*(int16_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVALS(buf,pos) (*(const int32_t *)((const char *)(buf) + (pos)))
+#define IVALS_NC(buf,pos) (*(int32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
 
 /* store single value in an SMB buffer */
-#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16)(val))
-#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32)(val))
-#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16)(val))
-#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32)(val))
+#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16_t)(val))
+#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32_t)(val))
+#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16_t)(val))
+#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32_t)(val))
 
 #endif /* CAREFUL_ALIGNMENT */
 
diff --git a/include/lru_cache.h b/include/lru_cache.h
index 80f77d3..b718bca 100644
--- a/include/lru_cache.h
+++ b/include/lru_cache.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2009 Timothy D. Morgan
+ * Copyright (C) 2008-2010 Timothy D. Morgan
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,9 +14,17 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: lru_cache.h 147 2009-02-22 19:31:52Z tim $
+ * $Id: lru_cache.h 169 2010-03-03 19:24:58Z tim $
  */
 
+/**
+ * @file
+ *
+ * A data structure which approximates a least recently used (LRU) cache.
+ * Implemented as a basic randomized hash table.
+ */
+
+
 #ifndef LRU_CACHE_H
 #define LRU_CACHE_H
 
@@ -41,6 +49,8 @@ struct lru_cache_element
   lru_cache_element* newer;
 };
 
+
+/** XXX: document this. */
 typedef struct _lru_cache
 {
   uint32_t secret;
@@ -54,26 +64,47 @@ typedef struct _lru_cache
 } lru_cache;
 
 
+/**
+ * XXX: finish documenting.
+ */
 lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret);
+
+
+/**
+ * XXX: finish documenting.
+ */
 lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys, 
 				uint32_t secret, bool talloc_data);
+
+
+/**
+ * XXX: finish documenting.
+ */
 void lru_cache_destroy(lru_cache* ht);
 
-/* 
- * 
+
+/**
+ * XXX: finish documenting.
  */
 bool lru_cache_update(lru_cache* ht, const void* index, 
 		      uint32_t index_len, void* data);
 
-/* Returns pointer to data previously stored at index.
- * If no data was found at index, NULL is returned.
+/**
+ * XXX: finish documenting.
+ *
+ * @return A pointer to data previously stored at index.
+ *         If no data was found at index, NULL is returned.
  */
 void* lru_cache_find(lru_cache* ht, const void* index, 
 		     uint32_t index_len);
 
-/* Removes entry from table at index.
- * Returns pointer to data that was there previously.  
- * Returns NULL if no entry is at index.
+/**
+ * XXX: finish documenting.
+ *
+ * Removes entry from table at index.
+ *
+ * @return A pointer to data that was there previously or NULL if no entry is
+ *         at index.
  */
 bool lru_cache_remove(lru_cache* ht, const void* index, 
 		      uint32_t index_len);
diff --git a/include/range_list.h b/include/range_list.h
index 942528c..65c4643 100644
--- a/include/range_list.h
+++ b/include/range_list.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2009 Timothy D. Morgan
+ * Copyright (C) 2008-2010 Timothy D. Morgan
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,7 +14,17 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: range_list.h 148 2009-02-22 23:22:59Z tim $
+ * $Id: range_list.h 169 2010-03-03 19:24:58Z tim $
+ */
+
+/**
+ * @file 
+ *
+ * A data structure which stores a list of address ranges.
+ * 
+ * range_lists support basic in-place modifications and maintain the address
+ * space in sorted order.  Looking up a range_list_element is implemented 
+ * through binary search.
  */
 
 #ifndef _RANGE_LIST_H
@@ -35,6 +45,7 @@ typedef struct _range_list_element
 } range_list_element;
 
 
+/** XXX: document this. */
 typedef struct _range_list
 {
   range_list_element** elements;
@@ -43,151 +54,127 @@ typedef struct _range_list
 } range_list;
 
 
-/* range_list_new(): 
- *  Allocates a new range_list.
+/** Allocates a new range_list.
  *
- * Returns: 
- *  A newly allocated range_list, or NULL if an error occurred.
+ * @return A newly allocated range_list, or NULL if an error occurred.
  */
 range_list* range_list_new();
 
 
-/* range_list_free(): 
- *  Frees the memory associated with a range_list, including the elements, but
- *  not any data parameters referenced by those elements.  If rl is NULL, does
- *  nothing.
+/** Frees the memory associated with a range_list, including the elements, but
+ *  not any data parameters referenced by those elements.  
+ *
+ * If rl is NULL, does nothing.
  *
- * Arguments:
- *  rl -- the range_list to be free()d.
+ * @param rl the range_list to be free()d.
  */
 void range_list_free(range_list* rl);
 
 
-/* range_list_size(): 
- *  Query the current number of elements on a range_list
+/** Query the current number of elements on a range_list
  *
- * Arguments:
- *  rl -- the range_list to query
+ * @param rl the range_list to query
  *
- * Returns:
- *  The number of elements currently in the list.
+ * @return The number of elements currently in the list.
  */
 uint32_t range_list_size(const range_list* rl);
 
 
-/* range_list_add():
- *  Adds an element to the range_list.  
- *  The new element must not overlap with others.
- *  NOTE: this is a slow operation.
- *
- * Arguments:
- *  rl     -- the range list to update
- *  offset -- the starting point for the range
- *  length -- the length of the range
- *  data   -- misc data associated with this range element
- * Returns:
- *  true on success, false on failure.
- *  Failures can occur due to memory limitations, max_size limitations,
- *  or if the submitted range overlaps with an existing element.  Other
- *  errors may also be possible.
+/** Adds an element to the range_list.  
+ *
+ * The new element must not overlap with others.
+ * NOTE: this is a slow operation.
+ *
+ * @param rl     the range list to update
+ * @param offset the starting point for the range
+ * @param length the length of the range
+ * @param data   misc data associated with this range element
+ *
+ * @return  true on success, false on failure.
+ *
+ * Failures can occur due to memory limitations, max_size limitations,
+ * or if the submitted range overlaps with an existing element.  Other
+ * errors may also be possible.
  */
 bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data);
 
 
-/* range_list_remove():
- *  Removes an element from the list.  The element data structure will be 
- *  freed, but the data property will not be.
+/** Removes an element from the list.  
  *
- * Arguments:
- *  rl     -- the range_list to modify
- *  index  -- the element index to remove
+ * The element data structure will be freed, but the data property will not be.
  *
- * Returns:
- *  true if the element was successfully removed, false otherwise.
+ * @param rl    the range_list to modify
+ * @param index the element index to remove
+ *
+ * @return true if the element was successfully removed, false otherwise.
  */
 bool range_list_remove(range_list* rl, uint32_t index);
 
 
-/* range_list_get():
- *  Retrieves the element for a given index.
+/** Retrieves the element for a given index.
  *
- * Arguments:
- *  rl    -- the range_list being queried.
- *  index -- the element index desired.
+ * @param rl    the range_list being queried.
+ * @param index the element index desired.
  * 
- * Returns:
- *  The element for a given index, or NULL if the element is not available.
+ * @return The element for a given index, or NULL if the element is not 
+ *         available.
  */
 const range_list_element* range_list_get(const range_list* rl, uint32_t index);
 
 
-/* range_list_find():
- *  Attempts to find the unique element whose range encompasses offset.
+/** Attempts to find the unique element whose range encompasses offset.
  *
- * Arguments:
- *  rl     -- the range_list being queried.
- *  offset -- the location for which an element is desired.
+ * @param rl     the range_list being queried.
+ * @param offset the location for which an element is desired.
  *
- * Returns:
- *  A matching element index or a negative value if none could be found.
+ * @return A matching element index or a negative value if none could be found.
  */
 int32_t range_list_find(const range_list* rl, uint32_t offset);
 
 
-/* range_list_find_data():
- *  Same as range_list_find(), but returns the data associated with an element.
+/** Same as range_list_find(), but returns the data associated with an element.
  *
- * Arguments:
- *  rl     -- the range_list being queried.
- *  offset -- the address to search for in the ranges
+ * @param rl     the range_list being queried.
+ * @param offset the address to search for in the ranges
  *
- * Returns:
- *  The data element of the matching element index or NULL if none could
- *  be found.
+ * @return The data element of the matching element index or NULL if none could
+ *         be found.
  *
- *  NOTE: May also return NULL if an element matched but if the data
+ *  NOTE: May also return NULL if an element matched but the data
  *        element was never set.
  */
 void* range_list_find_data(const range_list* rl, uint32_t offset);
 
 
-/* range_list_split_element():
- *  Splits an existing element into two elements in place.
- *
- *  The resulting list will contain an additional element whose offset 
- *  is the one provided and whose length extends to the end of the old element
- *  (the one identified by the index).  The original element's offset will 
- *  remain the same while it's length is shortened such that it is contiguous
- *  with the newly created element.  The newly created element will have an index 
- *  of one more than the current element.
+/**  Splits an existing element into two elements in place.
  *
- *  Both the original element and the newly created element will reference the 
- *  original element's data.
+ * The resulting list will contain an additional element whose offset 
+ * is the one provided and whose length extends to the end of the old element
+ * (the one identified by the index).  The original element's offset will 
+ * remain the same while it's length is shortened such that it is contiguous
+ * with the newly created element.  The newly created element will have an index 
+ * of one more than the current element.
  *
- * Arguments:
- *  rl     -- the range_list to modify
- *  index  -- the index of the element to be split
- *  offset -- the at which the element will be split
+ * Both the original element and the newly created element will reference the 
+ * original element's data.
  *
- * Returns:
- *  true if the element was successfully split, false otherwise.
- *   
+ * @param rl     the range_list to modify
+ * @param index  the index of the element to be split
+ * @param offset the at which the element will be split
  *
+ * @return true if the element was successfully split, false otherwise.
  */
 bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset);
 
 
-/* range_list_has_range():
- *  Determines whether or not a specified range exists contiguously within the
+/** Determines whether or not a specified range exists contiguously within the
  *  range_list.
  *
- * Arguments:
- *  rl     -- the range_list to search
- *  start  -- the offset at the beginning of the range
- *  length -- the length of the range
+ * @param rl     the range_list to search
+ * @param start  the offset at the beginning of the range
+ * @param length the length of the range
  *
- * Returns:
- *  true if the specified range exists and is complete, false otherwise.
+ * @return true if the specified range exists and is complete, false otherwise.
  */
 bool range_list_has_range(range_list* rl, uint32_t start, uint32_t length);
 
diff --git a/include/regfi.h b/include/regfi.h
index a381560..2f3ae08 100644
--- a/include/regfi.h
+++ b/include/regfi.h
@@ -1,10 +1,5 @@
 /*
- * Branched from Samba project Subversion repository, version #6903:
- *   http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/regfio.h?rev=6903&view=auto
- *
- * Windows NT (and later) registry parsing library
- *
- * Copyright (C) 2005-2009 Timothy D. Morgan
+ * Copyright (C) 2005-2010 Timothy D. Morgan
  * Copyright (C) 2005 Gerald (Jerry) Carter
  *
  * This program is free software; you can redistribute it and/or modify
@@ -20,14 +15,44 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: regfi.h 152 2009-06-02 20:00:38Z tim $
+ * $Id: regfi.h 172 2010-03-08 03:04:34Z tim $
+ */
+
+/**
+ * @file
+ * Windows NT (and later) read-only registry library
+ *
+ * This library is intended for use in digital forensics investigations, but 
+ * is likely useful in other applications.
+ *
+ * Branched from Samba project Subversion repository, version #6903:
+ *   http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/regfio.h?rev=6903&view=auto
+ *
+ * Since then, it has been heavily rewritten, simplified, and improved.
+ */
+
+/**
+ * @mainpage Home
+ *
+ * The regfi library is a read-only NT registry library which serves as the main
+ * engine behind the reglookup tool.  It is designed with digital forensic 
+ * analysis in mind, but it should also be useful in other tools which need to 
+ * efficiently traverse and query registry data structures.
+ *
+ * The library is broken down into four main parts, the 
+ * @ref regfiBase "Base Layer", which any code dependent on the library will 
+ * likely need to rely on, as well as three main functional layers: 
+ * @li @ref regfiIteratorLayer
+ * @li @ref regfiGlueLayer
+ * @li @ref regfiParseLayer
+ *
+ * Most users will find that a combination of the Base Layer and the Iterator Layer 
+ * will be sufficient for accessing registry hive files.  Those who are wiling
+ * to dive deep into registry data structures, for instance to recover deleted
+ * data structures or to research Windows registry behavior in detail, will 
+ * find the Parse Layer to be quite useful.
  */
 
-/************************************************************
- * Most of this information was obtained from 
- * http://www.wednesday.demon.co.uk/dosreg.html
- * Thanks Nigel!
- ***********************************************************/
 
 #ifndef _REGFI_H
 #define _REGFI_H
@@ -35,7 +60,6 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdbool.h>
-#include <stdarg.h>
 #include <string.h>
 #include <errno.h>
 #include <time.h>
@@ -43,10 +67,10 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
-#include <assert.h>
+#include <iconv.h>
 
+#include "byteorder.h"
 #include "talloc.h"
-#include "smb_deps.h"
 #include "winsec.h"
 #include "void_stack.h"
 #include "range_list.h"
@@ -59,6 +83,16 @@
 #define REGFI_MSG_WARN  0x0004
 #define REGFI_MSG_ERROR 0x0010
 
+typedef uint8_t REGFI_ENCODING;
+/* regfi library supported character encodings */
+#define REGFI_ENCODING_ASCII   0
+#define REGFI_ENCODING_UTF8    1
+#define REGFI_ENCODING_DEFAULT REGFI_ENCODING_ASCII
+/* UTF16LE is not supported for output */
+#define REGFI_ENCODING_UTF16LE 2
+
+#define REGFI_NUM_ENCODINGS    3
+
 /* Windows is lame */
 #ifdef O_BINARY
 #define REGFI_OPEN_FLAGS O_RDONLY|O_BINARY
@@ -84,12 +118,23 @@
 /* Not a real type in the registry */
 #define REG_KEY                    0x7FFFFFFF
 
-#define REGFI_MAX_DEPTH		   512
 #define REGFI_OFFSET_NONE          0xffffffff
 
-/* XXX: This is totally arbitrary right now. */
+
+/* This maximum depth is described here:
+ * http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+ */
+#define REGFI_MAX_DEPTH		   512
+
+/* This limit defines the maximum number of levels deep that ri subkey list
+ * trees can go.
+ */
+/* XXX: This is totally arbitrary right now.
+ *      The actual limit may need to be discovered by experimentation.
+ */
 #define REGFI_MAX_SUBKEY_DEPTH     255
 
+
 /* Header sizes and magic number lengths for various records */
 #define REGFI_HBIN_ALLOC           0x1000 /* Minimum allocation unit for HBINs */
 #define REGFI_REGF_SIZE            0x1000 /* "regf" header block size */
@@ -104,6 +149,7 @@
 #define REGFI_VK_MIN_LENGTH        0x14
 #define REGFI_SK_MIN_LENGTH        0x14
 #define REGFI_SUBKEY_LIST_MIN_LEN  0x4
+#define REGFI_BIG_DATA_MIN_LENGTH  0xC
 
 
 /* Constants used for validation */
@@ -124,11 +170,7 @@
 
 
 /* Flags for the vk records */
-/* XXX: This next flag may be incorrect.  According to Jeffrey Muir,
-*       this may actually indicate that the value name is stored in
-*       UTF-16LE.
-*/
-#define REGFI_VK_FLAG_NAME_PRESENT 0x0001
+#define REGFI_VK_FLAG_ASCIINAME    0x0001
 #define REGFI_VK_DATA_IN_OFFSET    0x80000000
 #define REGFI_VK_MAX_DATA_LENGTH   1024*1024  /* XXX: This is arbitrary */
 
@@ -139,7 +181,13 @@
 #define REGFI_NK_FLAG_UNKNOWN1     0x4000
 #define REGFI_NK_FLAG_UNKNOWN2     0x1000
 
-/* This next one shows up on root keys in some Vista "software" registries */
+/* This next one shows up in some Vista "software" registries */
+/* XXX: This shows up in the following two SOFTWARE keys in Vista:
+ *   /Wow6432Node/Microsoft
+ *   /Wow6432Node/Microsoft/Cryptography
+ *  
+ * It comes along with UNKNOWN2 and ASCIINAME for a total flags value of 0x10A0
+ */
 #define REGFI_NK_FLAG_UNKNOWN3     0x0080
 
 /* Predefined handle.  Rumor has it that the valuelist count for this key is 
@@ -181,25 +229,48 @@
 				    | REGFI_NK_FLAG_HIVE_LINK\
 				    | REGFI_NK_FLAG_VOLATILE\
 				    | REGFI_NK_FLAG_UNKNOWN1\
-				    | REGFI_NK_FLAG_UNKNOWN2)
+				    | REGFI_NK_FLAG_UNKNOWN2\
+                                    | REGFI_NK_FLAG_UNKNOWN3)
+
+
+#define CHAR_BIT 8
+#define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
+		    : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
+#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
+#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
+
+typedef struct _regfi_nttime
+{
+  uint32_t low;
+  uint32_t high;
+} REGFI_NTTIME;
 
-/* HBIN block */
+
+/** HBIN block information
+ * @ingroup regfiMiddleLayer
+ */
 typedef struct _regfi_hbin 
 {
-  uint32 file_off;       /* my offset in the registry file */
-  uint32 ref_count;      /* how many active records are pointing to this
-                          * block (not used currently) 
-			  */
-  
-  uint32 first_hbin_off; /* offset from first hbin block */
-  uint32 block_size;     /* block size of this block 
-                          * Should be a multiple of 4096 (0x1000)
-			  */
-  uint32 next_block;     /* relative offset to next block.  
-			  * NOTE: This value may be unreliable!
-			  */
-
-  uint8 magic[REGFI_HBIN_MAGIC_SIZE]; /* "hbin" */
+  /** Offset of this HBIN in the registry file */
+  uint32_t file_off;
+
+  /** Number of active records pointing to this block (not used currently) */
+  uint32_t ref_count;
+
+  /** Offset from first hbin block */
+  uint32_t first_hbin_off;
+
+  /** Block size of this block Should be a multiple of 4096 (0x1000) */
+  uint32_t block_size;
+
+  /** Relative offset to next block.  
+   * 
+   * @note This value may be unreliable!
+   */
+  uint32_t next_block;
+
+  /** Magic number for the HBIN (should be "hbin"). */
+  uint8_t magic[REGFI_HBIN_MAGIC_SIZE];
 } REGFI_HBIN;
 
 
@@ -209,132 +280,390 @@ typedef struct
   /* Virtual offset of NK record or additional subkey list, 
    * depending on this list's type. 
    */
-  uint32 offset;
+  uint32_t offset;
 
-  uint32 hash;
+  uint32_t hash;
 } REGFI_SUBKEY_LIST_ELEM;
 
 
+/** Subkey-list structure
+ * @ingroup regfiMiddleLayer
+ */
 typedef struct _regfi_subkey_list
 {
   /* Real offset of this record's cell in the file */
-  uint32 offset;
+  uint32_t offset;
 
-  uint32 cell_size;
+  uint32_t cell_size;
   
   /* Number of immediate children */
-  uint32 num_children;  
+  uint32_t num_children;  
 
   /* Total number of keys referenced by this list and it's children */
-  uint32 num_keys;      
+  uint32_t num_keys;      
 
   REGFI_SUBKEY_LIST_ELEM* elements;
-  uint8 magic[REGFI_CELL_MAGIC_SIZE];
+  uint8_t magic[REGFI_CELL_MAGIC_SIZE];
 
   /* Set if the magic indicates this subkey list points to child subkey lists */
   bool recursive_type;  
 } REGFI_SUBKEY_LIST;
 
 
-typedef uint32 REGFI_VALUE_LIST_ELEM;
+typedef uint32_t REGFI_VALUE_LIST_ELEM;
+/** Value-list structure
+ * @ingroup regfiMiddleLayer
+ */
 typedef struct _regfi_value_list
 {
   /* Actual number of values referenced by this list.  
    * May differ from parent key's num_values if there were parsing errors. 
    */
-  uint32 num_values;
+  uint32_t num_values;
 
   REGFI_VALUE_LIST_ELEM* elements;
 } REGFI_VALUE_LIST;
 
 
-/* Value record */
-typedef struct 
+/** Class name structure (used in storing SysKeys)
+ * @ingroup regfiBase
+ */
+typedef struct _regfi_classname
 {
-  uint32 offset;	/* Real offset of this record's cell in the file */
-  uint32 cell_size;	/* ((start_offset - end_offset) & 0xfffffff8) */
+  /** As converted to requested REGFI_ENCODING */
+  char* interpreted;
+
+  /** Represents raw buffer read from classname cell.
+   *
+   * Length of this item is specified in the size field.
+   */
+  uint8_t* raw;
+
+  /** Length of the raw data.
+   *
+   * May be shorter than that indicated by parent key.
+   */
+  uint16_t size;
+} REGFI_CLASSNAME;
+
 
-  uint8* data;
-  char*  valuename;
-  uint16 name_length;
-  uint32 hbin_off;	/* offset from beginning of this hbin block */
+/** Data record structure
+ * @ingroup regfiBase
+ */
+typedef struct _regfi_data
+{
+  /** Data type of this data, as indicated by the referencing VK record. */
+  uint32_t type;
+
+  /** Length of the raw data. */
+  uint32_t size;
+
+  /** This is always present, representing the raw data cell contents. */
+  uint8_t* raw;
+
+  /** Represents the length of the interpreted value. Meaning is type-specific. */
+  uint32_t interpreted_size;
+
+  /** These items represent interpreted versions of the REGFI_DATA::raw field.
+   *
+   * Only use the appropriate member according to the REGFI_DATA::type field.
+   * In the event of an unknown type, use only the REGFI_DATA::raw field.
+   */
+  union _regfi_data_interpreted
+  {
+    /** REG_NONE 
+     *
+     * Stored as a raw buffer.  Use REGFI_DATA::interpreted_size to determine
+     * length.
+     */
+    uint8_t* none; 
+
+    /** REG_SZ 
+     *
+     * Stored as a NUL terminated string.  Converted to the specified
+     * REGFI_ENCODING.
+     */
+    uint8_t* string;
+
+    /** REG_EXPAND_SZ 
+     *
+     * Stored as a NUL terminated string.  Converted to the specified
+     * REGFI_ENCODING.
+     */
+    uint8_t* expand_string;
+
+    /** REG_BINARY
+     *
+     * Stored as a raw buffer.  Use REGFI_DATA::interpreted_size to determine
+     * length.
+     */
+    uint8_t* binary;
+
+    /** REG_DWORD */
+    uint32_t dword;
+
+    /** REG_DWORD_BE */
+    uint32_t dword_be;
+
+    /** REG_LINK
+     *
+     * Stored as a NUL terminated string.  Converted to the specified
+     * REGFI_ENCODING.
+     */
+    uint8_t* link;
+
+    /** REG_MULTI_SZ 
+     *
+     * Stored as a list of uint8_t* pointers, terminated with a NULL pointer.
+     * Each string element in the list is NUL terminated, and the character set
+     * is determined by the specified REGFI_ENCODING.
+     */
+    uint8_t** multiple_string;
+
+    /** REG_QWORD */
+    uint64_t qword;
+
+    /* The following are treated as binary currently, but this may change in
+     * the future as the formats become better understood.
+     */
+
+    /** REG_RESOURCE_LIST
+     *
+     * Stored as a raw buffer.  Use REGFI_DATA::interpreted_size to determine
+     * length.
+     */
+    uint8_t* resource_list;
+
+    /** REG_FULL_RESOURCE_DESCRIPTOR
+     *
+     * Stored as a raw buffer.  Use REGFI_DATA::interpreted_size to determine
+     * length.
+     */
+    uint8_t* full_resource_descriptor;
+
+    /** REG_RESOURCE_REQUIREMENTS_LIST
+     *
+     * Stored as a raw buffer.  Use REGFI_DATA::interpreted_size to determine
+     * length.
+     */
+    uint8_t* resource_requirements_list;
+  } interpreted;
+} REGFI_DATA;
+
+
+/** Value structure
+ * @ingroup regfiBase
+ */
+typedef struct
+{
+  /** Real offset of this record's cell in the file */
+  uint32_t offset;	
+
+  /** ((start_offset - end_offset) & 0xfffffff8) */
+  uint32_t cell_size;
+
+  /* XXX: deprecated */
+  REGFI_DATA* data;
+
+  /** The name of this value converted to desired REGFI_ENCODING.  
+   *
+   * This conversion typically occurs automatically through REGFI_ITERATOR
+   * settings.  String is NUL terminated.
+   */
+  char*    valuename;
+
+  /** The raw value name
+   *
+   * Length of the buffer is stored in name_length.
+   */
+  uint8_t* valuename_raw;
+
+  /** Length of valuename_raw */
+  uint16_t name_length;
+
+  /** Offset from beginning of this hbin block */
+  uint32_t hbin_off;
   
-  uint32 data_size;
-  uint32 data_off;      /* offset of data cell (virtual) */
-  uint32 type;
-  uint8  magic[REGFI_CELL_MAGIC_SIZE];
-  uint16 flag;
-  uint16 unknown1;
-  bool data_in_offset;
+  /** Size of the value's data as reported in the VK record.
+   *
+   * May be different than that obtained while parsing the data cell itself.
+   */
+  uint32_t data_size;
+
+  /** Virtual offset of data cell */
+  uint32_t data_off;
+
+  /** Value's data type */
+  uint32_t type;
+
+  /** VK record's magic number (should be "vk") */
+  uint8_t  magic[REGFI_CELL_MAGIC_SIZE];
+
+  /** VK record flags */
+  uint16_t flags;
+
+  /* XXX: A 2-byte field of unknown purpose stored in the VK record */
+  uint16_t unknown1;
+
+  /** Whether or not the data record is stored in the VK record's data_off field. 
+   *
+   * This information is derived from the high bit of the raw data size field.
+   */
+  bool     data_in_offset;
 } REGFI_VK_REC;
 
 
 /* Key Security */
 struct _regfi_sk_rec;
 
+/** Security structure
+ * @ingroup regfiBase
+ */
 typedef struct _regfi_sk_rec 
 {
-  uint32 offset;        /* Real file offset of this record */
-  uint32 cell_size;	/* ((start_offset - end_offset) & 0xfffffff8) */
+  /** Real file offset of this record */
+  uint32_t offset;
 
+  /** ((start_offset - end_offset) & 0xfffffff8) */
+  uint32_t cell_size;
+
+  /** The stored Windows security descriptor for this SK record */
   WINSEC_DESC* sec_desc;
-  uint32 hbin_off;	/* offset from beginning of this hbin block */
+
+  /** Offset of this record from beginning of this hbin block */
+  uint32_t hbin_off;
   
-  uint32 prev_sk_off;
-  uint32 next_sk_off;
-  uint32 ref_count;
-  uint32 desc_size;     /* size of security descriptor */
-  uint16 unknown_tag;
-  uint8  magic[REGFI_CELL_MAGIC_SIZE];
+  /** Offset of the previous SK record in the linked list of SK records */
+  uint32_t prev_sk_off;
+
+  /** Offset of the next SK record in the linked list of SK records */
+  uint32_t next_sk_off;
+
+  /** Number of keys referencing this SK record */
+  uint32_t ref_count;
+
+  /** Size of security descriptor (sec_desc) */
+  uint32_t desc_size;
+
+  /* XXX: A 2-byte field of unknown purpose */
+  uint16_t unknown_tag;
+
+  /** The magic number for this record (should be "sk") */
+  uint8_t  magic[REGFI_CELL_MAGIC_SIZE];
 } REGFI_SK_REC;
 
 
-/* Key Name */
+/** Key structure
+ * @ingroup regfiBase
+ */
 typedef struct
 {
-  uint32 offset;	/* Real offset of this record's cell in the file */
-  uint32 cell_size;	/* Actual or estimated length of the cell.  
-			 * Always in multiples of 8. 
-			 */
+  /** Real offset of this record's cell in the file */
+  uint32_t offset;
+
+  /** Actual or estimated length of the cell.  
+   * Always in multiples of 8. 
+   */
+  uint32_t cell_size;
 
-  /* link in the other records here */
+  /** Preloaded value-list for this key.
+   * This element is loaded automatically when using the iterator interface and
+   * possibly some lower layer interfaces.
+   */
   REGFI_VALUE_LIST* values;
+
+
+  /** Preloaded subkey-list for this key.
+   * This element is loaded automatically when using the iterator interface and
+   * possibly some lower layer interfaces.
+   */
   REGFI_SUBKEY_LIST* subkeys;
   
-  /* header information */
-  uint16 key_type;
-  uint8  magic[REGFI_CELL_MAGIC_SIZE];
-  NTTIME mtime;
-  uint16 name_length;
-  uint16 classname_length;
-  char* classname;
+  /** Key flags */
+  uint16_t flags;
+
+  /** Magic number of key (should be "nk") */
+  uint8_t  magic[REGFI_CELL_MAGIC_SIZE];
+
+  /** Key's last modification time */
+  REGFI_NTTIME mtime;
+
+  /** Length of keyname_raw */
+  uint16_t name_length;
+
+  /** Length of referenced classname */
+  uint16_t classname_length;
+
+  /** The name of this key converted to desired REGFI_ENCODING.  
+   *
+   * This conversion typically occurs automatically through REGFI_ITERATOR
+   * settings.  String is NUL terminated.
+   */
   char* keyname;
-  uint32 parent_off;	            /* pointer to parent key */
-  uint32 classname_off;
+
+  /** The raw key name
+   *
+   * Length of the buffer is stored in name_length.
+   */
+  uint8_t* keyname_raw;
+
+  /** Virutal offset of parent key */
+  uint32_t parent_off;
+
+  /** Virutal offset of classname key */
+  uint32_t classname_off;
   
-  /* max lengths */
-  uint32 max_bytes_subkeyname;	    /* max subkey name * 2 */
-  uint32 max_bytes_subkeyclassname; /* max subkey classname length (as if) */
-  uint32 max_bytes_valuename;	    /* max valuename * 2 */
-  uint32 max_bytes_value;           /* max value data size */
+  /* XXX: max subkey name * 2 */
+  uint32_t max_bytes_subkeyname;
+
+  /* XXX: max subkey classname length (as if) */
+  uint32_t max_bytes_subkeyclassname;
+
+  /* XXX: max valuename * 2 */
+  uint32_t max_bytes_valuename;
+
+  /* XXX: max value data size */
+  uint32_t max_bytes_value;
   
-  /* unknowns */
-  uint32 unknown1;
-  uint32 unknown2;
-  uint32 unknown3;
-  uint32 unk_index;		    /* nigel says run time index ? */
+  /* XXX: Fields of unknown purpose */
+  uint32_t unknown1;
+  uint32_t unknown2;
+  uint32_t unknown3;
+  uint32_t unk_index;		    /* nigel says run time index ? */
   
-  /* children */
-  uint32 num_subkeys;
-  uint32 subkeys_off;	/* offset of subkey list that points to NK records */
-  uint32 num_values;
-  uint32 values_off;	/* value lists which point to VK records */
-  uint32 sk_off;	/* offset to SK record */
+  /** Number of subkeys */
+  uint32_t num_subkeys;
+
+  /** Virtual offset of subkey-list */
+  uint32_t subkeys_off;
+
+  /** Number of values for this key */
+  uint32_t num_values;
+
+  /** Virtual offset of value-list */
+  uint32_t values_off;
+
+  /** Virtual offset of SK record */
+  uint32_t sk_off;
 } REGFI_NK_REC;
 
 
 
-/* REGF block */
+/** Registry hive file data structure
+ *
+ * This essential structure stores run-time information about a single open
+ * registry hive as well as file header (REGF block) data.  This structure 
+ * also stores a list of warnings and error messages generated while parsing
+ * the registry hive.  These can be tuned using @ref regfi_set_message_mask.  
+ * Messages may be retrieved using @ref regfi_get_messages.
+ *
+ * @note If the message mask is set to record any messages, dependent code 
+ *       must use @ref regfi_get_messages periodically to clear the message
+ *       queue. Otherwise, this structure will grow in size over time as 
+ *       messages queue up.
+ *
+ * @ingroup regfiBase
+ */ 
 typedef struct 
 {
   /* Run-time information */
@@ -343,7 +672,7 @@ typedef struct
   int fd;
 
   /* For sanity checking (not part of the registry header) */
-  uint32 file_length;
+  uint32_t file_length;
 
   /* Metadata about hbins */
   range_list* hbins;
@@ -355,219 +684,732 @@ typedef struct
   char* last_message;
 
   /* Mask for error message types that will be stored. */
-  uint16 msg_mask;
+  uint16_t msg_mask;
 
 
   /* Data parsed from file header */
   /********************************/
-  uint8  magic[REGFI_REGF_MAGIC_SIZE];/* "regf" */
+  uint8_t  magic[REGFI_REGF_MAGIC_SIZE];/* "regf" */
 
  /* These sequence numbers should match if
   * the hive was properly synced to disk.
   */
-  uint32 sequence1;            
-  uint32 sequence2;
+  uint32_t sequence1;            
+  uint32_t sequence2;
 
-  NTTIME mtime;
-  uint32 major_version;  /* Set to 1 in all known hives */
-  uint32 minor_version;  /* Set to 3 or 5 in all known hives */
-  uint32 type;           /* XXX: Unverified.  Set to 0 in all known hives */
-  uint32 format;         /* XXX: Unverified.  Set to 1 in all known hives */
+  REGFI_NTTIME mtime;
+  uint32_t major_version;  /* Set to 1 in all known hives */
+  uint32_t minor_version;  /* Set to 3 or 5 in all known hives */
+  uint32_t type;           /* XXX: Unverified.  Set to 0 in all known hives */
+  uint32_t format;         /* XXX: Unverified.  Set to 1 in all known hives */
 
-  uint32 root_cell;  /* Offset to root cell in the first (or any?) hbin block */
-  uint32 last_block; /* Offset to last hbin block in file */
+  uint32_t root_cell;  /* Offset to root cell in the first (or any?) hbin block */
+  uint32_t last_block; /* Offset to last hbin block in file */
 
-  uint32 cluster;    /* XXX: Unverified. Set to 1 in all known hives */
+  uint32_t cluster;    /* XXX: Unverified. Set to 1 in all known hives */
 
   /* Matches hive's base file name. Stored in UTF-16LE */
-  uint8 file_name[REGFI_REGF_NAME_SIZE];
+  uint8_t file_name[REGFI_REGF_NAME_SIZE];
 
   WINSEC_UUID* rm_id;       /* XXX: Unverified. */
   WINSEC_UUID* log_id;      /* XXX: Unverified. */
   WINSEC_UUID* tm_id;       /* XXX: Unverified. */
-  uint32 flags;             /* XXX: Unverified. */
-  uint32 guid_signature;    /* XXX: Unverified. */
+  uint32_t flags;             /* XXX: Unverified. */
+  uint32_t guid_signature;    /* XXX: Unverified. */
 
-  uint32 checksum;          /* Stored checksum from file */
-  uint32 computed_checksum; /* Our own calculation of the checksum. 
+  uint32_t checksum;          /* Stored checksum from file */
+  uint32_t computed_checksum; /* Our own calculation of the checksum. 
 			     * (XOR of bytes 0x0000 - 0x01FB) */
 
   WINSEC_UUID* thaw_tm_id;  /* XXX: Unverified. */
   WINSEC_UUID* thaw_rm_id;  /* XXX: Unverified. */
   WINSEC_UUID* thaw_log_id; /* XXX: Unverified. */
-  uint32 boot_type;         /* XXX: Unverified. */
-  uint32 boot_recover;      /* XXX: Unverified. */
+  uint32_t boot_type;         /* XXX: Unverified. */
+  uint32_t boot_recover;      /* XXX: Unverified. */
 
   /* This seems to include random junk.  Possibly unsanitized memory left over
    * from when header block was written.  For instance, chunks of nk records 
    * can be found, though often it's all 0s. */
-  uint8 reserved1[REGFI_REGF_RESERVED1_SIZE];
+  uint8_t reserved1[REGFI_REGF_RESERVED1_SIZE];
 
   /* This is likely reserved and unusued currently.  (Should be all 0s.)
    * Included here for easier access in looking for hidden data 
    * or doing research. */
-  uint8 reserved2[REGFI_REGF_RESERVED2_SIZE];
+  uint8_t reserved2[REGFI_REGF_RESERVED2_SIZE];
 
 } REGFI_FILE;
 
 
-/* XXX: Should move all caching (SK records, HBINs, NKs, etc) to a single
- *      structure, probably REGFI_FILE.  Once key caching is in place, 
- *      convert key_positions stack to store just key offsets rather than
- *      whole keys.
+/** Registry hive iterator
+ * @ingroup regfiIteratorLayer
  */
 typedef struct _regfi_iterator
 {
+  /** The registry hive this iterator is associated with */
   REGFI_FILE* f;
+
+  /** All current parent keys and associated iterator positions */
   void_stack* key_positions;
+
+  /** The current key */
   REGFI_NK_REC* cur_key;
-  uint32 cur_subkey;
-  uint32 cur_value;
+
+  /** The encoding that all strings are converted to as set during iterator
+   *  creation.
+   */
+  REGFI_ENCODING string_encoding;
+
+  /** Index of the current subkey */
+  uint32_t cur_subkey;
+
+  /** Index of the current value */
+  uint32_t cur_value;
 } REGFI_ITERATOR;
 
 
 typedef struct _regfi_iter_position
 {
   REGFI_NK_REC* nk;
-  uint32 cur_subkey;
+  uint32_t cur_subkey;
   /* We could store a cur_value here as well, but didn't see 
    * the use in it right now.
    */
 } REGFI_ITER_POSITION;
 
 
+/** General purpose buffer with stored length
+ * @ingroup regfiBottomLayer
+ */
 typedef struct _regfi_buffer
 {
-  uint8* buf;
+  uint8_t* buf;
   uint32_t len;
 } REGFI_BUFFER;
 
 
+
 /******************************************************************************/
-/*                         Main iterator API                                  */
+/** 
+ * @defgroup regfiBase Base Layer: Essential Functions and Data Structures
+ *
+ * These functions are either necessary for normal use of the regfi API or just
+ * don't fit particularly well in any of the other layers.
+ */
 /******************************************************************************/
+
+/** Attempts to open a registry hive and allocate related data structures.
+ * 
+ * @param filename A string containing the relative or absolute path of the
+ *               registry hive to be opened.
+ *
+ * @return A reference to a newly allocated REGFI_FILE structure, 
+ *         if successful;  NULL on error.
+ *
+ * @ingroup regfiBase
+ */
 REGFI_FILE*           regfi_open(const char* filename);
-int                   regfi_close(REGFI_FILE* r);
 
-/* regfi_get_messages: Get errors, warnings, and/or verbose information
- *                     relating to processing of the given registry file.
+
+/** Parses file headers of an already open registry hive file and 
+ *  allocates related structures for further parsing.
+ *
+ * @param fd A file descriptor of an already open file.  Must be seekable.
+ *
+ * @return A reference to a newly allocated REGFI_FILE structure, if successful;
+ *         NULL on error.
+ *
+ * @ingroup regfiBase
+ */
+REGFI_FILE*           regfi_alloc(int fd);
+
+
+/** Closes and frees an open registry hive.
+ *
+ * @param file The registry structure to close.
+ *
+ * @return 0 on success, -1 on failure with errno set.  
+ *         errno codes are similar to those of close(2).
+ *
+ * @ingroup regfiBase
+ */
+int                   regfi_close(REGFI_FILE* file);
+
+
+/** Frees a hive's data structures without closing the underlying file.
+ *
+ * @param file The registry structure to free.
+ *
+ * @ingroup regfiBase
+ */
+void                  regfi_free(REGFI_FILE* file);
+
+
+/** Get errors, warnings, and/or verbose information relating to processing of
+ *  the given registry file.
  *
- * Arguments:
- *   file     -- the structure for the registry file
+ * @param file the structure for the registry file
  *
- * Returns:
- *   A newly allocated char* which must be free()d by the caller.
+ * @return A newly allocated char* which must be free()d by the caller.
+ *
+ * @ingroup regfiBase
  */
 char*                 regfi_get_messages(REGFI_FILE* file);
-void                  regfi_set_message_mask(REGFI_FILE* file, uint16 mask);
 
-REGFI_ITERATOR*       regfi_iterator_new(REGFI_FILE* fh);
+
+/** Set the verbosity level of errors and warnings generated by the library
+ *  (as accessible via regfi_get_messages).
+ *
+ * This may be called at any time and will take effect immediately.
+ *
+ * @param file   the structure for the registry file
+ *
+ * @param mask   an integer representing the types of messages desired.
+ *               Acceptable values are created through bitwise ORs of 
+ *               REGFI_MSG_* values.  For instance, if only errors and
+ *               informational messages were desired (but not warnings),
+ *               then one would specify: REGFI_MSG_ERROR|REGFI_MSG_INFO
+ *               New REGFI_FILE structures are created with:
+ *                REGFI_MSG_ERROR|REGFI_MSG_WARN
+ *               Note that error and warning messages will continue to
+ *               accumulate in memory if they are not fetched using
+ *               regfi_get_messages and then freed by the caller.
+ *               To disable error messages entirely, supply 0, which
+ *               will prevent message accumulation.  
+ *
+ * @ingroup regfiBase
+ */
+void                  regfi_set_message_mask(REGFI_FILE* file, uint16_t mask);
+
+
+/* Dispose of previously parsed records */
+
+/** Frees a key structure previously returned by one of the API functions
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiBase
+ */
+void                  regfi_free_key(REGFI_NK_REC* nk);
+
+
+/** Frees a value structure previously returned by one of the API functions
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiBase
+ */
+void                  regfi_free_value(REGFI_VK_REC* vk);
+
+
+
+/******************************************************************************/
+/** 
+ * @defgroup regfiIteratorLayer Iterator Layer: Primary regfi Library Interface
+ *
+ * This top layer of API functions provides an iterator interface which makes
+ * traversing registry data structures easy in both single-threaded and 
+ * multi-threaded scenarios.
+ */
+/******************************************************************************/
+
+/** Creates a new iterator for the provided registry file.
+ *
+ * @param file The opened registry file the iterator should be created for.
+ *
+ * @param output_encoding Character encoding that strings should be returned in.
+ *                        Only supply the REGFI_ENCODING_* constants, as others 
+ *                        will be rejected.
+ *                        The following values are currently accepted:
+ *                        REGFI_ENCODING_DEFAULT (currently REGFI_ENCODING_ASCII)
+ *                        REGFI_ENCODING_ASCII
+ *                        REGFI_ENCODING_UTF8
+ *
+ * @return A newly allocated REGFI_ITERATOR. 
+ *         Must be free()d with regfi_iterator_free.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+REGFI_ITERATOR*       regfi_iterator_new(REGFI_FILE* file,
+					 REGFI_ENCODING output_encoding);
+
+
+/** Frees a registry file iterator previously created by regfi_iterator_new.
+ *
+ * This does not affect the underlying registry file's allocation status.
+ *
+ * @param i the iterator to be freed
+ *
+ * @ingroup regfiIteratorLayer
+ */
 void                  regfi_iterator_free(REGFI_ITERATOR* i);
+
+
+/** Traverse deeper into the registry tree at the current subkey.
+ *
+ * @param i the iterator
+ *
+ * @return  true on success, false on failure.  
+ *          Note that subkey and value indexes are preserved.  That is, if a
+ *          regfi_iterator_up call occurs later (reversing the effect of this
+ *          call) then the subkey and value referenced prior to the
+ *          regfi_iterator_down call will still be referenced.  This  makes
+ *          depth-first iteration particularly easy.
+ *
+ * @ingroup regfiIteratorLayer
+ */
 bool                  regfi_iterator_down(REGFI_ITERATOR* i);
+
+
+/** Traverse up to the current key's parent key.
+ *
+ * @param i the iterator
+ *
+ * @return  true on success, false on failure.  Any subkey or value state
+ *          associated with the current key is lost.
+ *
+ * @ingroup regfiIteratorLayer
+ */
 bool                  regfi_iterator_up(REGFI_ITERATOR* i);
+
+
+/** Traverse up to the root key of the hive.
+ *
+ * @param i the iterator
+ *
+ * @return true on success, false on failure.
+ *
+ * @ingroup regfiIteratorLayer
+ */
 bool                  regfi_iterator_to_root(REGFI_ITERATOR* i);
 
-bool                  regfi_iterator_find_subkey(REGFI_ITERATOR* i, 
-						 const char* subkey_name);
-bool                  regfi_iterator_walk_path(REGFI_ITERATOR* i, 
-					       const char** path);
+
+/** Traverse down multiple levels in the registry hive.
+ *
+ * XXX: This currently only accepts ASCII key names.  Need to look into
+ *      accepting other encodings.
+ *
+ * @param i    the iterator
+ * @param path a list of key names representing the path.  This list must
+ *             contain NUL terminated strings.  The list itself is
+ *             terminated with a NULL pointer.  All path elements must be
+ *             keys; value names are not accepted (even as the last
+ *             element).
+ *
+ * @return true on success, false on failure.  If any element of path is not
+ *                 found, false will be returned and the iterator will remain
+ *                 in its original position.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path);
+
+
+/** Returns the currently referenced key.
+ *
+ * @param i the iterator
+ *
+ * @return A read-only key structure for the current key, or NULL on failure.
+ *
+ * @ingroup regfiIteratorLayer
+ */
 const REGFI_NK_REC*   regfi_iterator_cur_key(REGFI_ITERATOR* i);
+
+
+/** Returns the SK (security) record referenced by the current key.
+ *
+ * @param i the iterator
+ *
+ * @return A read-only SK structure, or NULL on failure.
+ *
+ * @ingroup regfiIteratorLayer
+ */
 const REGFI_SK_REC*   regfi_iterator_cur_sk(REGFI_ITERATOR* i);
 
+
+/** Sets the internal subkey index to the first subkey referenced by the current
+ *  key and returns that key.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated key structure for the newly referenced first 
+ *         subkey, or NULL on failure.  Failure may be due to a lack of any
+ *         subkeys or other errors.  Newly allocated keys must be freed with
+ *         regfi_free_key.
+ *
+ * @ingroup regfiIteratorLayer
+ */
 REGFI_NK_REC*         regfi_iterator_first_subkey(REGFI_ITERATOR* i);
+
+
+/** Returns the currently indexed subkey.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated key structure for the currently referenced subkey,
+ *         or NULL on failure.  Newly allocated keys must be freed with 
+ *         regfi_free_key.
+ *
+ * @ingroup regfiIteratorLayer
+ */
 REGFI_NK_REC*         regfi_iterator_cur_subkey(REGFI_ITERATOR* i);
+
+
+/** Increments the internal subkey index to the next key in the subkey-list and
+ *  returns the subkey for that index.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated key structure for the next subkey or NULL on
+ *         failure.  Newly allocated keys must be freed with regfi_free_key.
+ *
+ * @ingroup regfiIteratorLayer
+ */
 REGFI_NK_REC*         regfi_iterator_next_subkey(REGFI_ITERATOR* i);
 
-bool                  regfi_iterator_find_value(REGFI_ITERATOR* i, 
-						const char* value_name);
+
+/** Searches for a subkey with a given name under the current key.
+ *
+ * @param i           the iterator
+ * @param subkey_name subkey name to search for
+ *
+ * @return True if such a subkey was found, false otherwise.  If a subkey is
+ *         found, the current subkey index is set to that subkey.  Otherwise,
+ *         the subkey index remains at the same location as before the call.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+bool                  regfi_iterator_find_subkey(REGFI_ITERATOR* i, 
+						 const char* subkey_name);
+
+/** Sets the internal value index to the first value referenced by the current
+ *  key and returns that value.
+ *
+ * @param i the iterator
+ *
+ * @return  A newly allocated value structure for the newly referenced first
+ *          value, or NULL on failure.  Failure may be due to a lack of any
+ *          values or other errors.  Newly allocated keys must be freed with
+ *          regfi_free_value.
+ *
+ * @ingroup regfiIteratorLayer
+ */
 REGFI_VK_REC*         regfi_iterator_first_value(REGFI_ITERATOR* i);
+
+
+/** Returns the currently indexed value.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated value structure for the currently referenced value,
+ *         or NULL on failure.  Newly allocated values must be freed with 
+ *         regfi_free_value.
+ *
+ * @ingroup regfiIteratorLayer
+ */
 REGFI_VK_REC*         regfi_iterator_cur_value(REGFI_ITERATOR* i);
+
+
+/** Increments the internal value index to the next value in the value-list and
+ *  returns the value for that index.
+ *
+ * @param i the iterator
+ *
+ * @return  A newly allocated key structure for the next value or NULL on 
+ *          failure.  Newly allocated keys must be freed with regfi_free_value.
+ *
+ * @ingroup regfiIteratorLayer
+ */
 REGFI_VK_REC*         regfi_iterator_next_value(REGFI_ITERATOR* i);
 
 
-/********************************************************/
-/* Middle-layer structure loading, linking, and caching */
-/********************************************************/
-REGFI_NK_REC*         regfi_load_key(REGFI_FILE* file, uint32 offset, 
+/** Searches for a value with a given name under the current key.
+ *
+ * @param i          the iterator
+ * @param value_name value name to search for
+ *
+ * @return True if such a value was found, false otherwise.  If a value is 
+ *         found, the current value index is set to that value.  Otherwise,
+ *         the value index remains at the same location as before the call.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+bool                  regfi_iterator_find_value(REGFI_ITERATOR* i, 
+						const char* value_name);
+
+/** Retrieves classname for a given key.
+ *
+ * @param i   the iterator
+ * @param key the key whose classname is desired
+ *
+ * @return Returns a newly allocated classname structure, or NULL on failure.
+ *         Classname structures must be freed with regfi_free_classname.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+REGFI_CLASSNAME*      regfi_iterator_fetch_classname(REGFI_ITERATOR* i, 
+						     const REGFI_NK_REC* key);
+
+
+/** Retrieves data for a given value.
+ *
+ * @param i     the iterator
+ * @param value the value whose data is desired
+ *
+ * @return Returns a newly allocated data structure, or NULL on failure.
+ *         Data structures must be freed with regfi_free_data.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+REGFI_DATA*           regfi_iterator_fetch_data(REGFI_ITERATOR* i, 
+						const REGFI_VK_REC* value);
+
+
+
+/******************************************************************************/
+/** 
+ * @defgroup regfiGlueLayer Glue Layer: Logical Data Structure Loading 
+ */
+/******************************************************************************/
+
+/** Loads a key at a given file offset along with associated data structures.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_NK_REC*         regfi_load_key(REGFI_FILE* file, uint32_t offset, 
+				     REGFI_ENCODING output_encoding, 
 				     bool strict);
-REGFI_VK_REC*         regfi_load_value(REGFI_FILE* file, uint32 offset, 
+
+
+/** Loads a value at a given file offset alng with associated data structures.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_VK_REC*         regfi_load_value(REGFI_FILE* file, uint32_t offset, 
+				       REGFI_ENCODING output_encoding, 
 				       bool strict);
-REGFI_SUBKEY_LIST*    regfi_load_subkeylist(REGFI_FILE* file, uint32 offset,
-					    uint32 num_keys, uint32 max_size,
+
+
+/** Loads a logical subkey list in its entirety which may span multiple records.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_SUBKEY_LIST*    regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset,
+					    uint32_t num_keys, uint32_t max_size,
 					    bool strict);
-REGFI_VALUE_LIST*     regfi_load_valuelist(REGFI_FILE* file, uint32 offset, 
-					   uint32 num_values, uint32 max_size,
+
+
+/** Loads a valuelist.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_VALUE_LIST*     regfi_load_valuelist(REGFI_FILE* file, uint32_t offset, 
+					   uint32_t num_values, uint32_t max_size,
 					   bool strict);
 
+
+/** Loads a data record which may be contained in the virtual offset, in a
+ *  single cell, or in multiple cells through big data records.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_BUFFER          regfi_load_data(REGFI_FILE* file, uint32_t voffset,
+				      uint32_t length, bool data_in_offset,
+				      bool strict);
+
+
+/** Loads the data associated with a big data record at the specified offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_BUFFER          regfi_load_big_data(REGFI_FILE* file, uint32_t offset, 
+					  uint32_t data_length,uint32_t cell_length,
+					  range_list* used_ranges,
+					  bool strict);
+
+
+/** Given raw data, attempts to interpret the data based on a specified registry
+ *  data type.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+bool                  regfi_interpret_data(REGFI_FILE* file, 
+					   REGFI_ENCODING string_encoding,
+					   uint32_t type, REGFI_DATA* data);
+
+
+/** Frees the memory associated with a REGFI_CLASSNAME data structure.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+void                  regfi_free_classname(REGFI_CLASSNAME* classname);
+
+
+/** Frees the memory associated with a REGFI_DATA data structure.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+void                  regfi_free_data(REGFI_DATA* data);
+
+
 /* These are cached so return values don't need to be freed. */
-const REGFI_SK_REC*   regfi_load_sk(REGFI_FILE* file, uint32 offset,
+
+/** Loads an "sk" security record at the specified offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+const REGFI_SK_REC*   regfi_load_sk(REGFI_FILE* file, uint32_t offset,
 				    bool strict);
-const REGFI_HBIN*     regfi_lookup_hbin(REGFI_FILE* file, uint32 voffset);
 
 
+/** Retrieves the HBIN data structure stored at the specified offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+const REGFI_HBIN*     regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset);
+
+
+
+/******************************************************************************/
+/**
+ * @defgroup regfiParseLayer Parsing Layer: Direct Data Structure Access 
+ */
+/******************************************************************************/
 
-/************************************/
-/*  Low-layer data structure access */
-/************************************/
 REGFI_FILE*           regfi_parse_regf(int fd, bool strict);
-REGFI_HBIN*           regfi_parse_hbin(REGFI_FILE* file, uint32 offset, 
+REGFI_HBIN*           regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, 
 				       bool strict);
 
 
-/* regfi_parse_nk: Parses an NK record.
+/** Parses an NK record at the specified offset
+ *
+ * @param file     the registry file structure
+ * @param offset   the offset of the cell (not the record) to be parsed.
+ * @param max_size the maximum size the NK cell could be. (for validation)
+ * @param strict   if true, rejects any malformed records.  Otherwise,
+ *                 tries to minimally validate integrity.
+ *
+ * @return A newly allocated NK record structure, or NULL on failure.
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_NK_REC*         regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
+				     uint32_t max_size, bool strict);
+
+
+/** Parses a single cell containing a subkey-list record.
  *
- * Arguments:
- *   f        -- the registry file structure
- *   offset   -- the offset of the cell (not the record) to be parsed.
- *   max_size -- the maximum size the NK cell could be. (for validation)
- *   strict   -- if true, rejects any malformed records.  Otherwise,
- *               tries to minimally validate integrity.
- * Returns:
- *   A newly allocated NK record structure, or NULL on failure.
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
  */
-REGFI_NK_REC*         regfi_parse_nk(REGFI_FILE* file, uint32 offset,
-				     uint32 max_size, bool strict);
+REGFI_SUBKEY_LIST*    regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
+					     uint32_t max_size, bool strict);
 
-REGFI_SUBKEY_LIST*    regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
-					     uint32 max_size, bool strict);
 
-REGFI_VK_REC*         regfi_parse_vk(REGFI_FILE* file, uint32 offset, 
-				     uint32 max_size, bool strict);
+/** Parses a VK (value) record at the specified offset
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_VK_REC*         regfi_parse_vk(REGFI_FILE* file, uint32_t offset, 
+				     uint32_t max_size, bool strict);
 
-REGFI_BUFFER          regfi_load_data(REGFI_FILE* file, 
-				      uint32 data_type, uint32 offset, 
-				      uint32 length, uint32 max_size, 
-				      bool data_in_offset, bool strict);
 
-REGFI_BUFFER          regfi_load_big_data(REGFI_FILE* file, 
-					  uint32 offset, uint32 data_length,
-					  uint32 cell_length, bool strict);
+/** Parses an SK (security) record at the specified offset
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_SK_REC*         regfi_parse_sk(REGFI_FILE* file, uint32_t offset, 
+				     uint32_t max_size, bool strict);
 
-REGFI_SK_REC*         regfi_parse_sk(REGFI_FILE* file, uint32 offset, 
-				     uint32 max_size, bool strict);
 
+/** Retrieves information on all cells in the registry hive which are 
+ *  currently in the unallocated status.  
+ *
+ * The unallocated status is determined based soley on the cell length sign.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
 range_list*           regfi_parse_unalloc_cells(REGFI_FILE* file);
 
-bool                  regfi_parse_cell(int fd, uint32 offset, 
-				       uint8* hdr, uint32 hdr_len,
-				       uint32* cell_length, bool* unalloc);
 
-char*                 regfi_parse_classname(REGFI_FILE* file, uint32 offset,
-					    uint16* name_length, 
-					    uint32 max_size, bool strict);
+/** Helper function to parse a cell
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+bool                  regfi_parse_cell(int fd, uint32_t offset, 
+				       uint8_t* hdr, uint32_t hdr_len,
+				       uint32_t* cell_length, bool* unalloc);
+
+
+/** Parses a classname cell
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+uint8_t*                regfi_parse_classname(REGFI_FILE* file, uint32_t offset,
+					    uint16_t* name_length, 
+					    uint32_t max_size, bool strict);
+
+
+/** Parses a single-cell data record
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_BUFFER          regfi_parse_data(REGFI_FILE* file, uint32_t offset,
+				       uint32_t length, bool strict);
 
-/* Dispose of previously parsed records */
-void                  regfi_free_key(REGFI_NK_REC* nk);
-void                  regfi_free_value(REGFI_VK_REC* vk);
 
+/** Parses a "little data" record which is stored entirely within the 
+ *  provided virtual offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_BUFFER          regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset, 
+					      uint32_t length, bool strict);
 
 
-/************************************/
-/*    Private Functions             */
-/************************************/
-REGFI_NK_REC*         regfi_rootkey(REGFI_FILE* file);
+/******************************************************************************/
+/*    Private Functions                                                       */
+/******************************************************************************/
+REGFI_NK_REC*         regfi_rootkey(REGFI_FILE* file, 
+				    REGFI_ENCODING output_encoding);
 void                  regfi_subkeylist_free(REGFI_SUBKEY_LIST* list);
-uint32                regfi_read(int fd, uint8* buf, uint32* length);
+uint32_t              regfi_read(int fd, uint8_t* buf, uint32_t* length);
 
 const char*           regfi_type_val2str(unsigned int val);
 int                   regfi_type_str2val(const char* str);
@@ -577,15 +1419,32 @@ char*                 regfi_get_dacl(WINSEC_DESC* sec_desc);
 char*                 regfi_get_owner(WINSEC_DESC* sec_desc);
 char*                 regfi_get_group(WINSEC_DESC* sec_desc);
 
-REGFI_SUBKEY_LIST*    regfi_merge_subkeylists(uint16 num_lists, 
+REGFI_SUBKEY_LIST*    regfi_merge_subkeylists(uint16_t num_lists, 
 					      REGFI_SUBKEY_LIST** lists,
 					      bool strict);
-REGFI_SUBKEY_LIST*    regfi_load_subkeylist_aux(REGFI_FILE* file, uint32 offset,
-						uint32 max_size, bool strict,
-						uint8 depth_left);
-void                  regfi_add_message(REGFI_FILE* file, uint16 msg_type, 
+REGFI_SUBKEY_LIST*    regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offset,
+						uint32_t max_size, bool strict,
+						uint8_t depth_left);
+void                  regfi_add_message(REGFI_FILE* file, uint16_t msg_type, 
 					const char* fmt, ...);
 REGFI_NK_REC*         regfi_copy_nk(const REGFI_NK_REC* nk);
 REGFI_VK_REC*         regfi_copy_vk(const REGFI_VK_REC* vk);
+int32_t               regfi_calc_maxsize(REGFI_FILE* file, uint32_t offset);
+int32_t               regfi_conv_charset(const char* input_charset, 
+					 const char* output_charset,
+					 uint8_t* input, char* output, 
+					 uint32_t input_len, uint32_t output_max);
+REGFI_DATA*           regfi_buffer_to_data(REGFI_BUFFER raw_data);
+
+/* XXX: move to base API and document */
+void                  regfi_unix2nt_time(REGFI_NTTIME* nt, time_t t);
+time_t                regfi_nt2unix_time(const REGFI_NTTIME* nt);
+
+
+void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk, 
+			     REGFI_ENCODING output_encoding, bool strict);
+void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk, 
+			       REGFI_ENCODING output_encoding, bool strict);
+
 
 #endif	/* _REGFI_H */
diff --git a/include/smb_deps.h b/include/smb_deps.h
deleted file mode 100644
index cae3f8b..0000000
--- a/include/smb_deps.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * This file contains miscellaneous pieces of code which regfio.c
- * depends upon, from the Samba Subversion tree.  See:
- *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
- *
- * Copyright (C) 2005,2009 Timothy D. Morgan
- * Copyright (C) 1992-2005 Samba development team 
- *               (see individual files under Subversion for details.)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 3 of the License.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id: smb_deps.h 134 2009-01-16 18:36:04Z tim $
- */
-
-#ifndef _SMB_DEPS_H
-#define _SMB_DEPS_H
-
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "byteorder.h"
-
-#define DEBUG(lvl,body) 0
-
-void* zalloc(size_t size);
-void* zcalloc(size_t size, unsigned int count);
-
-/* From includes.h */
-
-#define uint8  uint8_t
-#define int16  int8_t
-#define uint16 uint16_t
-#define int32  int32_t
-#define uint32 uint32_t
-
-#define MIN(a,b) ((a)<(b)?(a):(b))
-#define MAX(a,b) ((a)>(b)?(a):(b))
-
-extern int DEBUGLEVEL;
-
-/* End of stuff from includes.h */
-
-/* From smb.h */
-
-typedef struct nttime_info
-{
-  uint32 low;
-  uint32 high;
-} NTTIME;
-
-/* End of stuff from smb.h */
-
-/* From lib/time.c */
-
-#define CHAR_BIT 8
-#define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
-		    : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
-#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
-#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
-
-void unix_to_nt_time(NTTIME* nt, time_t t);
-time_t nt_time_to_unix(const NTTIME* nt);
-
-/* End of stuff from lib/time.c */
-
-#endif /* _SMB_DEPS_H */
diff --git a/include/talloc.h b/include/talloc.h
index f34065c..d7a08a6 100644
--- a/include/talloc.h
+++ b/include/talloc.h
@@ -22,7 +22,7 @@
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */
-/* $Id: talloc.h 147 2009-02-22 19:31:52Z tim $ */
+/* $Id: talloc.h 168 2010-03-03 00:08:42Z tim $ */
 
 #ifndef _TALLOC_H_
 #define _TALLOC_H_
@@ -32,7 +32,14 @@
 #include <stdarg.h>
 #include <string.h>
 #include <stdbool.h>
-#include "smb_deps.h" /* MAX macro */
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
 
 /*
   this uses a little trick to allow __LINE__ to be stringified
diff --git a/include/void_stack.h b/include/void_stack.h
index 42fefb6..dc1c048 100644
--- a/include/void_stack.h
+++ b/include/void_stack.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005,2007,2009 Timothy D. Morgan
+ * Copyright (C) 2005,2007,2009-2010 Timothy D. Morgan
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,9 +14,17 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: void_stack.h 150 2009-03-02 02:17:46Z tim $
+ * $Id: void_stack.h 169 2010-03-03 19:24:58Z tim $
  */
 
+/** 
+ *@file
+ * 
+ * This is a very simple implementation of a stack which stores chunks of
+ * memory of any type.
+ */
+
+
 #ifndef _VOID_STACK_H
 #define _VOID_STACK_H
 
@@ -25,6 +33,7 @@
 #include <string.h>
 #include "talloc.h"
 
+/** XXX: document this. */
 typedef struct _void_stack
 {
   void** elements;
@@ -32,6 +41,8 @@ typedef struct _void_stack
   unsigned short top;
 } void_stack;
 
+
+/** XXX: document this. */
 typedef struct _void_stack_iterator
 {
   const void_stack* stack;
@@ -39,149 +50,120 @@ typedef struct _void_stack_iterator
 } void_stack_iterator;
 
 
-/* void_stack_new(): 
- *  Allocates a new void_stack.
+/** Allocates a new void_stack.
  *
- * Arguments:
- *  max_size -- the maxiumum number of elements 
- *              which may be pushed onto the stack.
+ * @param max_size the maxiumum number of elements 
+ *                 which may be pushed onto the stack.
  *
- * Returns: 
- *  a pointer to the newly allocated void_stack, or NULL if an error occurred.
+ * @return a pointer to the newly allocated void_stack, 
+ *         or NULL if an error occurred.
  */
 void_stack* void_stack_new(unsigned short max_size);
 
 
-/* void_stack_copy(): 
- *  Makes a shallow copy of void_stack.
+/** Makes a shallow copy of void_stack.
  *
- * Arguments:
- *  v -- the stack to make a copy of.
+ * @param v the stack to make a copy of.
  *
- * Returns:
- *  a pointer to the duplicate void_stack, or NULL If an error occurred.
+ * @return a pointer to the duplicate void_stack, or NULL if an error occurred.
  */
 void_stack* void_stack_copy(const void_stack* v);
 
 
-/* void_stack_copy_reverse(): 
- *  Makes a shallow copy of void_stack in reverse order.
+/** Makes a shallow copy of void_stack in reverse order.
  *
- * Arguments:
- *  v -- the stack to make a copy of.
+ * @param v the stack to make a copy of.
  *
- * Returns:
- *  a pointer to the duplicate void_stack (which will be in reverse order),
- *  or NULL If an error occurred.
+ * @return a pointer to the duplicate void_stack 
+ *         (which will be in reverse order), or NULL if an error occurred.
  */
 void_stack* void_stack_copy_reverse(const void_stack* v);
 
 
-/* void_stack_free(): 
- *  Frees the memory associated with a void_stack, but not the elements held
+/** Frees the memory associated with a void_stack, but not the elements held
  *  on the stack.
  *
- * Arguments:
- *  stack -- the stack to be free()d.
+ * @param stack the stack to be free()d.
  */
 void void_stack_free(void_stack* stack);
 
 
-/* void_stack_free_deep(): 
- *  Frees the memory associated with a void_stack and the elements referenced 
- *  by the stack.  Do not use this function if the elements of the stack 
- *  are also free()d elsewhere, or contain pointers to other memory which 
- *  cannot be otherwise free()d.
+/** Frees the memory associated with a void_stack and the elements referenced 
+ *  by the stack.  
  *
- * Arguments:
- *  stack -- the stack to be free()d.
+ * Do not use this function if the elements of the stack 
+ * are also free()d elsewhere, or contain pointers to other memory which 
+ * cannot be otherwise free()d.
+ *
+ * @param stack the stack to be free()d.
  */
 void void_stack_free_deep(void_stack* stack);
 
 
-/* void_stack_size(): 
- *  Query the current number of elements on a void_stack()
+/** Query the current number of elements on a void_stack()
  *
- * Arguments:
- *  stack -- the void_stack to query
+ * @param stack the void_stack to query
  *
- * Returns:
- *  the number of elements currently on the stack.
+ * @return the number of elements currently on the stack.
  */
 unsigned short void_stack_size(const void_stack* stack);
 
 
-/* void_stack_pop():
- *  Removes the top element on a void_stack and returns a reference to it.
+/** Removes the top element on a void_stack and returns a reference to it.
  *
- * Arguments:
- *  stack -- the void_stack to pop
+ * @param stack the void_stack to pop
  *
- * Returns:
- *  a pointer to the popped stack element, or NULL if no elements exist on 
- *  the stack.
+ * @return a pointer to the popped stack element, or NULL if no elements exist
+ *         on the stack.
  */
 void* void_stack_pop(void_stack* stack);
 
 
-/* void_stack_push():
- *  Puts a new element on the top of a void_stack.
- *
- * Arguments:
- *  stack -- the void_stack being modified.
+/** Puts a new element on the top of a void_stack.
  *
- *  e     -- the element to be added
+ * @param stack the void_stack being modified.
+ * @param e the element to be added
  *
- * Returns:
- *  true if the element was successfully added, false otherwise.
+ * @return true if the element was successfully added, false otherwise.
  */
 bool void_stack_push(void_stack* stack, void* e);
 
 
-/* void_stack_cur():
- *  Returns a pointer to the current element on the top of the stack.
+/** Returns a pointer to the current element on the top of the stack.
  *
- * Arguments:
- *  stack -- the void_stack being queried.
+ * @param stack the void_stack being queried.
  *
- * Returns:
- *  a pointer to the current element on the top of the stack, or NULL if no
- *  elements exist in the stack.
+ * @return a pointer to the current element on the top of the stack, or NULL if
+ *         no elements exist in the stack.
  */
 const void* void_stack_cur(const void_stack* stack);
 
 
-/* void_stack_iterator_new():
- *  Creates a new iterator for the specified void_stack.
+/** Creates a new iterator for the specified void_stack.
  *
- * Arguments:
- *  stack -- the void_stack to be referenced by the new iterator
+ * @param stack the void_stack to be referenced by the new iterator
  *
- * Returns:
- *  a new void_stack_iterator, or NULL if an error occurred.
+ * @return a new void_stack_iterator, or NULL if an error occurred.
  */
 void_stack_iterator* void_stack_iterator_new(const void_stack* stack);
 
 
-/* void_stack_iterator_free():
- *  Frees a void_stack_iterator.  Does not affect the void_stack referenced
- *  by the iterator.
+/** Frees a void_stack_iterator.
  *
- * Arguments:
- *  iter -- the void_stack_iterator to be free()d.
+ * Does not affect the void_stack referenced by the iterator.
+ *
+ * @param iter the void_stack_iterator to be free()d.
  */
 void void_stack_iterator_free(void_stack_iterator* iter);
 
 
-/* void_stack_iterator_next():
- *  Returns a pointer to the the next element in the stack.  Iterates over 
- *  elements starting in order from the oldest element (bottom of the stack).
+/** Returns a pointer to the the next element in the stack.
+ *
+ * Iterates over elements starting in order from the oldest element (bottom of the stack).
  *
- * Arguments:
- *  iter -- the void_stack_iterator used to lookup the next element.
+ * @param iter the void_stack_iterator used to lookup the next element.
  *
- * Returns:
- *  a pointer to the next element.
+ * @return a pointer to the next element.
  */
 const void* void_stack_iterator_next(void_stack_iterator* iter);
 
diff --git a/include/winsec.h b/include/winsec.h
index 1c2db4a..6c7de49 100644
--- a/include/winsec.h
+++ b/include/winsec.h
@@ -1,13 +1,5 @@
-/*
- * This file contains refactored Samba code used to interpret Windows
- * Security Descriptors. See:
- *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
- *
- * Revisions have been made based on information provided by Microsoft
- * at: 
- *    http://msdn.microsoft.com/en-us/library/cc230366(PROT.10).aspx
- *
- * Copyright (C) 2005,2009 Timothy D. Morgan
+/* 
+ * Copyright (C) 2005,2009-2010 Timothy D. Morgan
  * Copyright (C) 1992-2005 Samba development team 
  * 
  * This program is free software; you can redistribute it and/or modify
@@ -23,7 +15,19 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: winsec.h 148 2009-02-22 23:22:59Z tim $
+ * $Id: winsec.h 169 2010-03-03 19:24:58Z tim $
+ */
+
+/**
+ * @file
+ *
+ * A small library for interpreting Windows Security Descriptors.
+ * This library was originally based on Samba source from:
+ *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
+ *
+ * The library has been heavily rewritten and improved based on information
+ * provided by Microsoft at: 
+ *    http://msdn.microsoft.com/en-us/library/cc230366%28PROT.10%29.aspx
  */
 
 #ifndef _WINSEC_H
@@ -40,8 +44,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include "smb_deps.h"
 #include "talloc.h"
+#include "byteorder.h"
 
 
 /* This is the maximum number of subauths in a SID, as defined here:
@@ -69,103 +73,240 @@
 #define WINSEC_ACE_TYPE_SYSTEM_ALARM_OBJECT    0x8
 
 
+/** XXX: document this. */
 typedef struct _winsec_uuid
 {
-       uint32 time_low;
-       uint16 time_mid;
-       uint16 time_hi_and_version;
-       uint8  clock_seq[2];
-       uint8  node[6];
+  /** XXX: document this. */
+  uint32_t time_low;
+
+  /** XXX: document this. */
+  uint16_t time_mid;
+
+  /** XXX: document this. */
+  uint16_t time_hi_and_version;
+
+  /** XXX: document this. */
+  uint8_t  clock_seq[2];
+
+  /** XXX: document this. */
+  uint8_t  node[6];
 } WINSEC_UUID;
 
 
+/** XXX: document this. */
 typedef struct _winsec_sid
 {
-  uint8_t  sid_rev_num;             /* SID revision number */
-  uint8_t  num_auths;               /* Number of sub-authorities */
-  uint8_t  id_auth[6];              /* Identifier Authority */
-  /*
-   *  Pointer to sub-authorities.
-   *
+  /** SID revision number */
+  uint8_t  sid_rev_num;
+
+  /** Number of sub-authorities */
+  uint8_t  num_auths;
+
+  /** Identifier Authority */
+  uint8_t  id_auth[6];
+
+  /** Pointer to sub-authorities.
+   * 
    * @note The values in these uint32_t's are in *native* byteorder, not
    * neccessarily little-endian...... JRA.
    */
-  /* XXX: Make this dynamically allocated? */
-  uint32_t sub_auths[WINSEC_MAX_SUBAUTHS];
+  uint32_t sub_auths[WINSEC_MAX_SUBAUTHS];   /* XXX: Make this dynamically allocated? */
 } WINSEC_DOM_SID;
 
 
+/** XXX: document this. */
 typedef struct _winsec_ace
 {
-	uint8_t type;  /* xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
-	uint8_t flags; /* xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
-	uint16_t size;
-	uint32_t access_mask;
+  /** xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
+  uint8_t type;
+
+  /** xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
+  uint8_t flags;
+
+  /** XXX: finish documenting */
+  uint16_t size;
+
+  /** XXX: finish documenting */
+  uint32_t access_mask;
+  
+  /* This stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
+
+  /** xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
+  uint32_t  obj_flags;
 
-	/* this stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
-	uint32_t  obj_flags;   /* xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
-	WINSEC_UUID* obj_guid;  /* object GUID */
-	WINSEC_UUID* inh_guid;  /* inherited object GUID */		
-        /* eof object stuff */
+  /** Object GUID */
+  WINSEC_UUID* obj_guid;
 
-	WINSEC_DOM_SID* trustee;
+  /** Inherited object GUID */
+  WINSEC_UUID* inh_guid;
+
+  /* eof object stuff */
+  
+  /** XXX: finish documenting */
+  WINSEC_DOM_SID* trustee;
 
 } WINSEC_ACE;
 
+
+/** XXX: document this. */
 typedef struct _winsec_acl
 {
-	uint16_t revision; /* 0x0003 */
-	uint16_t size;     /* size in bytes of the entire ACL structure */
-	uint32_t num_aces; /* number of Access Control Entries */
+  /** 0x0003 */
+  uint16_t revision;
+
+  /** Size, in bytes, of the entire ACL structure */
+  uint16_t size;
 
-	WINSEC_ACE** aces;
+  /** Number of Access Control Entries */
+  uint32_t num_aces;
+  
+  /** XXX: document this. */
+  WINSEC_ACE** aces;
 
 } WINSEC_ACL;
 
+
+/** XXX: document this. */
 typedef struct _winsec_desc
 {
-	uint8_t revision; /* 0x01 */
-	uint8_t sbz1;     /* "If the Control field has the RM flag set,
-			   *  then this field contains the resource
-			   *  manager (RM) control value. ... Otherwise,
-			   *  this field is reserved and MUST be set to
-			   *  zero." -- Microsoft.  See reference above.
-			   */
-	uint16_t control; /* WINSEC_DESC_* flags */
-
-	uint32_t off_owner_sid; /* offset to owner sid */
-	uint32_t off_grp_sid  ; /* offset to group sid */
-	uint32_t off_sacl     ; /* offset to system list of permissions */
-	uint32_t off_dacl     ; /* offset to list of permissions */
-
-	WINSEC_DOM_SID* owner_sid; 
-	WINSEC_DOM_SID* grp_sid;
-	WINSEC_ACL* sacl;       /* system ACL */
-	WINSEC_ACL* dacl;       /* user ACL */
+  /** 0x01 */
+  uint8_t revision;
+
+  /** XXX: better explain this
+   *
+   * "If the Control field has the RM flag set, then this field contains the
+   *  resource manager (RM) control value. ... Otherwise, this field is reserved
+   *  and MUST be set to zero." -- Microsoft.
+   *  See:
+   *   http://msdn.microsoft.com/en-us/library/cc230371%28PROT.10%29.aspx
+   */
+  uint8_t sbz1;
+
+  /** WINSEC_DESC_* flags */
+  uint16_t control;
+  
+  /** Offset to owner sid */
+  uint32_t off_owner_sid;
+
+  /** Offset to group sid */
+  uint32_t off_grp_sid;
+
+  /** Offset to system list of permissions */
+  uint32_t off_sacl;
+
+  /** Offset to list of permissions */
+  uint32_t off_dacl;
+
+  /** XXX: document this */
+  WINSEC_DOM_SID* owner_sid; 
+
+  /** XXX: document this */
+  WINSEC_DOM_SID* grp_sid;
+
+  /** System ACL */
+  WINSEC_ACL* sacl;
+
+  /** User ACL */
+  WINSEC_ACL* dacl;
 
 } WINSEC_DESC;
 
+
+/**
+ *
+ * XXX: finish documenting
+ */
 WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len);
+
+
+/**
+ *
+ * XXX: finish documenting
+ */
 void winsec_free_descriptor(WINSEC_DESC* desc);
 
+/**
+ *
+ * XXX: finish documenting
+ */
 WINSEC_DESC* winsec_parse_desc(void* talloc_ctx,
 			       const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
 WINSEC_ACL* winsec_parse_acl(void* talloc_ctx, 
 			     const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
 WINSEC_ACE* winsec_parse_ace(void* talloc_ctx, 
 			     const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
 WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx, 
 				     const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
 WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx, 
 			       const uint8_t* buf, uint32_t buf_len);
 
+
+/**
+ *
+ * XXX: finish documenting
+ */
 size_t winsec_sid_size(const WINSEC_DOM_SID* sid);
+
+/**
+ *
+ * XXX: finish documenting
+ */
 int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
 int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
 bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
 bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
 bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
 bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
 bool winsec_ace_object(uint8_t type);
 
 #endif /* _WINSEC_H */
diff --git a/lib/Makefile b/lib/Makefile
index c761c61..022ae9b 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -2,16 +2,13 @@
 
 ################################################################################
 
-FILES=regfi.o smb_deps.o winsec.o void_stack.o range_list.o lru_cache.o talloc.o
+FILES=regfi.o winsec.o void_stack.o range_list.o lru_cache.o talloc.o
 
 all: $(FILES)
 
 regfi.o: regfi.c
 	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ regfi.c
 
-smb_deps.o: smb_deps.c
-	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ smb_deps.c
-
 winsec.o: winsec.c
 	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ winsec.c
 
diff --git a/lib/lru_cache.c b/lib/lru_cache.c
index 74f5f08..dfa9111 100644
--- a/lib/lru_cache.c
+++ b/lib/lru_cache.c
@@ -14,7 +14,11 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: lru_cache.c 147 2009-02-22 19:31:52Z tim $
+ * $Id: lru_cache.c 169 2010-03-03 19:24:58Z tim $
+ */
+
+/**
+ * @file
  */
 
 #include "lru_cache.h"
diff --git a/lib/range_list.c b/lib/range_list.c
index 7e80a72..80b97f0 100644
--- a/lib/range_list.c
+++ b/lib/range_list.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2009 Timothy D. Morgan
+ * Copyright (C) 2008-2010 Timothy D. Morgan
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,7 +14,11 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: range_list.c 150 2009-03-02 02:17:46Z tim $
+ * $Id: range_list.c 169 2010-03-03 19:24:58Z tim $
+ */
+
+/**
+ * @file
  */
 
 #include "range_list.h"
diff --git a/lib/regfi.c b/lib/regfi.c
index 7a6da3c..d292dfe 100644
--- a/lib/regfi.c
+++ b/lib/regfi.c
@@ -1,10 +1,5 @@
 /*
- * Branched from Samba project Subversion repository, version #7470:
- *   http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/registry/regfio.c?rev=7470&view=auto
- *
- * Windows NT (and later) registry parsing library
- *
- * Copyright (C) 2005-2009 Timothy D. Morgan
+ * Copyright (C) 2005-2010 Timothy D. Morgan
  * Copyright (C) 2005 Gerald (Jerry) Carter
  *
  * This program is free software; you can redistribute it and/or modify
@@ -18,9 +13,22 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: regfi.c 173 2010-03-08 03:39:09Z tim $
+ */
+
+/** 
+ * @file
+ *
+ * Windows NT (and later) read-only registry library
+ *
+ * See @ref regfi.h for more information.
  *
- * $Id: regfi.c 155 2009-06-03 23:45:58Z tim $
+ * Branched from Samba project Subversion repository, version #7470:
+ *   http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/registry/regfio.c?rev=7470&view=auto
+ *
+ * Since then, it has been heavily rewritten, simplified, and improved.
  */
 
 #include "regfi.h"
@@ -32,16 +40,18 @@ static const char* regfi_type_names[] =
   {"NONE", "SZ", "EXPAND_SZ", "BINARY", "DWORD", "DWORD_BE", "LINK",
    "MULTI_SZ", "RSRC_LIST", "RSRC_DESC", "RSRC_REQ_LIST", "QWORD"};
 
+const char* regfi_encoding_names[] =
+  {"US-ASCII//TRANSLIT", "UTF-8//TRANSLIT", "UTF-16LE//TRANSLIT"};
 
 
 /******************************************************************************
  ******************************************************************************/
-void regfi_add_message(REGFI_FILE* file, uint16 msg_type, const char* fmt, ...)
+void regfi_add_message(REGFI_FILE* file, uint16_t msg_type, const char* fmt, ...)
 {
   /* XXX: This function is not particularly efficient,
    *      but then it is mostly used during errors. 
    */
-  uint32 buf_size, buf_used;
+  uint32_t buf_size, buf_used;
   char* new_msg;
   va_list args;
 
@@ -95,13 +105,27 @@ char* regfi_get_messages(REGFI_FILE* file)
 }
 
 
-void regfi_set_message_mask(REGFI_FILE* file, uint16 mask)
+void regfi_set_message_mask(REGFI_FILE* file, uint16_t mask)
 {
   file->msg_mask = mask;
 }
 
 
-/* Returns NULL on error */
+/******************************************************************************
+ * Returns NULL for an invalid e
+ *****************************************************************************/
+static const char* regfi_encoding_int2str(REGFI_ENCODING e)
+{
+  if(e < REGFI_NUM_ENCODINGS)
+    return regfi_encoding_names[e];
+
+  return NULL;
+}
+
+
+/******************************************************************************
+ * Returns NULL for an invalid val
+ *****************************************************************************/
 const char* regfi_type_val2str(unsigned int val)
 {
   if(val == REG_KEY)
@@ -114,7 +138,9 @@ const char* regfi_type_val2str(unsigned int val)
 }
 
 
-/* Returns -1 on error */
+/******************************************************************************
+ * Returns -1 on error
+ *****************************************************************************/
 int regfi_type_str2val(const char* str)
 {
   int i;
@@ -135,7 +161,7 @@ int regfi_type_str2val(const char* str)
 
 /* Security descriptor formatting functions  */
 
-const char* regfi_ace_type2str(uint8 type)
+const char* regfi_ace_type2str(uint8_t type)
 {
   static const char* map[7] 
     = {"ALLOW", "DENY", "AUDIT", "ALARM", 
@@ -155,7 +181,7 @@ const char* regfi_ace_type2str(uint8 type)
 /* For more info, see:
  *   http://msdn2.microsoft.com/en-us/library/aa772242.aspx
  */
-char* regfi_ace_flags2str(uint8 flags)
+char* regfi_ace_flags2str(uint8_t flags)
 {
   static const char* flag_map[32] = 
     { "OI", /* Object Inherit */
@@ -170,8 +196,8 @@ char* regfi_ace_flags2str(uint8 flags)
 
   char* ret_val = malloc(35*sizeof(char));
   char* fo = ret_val;
-  uint32 i;
-  uint8 f;
+  uint32_t i;
+  uint8_t f;
 
   if(ret_val == NULL)
     return NULL;
@@ -204,9 +230,9 @@ char* regfi_ace_flags2str(uint8 flags)
 }
 
 
-char* regfi_ace_perms2str(uint32 perms)
+char* regfi_ace_perms2str(uint32_t perms)
 {
-  uint32 i, p;
+  uint32_t i, p;
   /* This is more than is needed by a fair margin. */
   char* ret_val = malloc(350*sizeof(char));
   char* r = ret_val;
@@ -285,9 +311,9 @@ char* regfi_ace_perms2str(uint32 perms)
 
 char* regfi_sid2str(WINSEC_DOM_SID* sid)
 {
-  uint32 i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
-  uint32 left = size;
-  uint8 comps = sid->num_auths;
+  uint32_t i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
+  uint32_t left = size;
+  uint8_t comps = sid->num_auths;
   char* ret_val = malloc(size);
   
   if(ret_val == NULL)
@@ -307,7 +333,7 @@ char* regfi_sid2str(WINSEC_DOM_SID* sid)
 
 char* regfi_get_acl(WINSEC_ACL* acl)
 {
-  uint32 i, extra, size = 0;
+  uint32_t i, extra, size = 0;
   const char* type_str;
   char* flags_str;
   char* perms_str;
@@ -406,10 +432,10 @@ char* regfi_get_group(WINSEC_DESC *sec_desc)
  * parameter by reference.  If both the return value and length parameter are 
  * returned as 0, then EOF was encountered immediately
  *****************************************************************************/
-uint32 regfi_read(int fd, uint8* buf, uint32* length)
+uint32_t regfi_read(int fd, uint8_t* buf, uint32_t* length)
 {
-  uint32 rsize = 0;
-  uint32 rret = 0;
+  uint32_t rsize = 0;
+  uint32_t rret = 0;
 
   do
   {
@@ -430,12 +456,12 @@ uint32 regfi_read(int fd, uint8* buf, uint32* length)
 /*****************************************************************************
  *
  *****************************************************************************/
-bool regfi_parse_cell(int fd, uint32 offset, uint8* hdr, uint32 hdr_len,
-		      uint32* cell_length, bool* unalloc)
+bool regfi_parse_cell(int fd, uint32_t offset, uint8_t* hdr, uint32_t hdr_len,
+		      uint32_t* cell_length, bool* unalloc)
 {
-  uint32 length;
-  int32 raw_length;
-  uint8 tmp[4];
+  uint32_t length;
+  int32_t raw_length;
+  uint8_t tmp[4];
 
   if(lseek(fd, offset, SEEK_SET) == -1)
     return false;
@@ -470,11 +496,11 @@ bool regfi_parse_cell(int fd, uint32 offset, uint8* hdr, uint32 hdr_len,
 }
 
 
-/*******************************************************************
+/******************************************************************************
  * Given an offset and an hbin, is the offset within that hbin?
  * The offset is a virtual file offset.
- *******************************************************************/
-static bool regfi_offset_in_hbin(const REGFI_HBIN* hbin, uint32 voffset)
+ ******************************************************************************/
+static bool regfi_offset_in_hbin(const REGFI_HBIN* hbin, uint32_t voffset)
 {
   if(!hbin)
     return false;
@@ -488,22 +514,36 @@ static bool regfi_offset_in_hbin(const REGFI_HBIN* hbin, uint32 voffset)
 
 
 
-/*******************************************************************
- * Provide a virtual offset and receive the correpsonding HBIN 
+/******************************************************************************
+ * Provide a physical offset and receive the correpsonding HBIN
  * block for it.  NULL if one doesn't exist.
- *******************************************************************/
-const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32 voffset)
+ ******************************************************************************/
+const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset)
 {
-  return (const REGFI_HBIN*)range_list_find_data(file->hbins, 
-						 voffset+REGFI_REGF_SIZE);
+  return (const REGFI_HBIN*)range_list_find_data(file->hbins, offset);
 }
 
 
+/******************************************************************************
+ * Calculate the largest possible cell size given a physical offset.
+ * Largest size is based on the HBIN the offset is currently a member of.
+ * Returns negative values on error.
+ * (Since cells can only be ~2^31 in size, this works out.)
+ ******************************************************************************/
+int32_t regfi_calc_maxsize(REGFI_FILE* file, uint32_t offset)
+{
+  const REGFI_HBIN* hbin = regfi_lookup_hbin(file, offset);
+  if(hbin == NULL)
+    return -1;
+
+  return (hbin->block_size + hbin->file_off) - offset;
+}
+
 
 /******************************************************************************
  ******************************************************************************/
-REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32 offset, 
-					 uint32 num_keys, uint32 max_size, 
+REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset, 
+					 uint32_t num_keys, uint32_t max_size, 
 					 bool strict)
 {
   REGFI_SUBKEY_LIST* ret_val;
@@ -535,14 +575,14 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32 offset,
 
 /******************************************************************************
  ******************************************************************************/
-REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32 offset, 
-					     uint32 max_size, bool strict,
-					     uint8 depth_left)
+REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offset, 
+					     uint32_t max_size, bool strict,
+					     uint8_t depth_left)
 {
   REGFI_SUBKEY_LIST* ret_val;
   REGFI_SUBKEY_LIST** sublists;
-  const REGFI_HBIN* sublist_hbin;
-  uint32 i, num_sublists, off, max_length;
+  uint32_t i, num_sublists, off;
+  int32_t sublist_maxsize;
 
   if(depth_left == 0)
   {
@@ -564,15 +604,13 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32 offset,
     for(i=0; i < num_sublists; i++)
     {
       off = ret_val->elements[i].offset + REGFI_REGF_SIZE;
-      sublist_hbin = regfi_lookup_hbin(file, ret_val->elements[i].offset);
-      if(sublist_hbin == NULL)
+
+      sublist_maxsize = regfi_calc_maxsize(file, off);
+      if(sublist_maxsize < 0)
 	sublists[i] = NULL;
       else
-      {
-	max_length = sublist_hbin->block_size + sublist_hbin->file_off - off;
-	sublists[i] = regfi_load_subkeylist_aux(file, off, max_length, strict,
-						depth_left-1);
-      }
+	sublists[i] = regfi_load_subkeylist_aux(file, off, sublist_maxsize, 
+						strict, depth_left-1);
     }
     talloc_free(ret_val);
 
@@ -585,13 +623,13 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32 offset,
 
 /******************************************************************************
  ******************************************************************************/
-REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset, 
-					  uint32 max_size, bool strict)
+REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset, 
+					  uint32_t max_size, bool strict)
 {
   REGFI_SUBKEY_LIST* ret_val;
-  uint32 i, cell_length, length, elem_size, read_len;
-  uint8* elements = NULL;
-  uint8 buf[REGFI_SUBKEY_LIST_MIN_LEN];
+  uint32_t i, cell_length, length, elem_size, read_len;
+  uint8_t* elements = NULL;
+  uint8_t buf[REGFI_SUBKEY_LIST_MIN_LEN];
   bool unalloc;
   bool recursive_type;
 
@@ -616,10 +654,10 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
   if(buf[0] == 'r' && buf[1] == 'i')
   {
     recursive_type = true;
-    elem_size = sizeof(uint32);
+    elem_size = sizeof(uint32_t);
   }
   else if(buf[0] == 'l' && buf[1] == 'i')
-    elem_size = sizeof(uint32);
+    elem_size = sizeof(uint32_t);
   else if((buf[0] == 'l') && (buf[1] == 'f' || buf[1] == 'h'))
     elem_size = sizeof(REGFI_SUBKEY_LIST_ELEM);
   else
@@ -645,14 +683,14 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
     ret_val->num_keys = ret_val->num_children;
 
   length = elem_size*ret_val->num_children;
-  if(cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32) < length)
+  if(cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t) < length)
   {
     regfi_add_message(file, REGFI_MSG_WARN, "Number of elements too large for"
 		      " cell while parsing subkey-list at offset 0x%.8X.", 
 		      offset);
     if(strict)
       goto fail;
-    length = cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32);
+    length = cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t);
   }
 
   ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM, 
@@ -660,7 +698,7 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
   if(ret_val->elements == NULL)
     goto fail;
 
-  elements = (uint8*)malloc(length);
+  elements = (uint8_t*)malloc(length);
   if(elements == NULL)
     goto fail;
 
@@ -668,7 +706,7 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
   if(regfi_read(file->fd, elements, &read_len) != 0 || read_len != length)
     goto fail;
 
-  if(elem_size == sizeof(uint32))
+  if(elem_size == sizeof(uint32_t))
   {
     for (i=0; i < ret_val->num_children; i++)
     {
@@ -698,11 +736,11 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
 
 /*******************************************************************
  *******************************************************************/
-REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16 num_lists, 
+REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16_t num_lists, 
 					   REGFI_SUBKEY_LIST** lists,
 					   bool strict)
 {
-  uint32 i,j,k;
+  uint32_t i,j,k;
   REGFI_SUBKEY_LIST* ret_val;
 
   if(lists == NULL)
@@ -754,13 +792,13 @@ REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16 num_lists,
 /******************************************************************************
  *
  ******************************************************************************/
-REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset, uint32 max_size, 
+REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, 
 			     bool strict)
 {
   REGFI_SK_REC* ret_val;
-  uint8* sec_desc_buf = NULL;
-  uint32 cell_length, length;
-  uint8 sk_header[REGFI_SK_MIN_LENGTH];
+  uint8_t* sec_desc_buf = NULL;
+  uint32_t cell_length, length;
+  uint8_t sk_header[REGFI_SK_MIN_LENGTH];
   bool unalloc = false;
 
   if(!regfi_parse_cell(file->fd, offset, sk_header, REGFI_SK_MIN_LENGTH,
@@ -791,7 +829,7 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset, uint32 max_size,
   if(ret_val->cell_size > max_size)
     ret_val->cell_size = max_size & 0xFFFFFFF8;
   if((ret_val->cell_size < REGFI_SK_MIN_LENGTH) 
-     || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
+     || (strict && (ret_val->cell_size & 0x00000007) != 0))
   {
     regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size found while"
 		      " parsing SK record at offset 0x%.8X.", offset);
@@ -807,8 +845,8 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset, uint32 max_size,
   ret_val->ref_count = IVAL(sk_header, 0xC);
   ret_val->desc_size = IVAL(sk_header, 0x10);
 
-  if(ret_val->prev_sk_off != (ret_val->prev_sk_off & 0xFFFFFFF8) 
-     || ret_val->next_sk_off != (ret_val->next_sk_off & 0xFFFFFFF8))
+  if((ret_val->prev_sk_off & 0x00000007) != 0
+     || (ret_val->next_sk_off & 0x00000007) != 0)
   {
     regfi_add_message(file, REGFI_MSG_WARN, "SK record's next/previous offsets"
 		      " are not a multiple of 8 while parsing SK record at"
@@ -824,7 +862,7 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset, uint32 max_size,
     goto fail;
   }
 
-  sec_desc_buf = (uint8*)malloc(ret_val->desc_size);
+  sec_desc_buf = (uint8_t*)malloc(ret_val->desc_size);
   if(sec_desc_buf == NULL)
     goto fail;
 
@@ -858,11 +896,11 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset, uint32 max_size,
 }
 
 
-REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset, 
-					uint32 num_values, bool strict)
+REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32_t offset, 
+					uint32_t num_values, bool strict)
 {
   REGFI_VALUE_LIST* ret_val;
-  uint32 i, cell_length, length, read_len;
+  uint32_t i, cell_length, length, read_len;
   bool unalloc;
 
   if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
@@ -872,7 +910,7 @@ REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset,
     return NULL;
   }
 
-  if(cell_length != (cell_length & 0xFFFFFFF8))
+  if((cell_length & 0x00000007) != 0)
   {
     regfi_add_message(file, REGFI_MSG_WARN, "Cell length not a multiple of 8"
 		      " while parsing value list at offset 0x%.8X.", offset);
@@ -881,16 +919,16 @@ REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset,
     cell_length = cell_length & 0xFFFFFFF8;
   }
 
-  if((num_values * sizeof(uint32)) > cell_length-sizeof(uint32))
+  if((num_values * sizeof(uint32_t)) > cell_length-sizeof(uint32_t))
   {
     regfi_add_message(file, REGFI_MSG_WARN, "Too many values found"
 		      " while parsing value list at offset 0x%.8X.", offset);
     if(strict)
       return NULL;
-    num_values = cell_length/sizeof(uint32) - sizeof(uint32);
+    num_values = cell_length/sizeof(uint32_t) - sizeof(uint32_t);
   }
 
-  read_len = num_values*sizeof(uint32);
+  read_len = num_values*sizeof(uint32_t);
   ret_val = talloc(NULL, REGFI_VALUE_LIST);
   if(ret_val == NULL)
     return NULL;
@@ -904,7 +942,7 @@ REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset,
   ret_val->num_values = num_values;
 
   length = read_len;
-  if((regfi_read(file->fd, (uint8*)ret_val->elements, &length) != 0) 
+  if((regfi_read(file->fd, (uint8_t*)ret_val->elements, &length) != 0) 
      || length != read_len)
   {
     regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read value pointers"
@@ -924,7 +962,7 @@ REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset,
       /* XXX: Need to revisit this file length check when we start dealing 
        *      with partial files. */
       if((ret_val->elements[i] + REGFI_REGF_SIZE > file->file_length)
-	 || ((ret_val->elements[i] & 0xFFFFFFF8) != ret_val->elements[i]))
+	 || ((ret_val->elements[i] & 0x00000007) != 0))
       {
 	regfi_add_message(file, REGFI_MSG_WARN, "Invalid value pointer"
 			  " (0x%.8X) found while parsing value list at offset"
@@ -939,70 +977,71 @@ REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset,
 }
 
 
+void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk, 
+			       REGFI_ENCODING output_encoding, bool strict)
+{
+  /* XXX: Registry value names are supposedly limited to 16383 characters 
+   *      according to:
+   *      http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+   *      Might want to emit a warning if this is exceeded.  
+   *      It is expected that "characters" could be variable width.
+   *      Also, it may be useful to use this information to limit false positives
+   *      when recovering deleted VK records.
+   */
+  int32_t tmp_size;
+  REGFI_ENCODING from_encoding = (vk->flags & REGFI_VK_FLAG_ASCIINAME)
+    ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE;
+
+  if(from_encoding == output_encoding)
+  {
+    vk->valuename_raw = talloc_realloc(vk, vk->valuename_raw,
+					    uint8_t, vk->name_length+1);
+    vk->valuename_raw[vk->name_length] = '\0';
+    vk->valuename = (char*)vk->valuename_raw;
+  }
+  else
+  {
+    vk->valuename = talloc_array(vk, char, vk->name_length+1);
+    if(vk->valuename == NULL)
+    {
+      regfi_free_value(vk);
+      return;
+    }
+
+    tmp_size = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
+				  regfi_encoding_int2str(output_encoding),
+				  vk->valuename_raw, vk->valuename,
+				  vk->name_length, vk->name_length+1);
+    if(tmp_size < 0)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Error occurred while converting"
+			" valuename to encoding %s.  Error message: %s",
+			regfi_encoding_int2str(output_encoding), 
+			strerror(-tmp_size));
+      talloc_free(vk->valuename);
+      vk->valuename = NULL;
+    }
+  }
+}
+
 
 /******************************************************************************
  ******************************************************************************/
-REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32 offset, bool strict)
+REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32_t offset, 
+			       REGFI_ENCODING output_encoding, bool strict)
 {
   REGFI_VK_REC* ret_val = NULL;
-  const REGFI_HBIN* hbin;
-  uint32 data_offset, data_maxsize;
-  REGFI_BUFFER data;
+  int32_t max_size;
 
-  hbin = regfi_lookup_hbin(file, offset - REGFI_REGF_SIZE);
-  if(!hbin)
+  max_size = regfi_calc_maxsize(file, offset);
+  if(max_size < 0)
     return NULL;
   
-  ret_val = regfi_parse_vk(file, offset, 
-			   hbin->block_size + hbin->file_off - offset, strict);
-
+  ret_val = regfi_parse_vk(file, offset, max_size, strict);
   if(ret_val == NULL)
     return NULL;
 
-  if(ret_val->data_size == 0)
-    ret_val->data = NULL;
-  else
-  {
-    if(ret_val->data_in_offset)
-    {
-      data = regfi_load_data(file, ret_val->type, ret_val->data_off,
-			      ret_val->data_size, 4,
-			      ret_val->data_in_offset, strict);
-      ret_val->data = data.buf;
-      ret_val->data_size = data.len;
-    }
-    else
-    {
-      hbin = regfi_lookup_hbin(file, ret_val->data_off);
-      if(hbin)
-      {
-	data_offset = ret_val->data_off+REGFI_REGF_SIZE;
-	data_maxsize = hbin->block_size + hbin->file_off - data_offset;
-	data = regfi_load_data(file, ret_val->type, data_offset, 
-				ret_val->data_size, data_maxsize, 
-				ret_val->data_in_offset, strict);
-	ret_val->data = data.buf;
-	ret_val->data_size = data.len;
-
-      }
-      else
-      {
-	regfi_add_message(file, REGFI_MSG_WARN, "Could not find HBIN for data"
-			  " while parsing VK record at offset 0x%.8X.", 
-			  ret_val->offset);
-	ret_val->data = NULL;
-      }
-    }
-
-    if(ret_val->data == NULL)
-    {
-      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse data record"
-			" while parsing VK record at offset 0x%.8X.",
-			ret_val->offset, ret_val->valuename);
-    }
-    else
-      talloc_steal(ret_val, ret_val->data);
-  }
+  regfi_interpret_valuename(file, ret_val, output_encoding, strict);
 
   return ret_val;
 }
@@ -1011,13 +1050,13 @@ REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32 offset, bool strict)
 /******************************************************************************
  * If !strict, the list may contain NULLs, VK records may point to NULL.
  ******************************************************************************/
-REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32 offset, 
-				       uint32 num_values, uint32 max_size,
+REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset, 
+				       uint32_t num_values, uint32_t max_size,
 				       bool strict)
 {
-  uint32 usable_num_values;
+  uint32_t usable_num_values;
 
-  if((num_values+1) * sizeof(uint32) > max_size)
+  if((num_values+1) * sizeof(uint32_t) > max_size)
   {
     regfi_add_message(file, REGFI_MSG_WARN, "Number of values indicated by"
 		      " parent key (%d) would cause cell to straddle HBIN"
@@ -1025,7 +1064,7 @@ REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32 offset,
 		      " 0x%.8X.", num_values, offset);
     if(strict)
       return NULL;
-    usable_num_values = max_size/sizeof(uint32) - sizeof(uint32);
+    usable_num_values = max_size/sizeof(uint32_t) - sizeof(uint32_t);
   }
   else
     usable_num_values = num_values;
@@ -1034,38 +1073,82 @@ REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32 offset,
 }
 
 
+void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk, 
+			     REGFI_ENCODING output_encoding, bool strict)
+{
+  /* XXX: Registry key names are supposedly limited to 255 characters according to:
+   *      http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+   *      Might want to emit a warning if this is exceeded.  
+   *      It is expected that "characters" could be variable width.
+   *      Also, it may be useful to use this information to limit false positives
+   *      when recovering deleted NK records.
+   */
+  int32_t tmp_size;
+  REGFI_ENCODING from_encoding = (nk->flags & REGFI_NK_FLAG_ASCIINAME) 
+    ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE;
+  
+  if(from_encoding == output_encoding)
+  {
+    nk->keyname_raw = talloc_realloc(nk, nk->keyname_raw, uint8_t, nk->name_length+1);
+    nk->keyname_raw[nk->name_length] = '\0';
+    nk->keyname = (char*)nk->keyname_raw;
+  }
+  else
+  {
+    nk->keyname = talloc_array(nk, char, nk->name_length+1);
+    if(nk->keyname == NULL)
+    {
+      regfi_free_key(nk);
+      return;
+    }
+
+    tmp_size = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
+				  regfi_encoding_int2str(output_encoding),
+				  nk->keyname_raw, nk->keyname,
+				  nk->name_length, nk->name_length+1);
+    if(tmp_size < 0)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Error occurred while converting"
+			" keyname to encoding %s.  Error message: %s",
+			regfi_encoding_int2str(output_encoding), 
+			strerror(-tmp_size));
+      talloc_free(nk->keyname);
+      nk->keyname = NULL;
+    }
+  }
+}
+
 
 /******************************************************************************
  *
  ******************************************************************************/
-REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset, bool strict)
+REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset, 
+			     REGFI_ENCODING output_encoding, bool strict)
 {
-  const REGFI_HBIN* hbin;
-  const REGFI_HBIN* sub_hbin;
   REGFI_NK_REC* nk;
-  uint32 max_length, off;
+  uint32_t off;
+  int32_t max_size;
 
-  hbin = regfi_lookup_hbin(file, offset-REGFI_REGF_SIZE);
-  if (hbin == NULL) 
+  max_size = regfi_calc_maxsize(file, offset);
+  if (max_size < 0) 
     return NULL;
 
   /* get the initial nk record */
-  max_length = hbin->block_size + hbin->file_off - offset;
-  if((nk = regfi_parse_nk(file, offset, max_length, true)) == NULL)
+  if((nk = regfi_parse_nk(file, offset, max_size, true)) == NULL)
   {
     regfi_add_message(file, REGFI_MSG_ERROR, "Could not load NK record at"
 		      " offset 0x%.8X.", offset);
     return NULL;
   }
 
+  regfi_interpret_keyname(file, nk, output_encoding, strict);
+
   /* get value list */
   if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE)) 
   {
-    sub_hbin = hbin;
-    if(!regfi_offset_in_hbin(hbin, nk->values_off)) 
-      sub_hbin = regfi_lookup_hbin(file, nk->values_off);
-    
-    if(sub_hbin == NULL)
+    off = nk->values_off + REGFI_REGF_SIZE;
+    max_size = regfi_calc_maxsize(file, off);
+    if(max_size < 0)
     {
       if(strict)
       {
@@ -1078,10 +1161,8 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset, bool strict)
     }
     else
     {
-      off = nk->values_off + REGFI_REGF_SIZE;
-      max_length = sub_hbin->block_size + sub_hbin->file_off - off;
-      nk->values = regfi_load_valuelist(file, off, nk->num_values, max_length, 
-					true);
+      nk->values = regfi_load_valuelist(file, off, nk->num_values, 
+					max_size, true);
       if(nk->values == NULL)
       {
 	regfi_add_message(file, REGFI_MSG_WARN, "Could not load value list"
@@ -1099,11 +1180,9 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset, bool strict)
   /* now get subkey list */
   if(nk->num_subkeys && (nk->subkeys_off != REGFI_OFFSET_NONE)) 
   {
-    sub_hbin = hbin;
-    if(!regfi_offset_in_hbin(hbin, nk->subkeys_off))
-      sub_hbin = regfi_lookup_hbin(file, nk->subkeys_off);
-
-    if(sub_hbin == NULL) 
+    off = nk->subkeys_off + REGFI_REGF_SIZE;
+    max_size = regfi_calc_maxsize(file, off);
+    if(max_size < 0) 
     {
       if(strict)
       {
@@ -1115,10 +1194,8 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset, bool strict)
     }
     else
     {
-      off = nk->subkeys_off + REGFI_REGF_SIZE;
-      max_length = sub_hbin->block_size + sub_hbin->file_off - off;
       nk->subkeys = regfi_load_subkeylist(file, off, nk->num_subkeys,
-					  max_length, true);
+					  max_size, true);
 
       if(nk->subkeys == NULL)
       {
@@ -1136,11 +1213,10 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset, bool strict)
 
 /******************************************************************************
  ******************************************************************************/
-const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32 offset, bool strict)
+const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32_t offset, bool strict)
 {
   REGFI_SK_REC* ret_val = NULL;
-  const REGFI_HBIN* hbin;
-  uint32 max_length;
+  int32_t max_size;
   void* failure_ptr = NULL;
   
   /* First look if we have already parsed it */
@@ -1152,12 +1228,11 @@ const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32 offset, bool strict)
 
   if(ret_val == NULL)
   {
-    hbin = regfi_lookup_hbin(file, offset - REGFI_REGF_SIZE);
-    if(hbin == NULL)
+    max_size = regfi_calc_maxsize(file, offset);
+    if(max_size < 0)
       return NULL;
 
-    max_length = hbin->block_size + hbin->file_off - offset;
-    ret_val = regfi_parse_sk(file, offset, max_length, strict);
+    ret_val = regfi_parse_sk(file, offset, max_size, strict);
     if(ret_val == NULL)
     { /* Cache the parse failure and bail out. */
       failure_ptr = talloc(NULL, uint32_t);
@@ -1178,59 +1253,47 @@ const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32 offset, bool strict)
 
 /******************************************************************************
  ******************************************************************************/
-static bool regfi_find_root_nk(REGFI_FILE* file, uint32 offset,uint32 hbin_size,
-			       uint32* root_offset)
+REGFI_NK_REC* regfi_find_root_nk(REGFI_FILE* file, const REGFI_HBIN* hbin, 
+				 REGFI_ENCODING output_encoding)
 {
-  uint8 tmp[4];
-  int32 record_size;
-  uint32 length, hbin_offset = 0;
   REGFI_NK_REC* nk = NULL;
-  bool found = false;
+  uint32_t cell_length;
+  uint32_t cur_offset = hbin->file_off+REGFI_HBIN_HEADER_SIZE;
+  uint32_t hbin_end = hbin->file_off+hbin->block_size;
+  bool unalloc;
 
-  for(record_size=0; !found && (hbin_offset < hbin_size); )
+  while(cur_offset < hbin_end)
   {
-    if(lseek(file->fd, offset+hbin_offset, SEEK_SET) == -1)
-      return false;
+    if(!regfi_parse_cell(file->fd, cur_offset, NULL, 0, &cell_length, &unalloc))
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell at offset"
+			" 0x%.8X while searching for root key.", cur_offset);
+      return NULL;
+    }
     
-    length = 4;
-    if((regfi_read(file->fd, tmp, &length) != 0) || length != 4)
-      return false;
-    record_size = IVALS(tmp, 0);
-
-    if(record_size < 0)
+    if(!unalloc)
     {
-      record_size = record_size*(-1);
-      nk = regfi_parse_nk(file, offset+hbin_offset, hbin_size-hbin_offset, true);
+      nk = regfi_load_key(file, cur_offset, output_encoding, true);
       if(nk != NULL)
       {
-	if(nk->key_type & REGFI_NK_FLAG_ROOT)
-	{
-	  found = true;
-	  *root_offset = nk->offset;
-	}
-	regfi_free_key(nk);
+	if(nk->flags & REGFI_NK_FLAG_ROOT)
+	  return nk;
       }
     }
 
-    hbin_offset += record_size;
+    cur_offset += cell_length;
   }
 
-  return found;
+  return NULL;
 }
 
 
-/*******************************************************************
- * Open the registry file and then read in the REGF block to get the
- * first hbin offset.
- *******************************************************************/
+/******************************************************************************
+ ******************************************************************************/
 REGFI_FILE* regfi_open(const char* filename)
 {
-  struct stat sbuf;
-  REGFI_FILE* rb;
-  REGFI_HBIN* hbin = NULL;
-  uint32 hbin_off, file_length, cache_secret;
+  REGFI_FILE* ret_val;
   int fd;
-  bool rla;
 
   /* open an existing file */
   if ((fd = open(filename, REGFI_OPEN_FLAGS)) == -1)
@@ -1238,7 +1301,26 @@ REGFI_FILE* regfi_open(const char* filename)
     /* fprintf(stderr, "regfi_open: failure to open %s (%s)\n", filename, strerror(errno));*/
     return NULL;
   }
-  
+
+  ret_val = regfi_alloc(fd);
+
+  if(ret_val == NULL)
+    close(fd);
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_FILE* regfi_alloc(int fd)
+{
+  struct stat sbuf;
+  REGFI_FILE* rb;
+  REGFI_HBIN* hbin = NULL;
+  uint32_t hbin_off, file_length, cache_secret;
+  bool rla;
+
   /* Determine file length.  Must be at least big enough 
    * for the header and one hbin. 
    */
@@ -1248,11 +1330,10 @@ REGFI_FILE* regfi_open(const char* filename)
   if(file_length < REGFI_REGF_SIZE+REGFI_HBIN_ALLOC)
     return NULL;
 
-  /* read in an existing file */
+  /* Read file header */
   if ((rb = regfi_parse_regf(fd, true)) == NULL) 
   {
-    /* fprintf(stderr, "regfi_open: Failed to read initial REGF block\n"); */
-    close(fd);
+    /* fprintf(stderr, "regfi_alloc: Failed to read initial REGF block\n"); */
     return NULL;
   }
   rb->file_length = file_length;  
@@ -1260,8 +1341,7 @@ REGFI_FILE* regfi_open(const char* filename)
   rb->hbins = range_list_new();
   if(rb->hbins == NULL)
   {
-    /* fprintf(stderr, "regfi_open: Failed to create HBIN list.\n"); */
-    close(fd);
+    /* fprintf(stderr, "regfi_alloc: Failed to create HBIN list.\n"); */
     talloc_free(rb);
     return NULL;
   }
@@ -1298,7 +1378,7 @@ REGFI_FILE* regfi_open(const char* filename)
 
 /******************************************************************************
  ******************************************************************************/
-int regfi_close(REGFI_FILE *file)
+int regfi_close(REGFI_FILE* file)
 {
   int fd;
 
@@ -1309,44 +1389,56 @@ int regfi_close(REGFI_FILE *file)
   fd = file->fd;
   file->fd = -1;
 
-  range_list_free(file->hbins);
+  regfi_free(file);
+
+  return close(fd);
+}
 
-  if(file->sk_cache != NULL)
-    lru_cache_destroy(file->sk_cache);
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_free(REGFI_FILE *file)
+{
+  if(file->last_message != NULL)
+    free(file->last_message);
 
   talloc_free(file);
-  return close(fd);
 }
 
 
 /******************************************************************************
- * There should be only *one* root key in the registry file based 
- * on my experience.  --jerry
+ * First checks the offset given by the file header, then checks the
+ * rest of the file if that fails.
  ******************************************************************************/
-REGFI_NK_REC* regfi_rootkey(REGFI_FILE *file)
+REGFI_NK_REC* regfi_rootkey(REGFI_FILE* file, REGFI_ENCODING output_encoding)
 {
   REGFI_NK_REC* nk = NULL;
   REGFI_HBIN* hbin;
-  uint32 root_offset, i, num_hbins;
+  uint32_t root_offset, i, num_hbins;
   
   if(!file)
     return NULL;
 
-  /* Scan through the file one HBIN block at a time looking 
-   * for an NK record with a root key type.
-   * This is typically the first NK record in the first HBIN
-   * block (but we're not assuming that generally).
+  root_offset = file->root_cell+REGFI_REGF_SIZE;
+  nk = regfi_load_key(file, root_offset, output_encoding, true);
+  if(nk != NULL)
+  {
+    if(nk->flags & REGFI_NK_FLAG_ROOT)
+      return nk;
+  }
+
+  regfi_add_message(file, REGFI_MSG_WARN, "File header indicated root key at"
+		    " location 0x%.8X, but no root key found."
+		    " Searching rest of file...", root_offset);
+  
+  /* If the file header gives bad info, scan through the file one HBIN
+   * block at a time looking for an NK record with a root key type.
    */
   num_hbins = range_list_size(file->hbins);
-  for(i=0; i < num_hbins; i++)
+  for(i=0; i < num_hbins && nk == NULL; i++)
   {
     hbin = (REGFI_HBIN*)range_list_get(file->hbins, i)->data;
-    if(regfi_find_root_nk(file, hbin->file_off+REGFI_HBIN_HEADER_SIZE, 
-			  hbin->block_size-REGFI_HBIN_HEADER_SIZE, &root_offset))
-    {
-      nk = regfi_load_key(file, root_offset, true);
-      break;
-    }
+    nk = regfi_find_root_nk(file, hbin, output_encoding);
   }
 
   return nk;
@@ -1383,14 +1475,25 @@ void regfi_subkeylist_free(REGFI_SUBKEY_LIST* list)
 
 /******************************************************************************
  *****************************************************************************/
-REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* fh)
+REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file, 
+				   REGFI_ENCODING output_encoding)
 {
   REGFI_NK_REC* root;
-  REGFI_ITERATOR* ret_val = talloc(NULL, REGFI_ITERATOR);
+  REGFI_ITERATOR* ret_val;
+
+  if(output_encoding != REGFI_ENCODING_UTF8
+     && output_encoding != REGFI_ENCODING_ASCII)
+  { 
+    regfi_add_message(file, REGFI_MSG_ERROR, "Invalid output_encoding supplied"
+		      " in creation of regfi iterator.");
+    return NULL;
+  }
+
+  ret_val = talloc(NULL, REGFI_ITERATOR);
   if(ret_val == NULL)
     return NULL;
 
-  root = regfi_rootkey(fh);
+  root = regfi_rootkey(file, output_encoding);
   if(root == NULL)
   {
     talloc_free(ret_val);
@@ -1405,11 +1508,12 @@ REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* fh)
   }
   talloc_steal(ret_val, ret_val->key_positions);
 
-  ret_val->f = fh;
+  ret_val->f = file;
   ret_val->cur_key = root;
   ret_val->cur_subkey = 0;
   ret_val->cur_value = 0;
-
+  ret_val->string_encoding = output_encoding;
+    
   return ret_val;
 }
 
@@ -1497,7 +1601,7 @@ bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
 {
   REGFI_NK_REC* subkey;
   bool found = false;
-  uint32 old_subkey = i->cur_subkey;
+  uint32_t old_subkey = i->cur_subkey;
 
   if(subkey_name == NULL)
     return false;
@@ -1531,7 +1635,7 @@ bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
  *****************************************************************************/
 bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
 {
-  uint32 x;
+  uint32_t x;
   if(path == NULL)
     return false;
 
@@ -1584,7 +1688,7 @@ REGFI_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
  *****************************************************************************/
 REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
 {
-  uint32 nk_offset;
+  uint32_t nk_offset;
 
   /* see if there is anything left to report */
   if (!(i->cur_key) || (i->cur_key->subkeys_off==REGFI_OFFSET_NONE)
@@ -1593,7 +1697,8 @@ REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
 
   nk_offset = i->cur_key->subkeys->elements[i->cur_subkey].offset;
 
-  return regfi_load_key(i->f, nk_offset+REGFI_REGF_SIZE, true);
+  return regfi_load_key(i->f, nk_offset+REGFI_REGF_SIZE, i->string_encoding, 
+			true);
 }
 
 
@@ -1620,6 +1725,7 @@ bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
 {
   REGFI_VK_REC* cur;
   bool found = false;
+  uint32_t old_value = i->cur_value;
 
   /* XXX: cur->valuename can be NULL in the registry.  
    *      Should we allow for a way to search for that? 
@@ -1639,8 +1745,15 @@ bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
       cur = regfi_iterator_next_value(i);
     }
   }
+  
+  if(found == false)
+  {
+    i->cur_value = old_value;
+    return false;
+  }
 
-  return found;
+  regfi_free_value(cur);
+  return true;
 }
 
 
@@ -1658,14 +1771,15 @@ REGFI_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
 REGFI_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
 {
   REGFI_VK_REC* ret_val = NULL;
-  uint32 voffset;
+  uint32_t voffset;
 
   if(i->cur_key->values != NULL && i->cur_key->values->elements != NULL)
   {
     if(i->cur_value < i->cur_key->values->num_values)
     {
       voffset = i->cur_key->values->elements[i->cur_value];
-      ret_val = regfi_load_value(i->f, voffset+REGFI_REGF_SIZE, true);
+      ret_val = regfi_load_value(i->f, voffset+REGFI_REGF_SIZE, 
+				 i->string_encoding, true);
     }
   }
 
@@ -1688,13 +1802,368 @@ REGFI_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
 }
 
 
+/******************************************************************************
+ *****************************************************************************/
+REGFI_CLASSNAME* regfi_iterator_fetch_classname(REGFI_ITERATOR* i, 
+						const REGFI_NK_REC* key)
+{
+  REGFI_CLASSNAME* ret_val;
+  uint8_t* raw;
+  char* interpreted;
+  uint32_t offset;
+  int32_t conv_size, max_size;
+  uint16_t parse_length;
+
+  if(key->classname_off == REGFI_OFFSET_NONE || key->classname_length == 0)
+    return NULL;
+
+  offset = key->classname_off + REGFI_REGF_SIZE;
+  max_size = regfi_calc_maxsize(i->f, offset);
+  if(max_size <= 0)
+    return NULL;
+
+  parse_length = key->classname_length;
+  raw = regfi_parse_classname(i->f, offset, &parse_length, max_size, true);
+  
+  if(raw == NULL)
+  {
+    regfi_add_message(i->f, REGFI_MSG_WARN, "Could not parse class"
+		      " name at offset 0x%.8X for key record at offset 0x%.8X.",
+		      offset, key->offset);
+    return NULL;
+  }
+
+  ret_val = talloc(NULL, REGFI_CLASSNAME);
+  if(ret_val == NULL)
+    return NULL;
+
+  ret_val->raw = raw;
+  ret_val->size = parse_length;
+  talloc_steal(ret_val, raw);
+
+  interpreted = talloc_array(NULL, char, parse_length);
+
+  conv_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+				 regfi_encoding_int2str(i->string_encoding),
+				 raw, interpreted,
+				 parse_length, parse_length);
+  if(conv_size < 0)
+  {
+    regfi_add_message(i->f, REGFI_MSG_WARN, "Error occurred while"
+		      " converting classname to charset %s.  Error message: %s",
+		      i->string_encoding, strerror(-conv_size));
+    talloc_free(interpreted);
+    ret_val->interpreted = NULL;
+  }
+  else
+  {
+    interpreted = talloc_realloc(NULL, interpreted, char, conv_size);
+    ret_val->interpreted = interpreted;
+    talloc_steal(ret_val, interpreted);
+  }
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_DATA* regfi_iterator_fetch_data(REGFI_ITERATOR* i, 
+				      const REGFI_VK_REC* value)
+{
+  REGFI_DATA* ret_val = NULL;
+  REGFI_BUFFER raw_data;
+
+  if(value->data_size != 0)
+  {
+    raw_data = regfi_load_data(i->f, value->data_off, value->data_size,
+			      value->data_in_offset, true);
+    if(raw_data.buf == NULL)
+    {
+      regfi_add_message(i->f, REGFI_MSG_WARN, "Could not parse data record"
+			" while parsing VK record at offset 0x%.8X.",
+			value->offset);
+    }
+    else
+    {
+      ret_val = regfi_buffer_to_data(raw_data);
+
+      if(ret_val == NULL)
+      {
+	regfi_add_message(i->f, REGFI_MSG_WARN, "Error occurred in converting"
+			  " data buffer to data structure while interpreting "
+			  "data for VK record at offset 0x%.8X.",
+			  value->offset);
+	talloc_free(raw_data.buf);
+	return NULL;
+      }
+
+      if(!regfi_interpret_data(i->f, i->string_encoding, value->type, ret_val))
+      {
+	regfi_add_message(i->f, REGFI_MSG_INFO, "Error occurred while"
+			  " interpreting data for VK record at offset 0x%.8X.",
+			  value->offset);
+      }
+    }
+  }
+  
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+void regfi_free_classname(REGFI_CLASSNAME* classname)
+{
+  talloc_free(classname);
+}
+
+/******************************************************************************
+ *****************************************************************************/
+void regfi_free_data(REGFI_DATA* data)
+{
+  talloc_free(data);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data)
+{
+  REGFI_DATA* ret_val;
+
+  if(raw_data.buf == NULL)
+    return NULL;
+
+  ret_val = talloc(NULL, REGFI_DATA);
+  if(ret_val == NULL)
+    return NULL;
+  
+  talloc_steal(ret_val, raw_data.buf);
+  ret_val->raw = raw_data.buf;
+  ret_val->size = raw_data.len;
+  ret_val->interpreted_size = 0;
+  ret_val->interpreted.qword = 0;
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_interpret_data(REGFI_FILE* file, REGFI_ENCODING string_encoding,
+			  uint32_t type, REGFI_DATA* data)
+{
+  uint8_t** tmp_array;
+  uint8_t* tmp_str;
+  int32_t tmp_size;
+  uint32_t i, j, array_size;
+
+  if(data == NULL)
+    return false;
+
+  switch (type)
+  {
+  case REG_SZ:
+  case REG_EXPAND_SZ:
+  /* REG_LINK is a symbolic link, stored as a unicode string. */
+  case REG_LINK:
+    tmp_str = talloc_array(NULL, uint8_t, data->size);
+    if(tmp_str == NULL)
+    {
+      data->interpreted.string = NULL;
+      data->interpreted_size = 0;
+      return false;
+    }
+      
+    tmp_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+				  regfi_encoding_int2str(string_encoding),
+				  data->raw, (char*)tmp_str, 
+				  data->size, data->size);
+    if(tmp_size < 0)
+    {
+      regfi_add_message(file, REGFI_MSG_INFO, "Error occurred while"
+			" converting data of type %d to %s.  Error message: %s",
+			type, string_encoding, strerror(-tmp_size));
+      talloc_free(tmp_str);
+      data->interpreted.string = NULL;
+      data->interpreted_size = 0;
+      return false;
+    }
+
+    tmp_str = talloc_realloc(NULL, tmp_str, uint8_t, tmp_size);
+    data->interpreted.string = tmp_str;
+    data->interpreted_size = tmp_size;
+    talloc_steal(data, tmp_str);
+    break;
+
+  case REG_DWORD:
+    if(data->size < 4)
+    {
+      data->interpreted.dword = 0;
+      data->interpreted_size = 0;
+      return false;
+    }
+    data->interpreted.dword = IVAL(data->raw, 0);
+    data->interpreted_size = 4;
+    break;
+
+  case REG_DWORD_BE:
+    if(data->size < 4)
+    {
+      data->interpreted.dword_be = 0;
+      data->interpreted_size = 0;
+      return false;
+    }
+    data->interpreted.dword_be = RIVAL(data->raw, 0);
+    data->interpreted_size = 4;
+    break;
+
+  case REG_QWORD:
+    if(data->size < 8)
+    {
+      data->interpreted.qword = 0;
+      data->interpreted_size = 0;
+      return false;
+    }
+    data->interpreted.qword = 
+      (uint64_t)IVAL(data->raw, 0) + (((uint64_t)IVAL(data->raw, 4))<<32);
+    data->interpreted_size = 8;
+    break;
+    
+  case REG_MULTI_SZ:
+    tmp_str = talloc_array(NULL, uint8_t, data->size);
+    if(tmp_str == NULL)
+    {
+      data->interpreted.multiple_string = NULL;
+      data->interpreted_size = 0;
+      return false;
+    }
+
+    /* Attempt to convert entire string from UTF-16LE to output encoding,
+     * then parse and quote fields individually.
+     */
+    tmp_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+				  regfi_encoding_int2str(string_encoding),
+				  data->raw, (char*)tmp_str,
+				  data->size, data->size);
+    if(tmp_size < 0)
+    {
+      regfi_add_message(file, REGFI_MSG_INFO, "Error occurred while"
+			" converting data of type %d to %s.  Error message: %s",
+			type, string_encoding, strerror(-tmp_size));
+      talloc_free(tmp_str);
+      data->interpreted.multiple_string = NULL;
+      data->interpreted_size = 0;
+      return false;
+    }
+
+    array_size = tmp_size+1;
+    tmp_array = talloc_array(NULL, uint8_t*, array_size);
+    if(tmp_array == NULL)
+    {
+      talloc_free(tmp_str);
+      data->interpreted.string = NULL;
+      data->interpreted_size = 0;
+      return false;
+    }
+    
+    tmp_array[0] = tmp_str;
+    for(i=0,j=1; i < tmp_size && j < array_size-1; i++)
+    {
+      if(tmp_str[i] == '\0' && (i+1 < tmp_size))
+	tmp_array[j++] = tmp_str+i+1;
+    }
+    tmp_array[j] = NULL;
+    tmp_array = talloc_realloc(NULL, tmp_array, uint8_t*, j+1);
+    data->interpreted.multiple_string = tmp_array;
+    /* XXX: how meaningful is this?  should we store number of strings instead? */
+    data->interpreted_size = tmp_size;
+    talloc_steal(tmp_array, tmp_str);
+    talloc_steal(data, tmp_array);
+    break;
+
+  /* XXX: Dont know how to interpret these yet, just treat as binary */
+  case REG_NONE:
+    data->interpreted.none = data->raw;
+    data->interpreted_size = data->size;
+    break;
+
+  case REG_RESOURCE_LIST:
+    data->interpreted.resource_list = data->raw;
+    data->interpreted_size = data->size;
+    break;
+
+  case REG_FULL_RESOURCE_DESCRIPTOR:
+    data->interpreted.full_resource_descriptor = data->raw;
+    data->interpreted_size = data->size;
+    break;
+
+  case REG_RESOURCE_REQUIREMENTS_LIST:
+    data->interpreted.resource_requirements_list = data->raw;
+    data->interpreted_size = data->size;
+    break;
+
+  case REG_BINARY:
+    data->interpreted.binary = data->raw;
+    data->interpreted_size = data->size;
+    break;
+
+  default:
+    data->interpreted.qword = 0;
+    data->interpreted_size = 0;
+    return false;
+  }
+
+  data->type = type;
+  return true;
+}
+
+
+/******************************************************************************
+ * Convert from UTF-16LE to specified character set. 
+ * On error, returns a negative errno code.
+ *****************************************************************************/
+int32_t regfi_conv_charset(const char* input_charset, const char* output_charset,
+			 uint8_t* input, char* output, 
+			 uint32_t input_len, uint32_t output_max)
+{
+  iconv_t conv_desc;
+  char* inbuf = (char*)input;
+  char* outbuf = output;
+  size_t in_len = (size_t)input_len;
+  size_t out_len = (size_t)(output_max-1);
+  int ret;
+
+  /* XXX: Consider creating a couple of conversion descriptors earlier,
+   *      storing them on an iterator so they don't have to be recreated
+   *      each time.
+   */
+
+  /* Set up conversion descriptor. */
+  conv_desc = iconv_open(output_charset, input_charset);
+
+  ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
+  if(ret == -1)
+  {
+    iconv_close(conv_desc);
+    return -errno;
+  }
+  *outbuf = '\0';
+
+  iconv_close(conv_desc);  
+  return output_max-out_len-1;
+}
+
+
+
 /*******************************************************************
  * Computes the checksum of the registry file header.
- * buffer must be at least the size of an regf header (4096 bytes).
+ * buffer must be at least the size of a regf header (4096 bytes).
  *******************************************************************/
-static uint32 regfi_compute_header_checksum(uint8* buffer)
+static uint32_t regfi_compute_header_checksum(uint8_t* buffer)
 {
-  uint32 checksum, x;
+  uint32_t checksum, x;
   int i;
 
   /* XOR of all bytes 0x0000 - 0x01FB */
@@ -1715,8 +2184,8 @@ static uint32 regfi_compute_header_checksum(uint8* buffer)
  *******************************************************************/
 REGFI_FILE* regfi_parse_regf(int fd, bool strict)
 {
-  uint8 file_header[REGFI_REGF_SIZE];
-  uint32 length;
+  uint8_t file_header[REGFI_REGF_SIZE];
+  uint32_t length;
   REGFI_FILE* ret_val;
 
   ret_val = talloc(NULL, REGFI_FILE);
@@ -1791,11 +2260,11 @@ REGFI_FILE* regfi_parse_regf(int fd, bool strict)
  * Given real file offset, read and parse the hbin at that location
  * along with it's associated cells.
  ******************************************************************************/
-REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32 offset, bool strict)
+REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, bool strict)
 {
   REGFI_HBIN *hbin;
-  uint8 hbin_header[REGFI_HBIN_HEADER_SIZE];
-  uint32 length;
+  uint8_t hbin_header[REGFI_HBIN_HEADER_SIZE];
+  uint32_t length;
   
   if(offset >= file->file_length)
     return NULL;
@@ -1863,14 +2332,12 @@ REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32 offset, bool strict)
 
 /*******************************************************************
  *******************************************************************/
-REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset, 
-			    uint32 max_size, bool strict)
+REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset, 
+			     uint32_t max_size, bool strict)
 {
-  uint8 nk_header[REGFI_NK_MIN_LENGTH];
-  const REGFI_HBIN *hbin;
+  uint8_t nk_header[REGFI_NK_MIN_LENGTH];
   REGFI_NK_REC* ret_val;
-  uint32 length,cell_length;
-  uint32 class_offset, class_maxsize;
+  uint32_t length,cell_length;
   bool unalloc = false;
 
   if(!regfi_parse_cell(file->fd, offset, nk_header, REGFI_NK_MIN_LENGTH,
@@ -1905,7 +2372,7 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
   if(ret_val->cell_size > max_size)
     ret_val->cell_size = max_size & 0xFFFFFFF8;
   if((ret_val->cell_size < REGFI_NK_MIN_LENGTH) 
-     || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
+     || (strict && (ret_val->cell_size & 0x00000007) != 0))
   {
     regfi_add_message(file, REGFI_MSG_WARN, "A length check failed while"
 		      " parsing NK record at offset 0x%.8X.", offset);
@@ -1915,13 +2382,13 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
 
   ret_val->magic[0] = nk_header[0x0];
   ret_val->magic[1] = nk_header[0x1];
-  ret_val->key_type = SVAL(nk_header, 0x2);
+  ret_val->flags = SVAL(nk_header, 0x2);
   
-  if((ret_val->key_type & ~REGFI_NK_KNOWN_FLAGS) != 0)
+  if((ret_val->flags & ~REGFI_NK_KNOWN_FLAGS) != 0)
   {
     regfi_add_message(file, REGFI_MSG_WARN, "Unknown key flags (0x%.4X) while"
 		      " parsing NK record at offset 0x%.8X.", 
-		      (ret_val->key_type & ~REGFI_NK_KNOWN_FLAGS), offset);
+		      (ret_val->flags & ~REGFI_NK_KNOWN_FLAGS), offset);
   }
 
   ret_val->mtime.low = IVAL(nk_header, 0x4);
@@ -1956,6 +2423,7 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
 
   ret_val->name_length = SVAL(nk_header, 0x48);
   ret_val->classname_length = SVAL(nk_header, 0x4A);
+  ret_val->keyname = NULL;
 
   if(ret_val->name_length + REGFI_NK_MIN_LENGTH > ret_val->cell_size)
   {
@@ -1981,8 +2449,8 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
       ret_val->cell_size = length;
   }
 
-  ret_val->keyname = talloc_array(ret_val, char, ret_val->name_length+1);
-  if(ret_val->keyname == NULL)
+  ret_val->keyname_raw = talloc_array(ret_val, uint8_t, ret_val->name_length);
+  if(ret_val->keyname_raw == NULL)
   {
     talloc_free(ret_val);
     return NULL;
@@ -1990,7 +2458,7 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
 
   /* Don't need to seek, should be at the right offset */
   length = ret_val->name_length;
-  if((regfi_read(file->fd, (uint8*)ret_val->keyname, &length) != 0)
+  if((regfi_read(file->fd, (uint8_t*)ret_val->keyname_raw, &length) != 0)
      || length != ret_val->name_length)
   {
     regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read key name"
@@ -1998,52 +2466,21 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
     talloc_free(ret_val);
     return NULL;
   }
-  ret_val->keyname[ret_val->name_length] = '\0';
-
-  /* XXX: This linking should be moved up to regfi_load_key */
-  if(ret_val->classname_off != REGFI_OFFSET_NONE)
-  {
-    hbin = regfi_lookup_hbin(file, ret_val->classname_off);
-    if(hbin)
-    {
-      class_offset = ret_val->classname_off+REGFI_REGF_SIZE;
-      class_maxsize = hbin->block_size + hbin->file_off - class_offset;
-      ret_val->classname
-	= regfi_parse_classname(file, class_offset, &ret_val->classname_length, 
-				class_maxsize, strict);
-    }
-    else
-    {
-      ret_val->classname = NULL;
-      regfi_add_message(file, REGFI_MSG_WARN, "Could not find hbin for class"
-			" name while parsing NK record at offset 0x%.8X.", 
-			offset);
-    }
-
-    if(ret_val->classname == NULL)
-    {
-      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse class"
-			" name while parsing NK record at offset 0x%.8X.", 
-			offset);
-    }
-    else
-      talloc_steal(ret_val, ret_val->classname);
-  }
 
   return ret_val;
 }
 
 
-char* regfi_parse_classname(REGFI_FILE* file, uint32 offset, 
-			    uint16* name_length, uint32 max_size, bool strict)
+uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset, 
+			     uint16_t* name_length, uint32_t max_size, bool strict)
 {
-  char* ret_val = NULL;
-  uint32 length;
-  uint32 cell_length;
+  uint8_t* ret_val = NULL;
+  uint32_t length;
+  uint32_t cell_length;
   bool unalloc = false;
 
   if(*name_length > 0 && offset != REGFI_OFFSET_NONE 
-     && offset == (offset & 0xFFFFFFF8))
+     && (offset & 0x00000007) == 0)
   {
     if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
     {
@@ -2052,7 +2489,7 @@ char* regfi_parse_classname(REGFI_FILE* file, uint32 offset,
 	return NULL;
     }
 
-    if((cell_length & 0xFFFFFFF8) != cell_length)
+    if((cell_length & 0x0000007) != 0)
     {
       regfi_add_message(file, REGFI_MSG_ERROR, "Cell length not a multiple of 8"
 			" while parsing class name at offset 0x%.8X.", offset);
@@ -2079,11 +2516,11 @@ char* regfi_parse_classname(REGFI_FILE* file, uint32 offset,
       *name_length = cell_length - 4;
     }
     
-    ret_val = talloc_array(NULL, char, *name_length);
+    ret_val = talloc_array(NULL, uint8_t, *name_length);
     if(ret_val != NULL)
     {
       length = *name_length;
-      if((regfi_read(file->fd, (uint8*)ret_val, &length) != 0)
+      if((regfi_read(file->fd, ret_val, &length) != 0)
 	 || length != *name_length)
       {
 	regfi_add_message(file, REGFI_MSG_ERROR, "Could not read class name"
@@ -2100,12 +2537,12 @@ char* regfi_parse_classname(REGFI_FILE* file, uint32 offset,
 
 /******************************************************************************
 *******************************************************************************/
-REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset, 
-			     uint32 max_size, bool strict)
+REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset, 
+			     uint32_t max_size, bool strict)
 {
   REGFI_VK_REC* ret_val;
-  uint8 vk_header[REGFI_VK_MIN_LENGTH];
-  uint32 raw_data_size, length, cell_length;
+  uint8_t vk_header[REGFI_VK_MIN_LENGTH];
+  uint32_t raw_data_size, length, cell_length;
   bool unalloc = false;
 
   if(!regfi_parse_cell(file->fd, offset, vk_header, REGFI_VK_MIN_LENGTH,
@@ -2122,13 +2559,13 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset,
 
   ret_val->offset = offset;
   ret_val->cell_size = cell_length;
-  ret_val->data = NULL;
   ret_val->valuename = NULL;
+  ret_val->valuename_raw = NULL;
   
   if(ret_val->cell_size > max_size)
     ret_val->cell_size = max_size & 0xFFFFFFF8;
   if((ret_val->cell_size < REGFI_VK_MIN_LENGTH) 
-     || ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8))
+     || (ret_val->cell_size & 0x00000007) != 0)
   {
     regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size encountered"
 		      " while parsing VK record at offset 0x%.8X.", offset);
@@ -2153,13 +2590,16 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset,
   ret_val->name_length = SVAL(vk_header, 0x2);
   raw_data_size = IVAL(vk_header, 0x4);
   ret_val->data_size = raw_data_size & ~REGFI_VK_DATA_IN_OFFSET;
+  /* The data is typically stored in the offset if the size <= 4, 
+   * in which case this flag is set. 
+   */
   ret_val->data_in_offset = (bool)(raw_data_size & REGFI_VK_DATA_IN_OFFSET);
   ret_val->data_off = IVAL(vk_header, 0x8);
   ret_val->type = IVAL(vk_header, 0xC);
-  ret_val->flag = SVAL(vk_header, 0x10);
+  ret_val->flags = SVAL(vk_header, 0x10);
   ret_val->unknown1 = SVAL(vk_header, 0x12);
 
-  if(ret_val->flag & REGFI_VK_FLAG_NAME_PRESENT)
+  if(ret_val->name_length > 0)
   {
     if(ret_val->name_length + REGFI_VK_MIN_LENGTH + 4 > ret_val->cell_size)
     {
@@ -2180,15 +2620,15 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset,
     if(cell_length < ret_val->name_length + REGFI_VK_MIN_LENGTH + 4)
       cell_length+=8;
 
-    ret_val->valuename = talloc_array(ret_val, char, ret_val->name_length+1);
-    if(ret_val->valuename == NULL)
+    ret_val->valuename_raw = talloc_array(ret_val, uint8_t, ret_val->name_length);
+    if(ret_val->valuename_raw == NULL)
     {
       talloc_free(ret_val);
       return NULL;
     }
 
     length = ret_val->name_length;
-    if((regfi_read(file->fd, (uint8*)ret_val->valuename, &length) != 0)
+    if((regfi_read(file->fd, (uint8_t*)ret_val->valuename_raw, &length) != 0)
        || length != ret_val->name_length)
     {
       regfi_add_message(file, REGFI_MSG_ERROR, "Could not read value name"
@@ -2196,8 +2636,6 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset,
       talloc_free(ret_val);
       return NULL;
     }
-    ret_val->valuename[ret_val->name_length] = '\0';
-
   }
   else
     cell_length = REGFI_VK_MIN_LENGTH + 4;
@@ -2214,37 +2652,50 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset,
 
 
 /******************************************************************************
-*******************************************************************************/
-REGFI_BUFFER regfi_load_data(REGFI_FILE* file, 
-			     uint32 data_type, uint32 offset,
-			     uint32 length, uint32 max_size, 
-			     bool data_in_offset, bool strict)
+ *
+ ******************************************************************************/
+REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
+			     uint32_t length, bool data_in_offset,
+			     bool strict)
 {
   REGFI_BUFFER ret_val;
-  uint32 read_length, cell_length;
-  uint8 i;
+  uint32_t cell_length, offset;
+  int32_t max_size;
   bool unalloc;
   
-  /* The data is typically stored in the offset if the size <= 4 */
+  /* Microsoft's documentation indicates that "available memory" is 
+   * the limit on value sizes for the more recent registry format version.
+   * This is not only annoying, but it's probably also incorrect, since clearly
+   * value data sizes are limited to 2^31 (high bit used as a flag) and even 
+   * with big data records, the apparent max size is:
+   *   16344 * 2^16 = 1071104040 (~1GB).
+   *
+   * We choose to limit it to 1M which was the limit in older versions and 
+   * should rarely be exceeded unless the file is corrupt or malicious. 
+   * For more info, see:
+   *   http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+   */
+  /* XXX: add way to skip this check at user discression. */
+  if(length > REGFI_VK_MAX_DATA_LENGTH)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Value data size %d larger than "
+		      "%d, truncating...", length, REGFI_VK_MAX_DATA_LENGTH);
+    length = REGFI_VK_MAX_DATA_LENGTH;
+  }
+
   if(data_in_offset)
+    return regfi_parse_little_data(file, voffset, length, strict);
+  else
   {
-    if(length > 4)
+    offset = voffset + REGFI_REGF_SIZE;
+    max_size = regfi_calc_maxsize(file, offset);
+    if(max_size < 0)
     {
-      regfi_add_message(file, REGFI_MSG_ERROR, "Data in offset but length > 4"
-			" while parsing data record at offset 0x%.8X.", 
-			offset);
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not find HBIN for data"
+			" at offset 0x%.8X.", offset);
       goto fail;
     }
-
-    if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
-      goto fail;
-    ret_val.len = length;
-
-    for(i = 0; i < length; i++)
-      ret_val.buf[i] = (uint8)((offset >> i*8) & 0xFF);
-  }
-  else
-  {
+    
     if(!regfi_parse_cell(file->fd, offset, NULL, 0,
 			 &cell_length, &unalloc))
     {
@@ -2253,7 +2704,7 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file,
       goto fail;
     }
 
-    if((cell_length & 0xFFFFFFF8) != cell_length)
+    if((cell_length & 0x00000007) != 0)
     {
       regfi_add_message(file, REGFI_MSG_WARN, "Cell length not multiple of 8"
 			" while parsing data record at offset 0x%.8X.",
@@ -2266,10 +2717,7 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file,
       regfi_add_message(file, REGFI_MSG_WARN, "Cell extends past HBIN boundary"
 			" while parsing data record at offset 0x%.8X.",
 			offset);
-      if(strict)
-	goto fail;
-      else
-	cell_length = max_size;
+      goto fail;
     }
 
     if(cell_length - 4 < length)
@@ -2281,7 +2729,8 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file,
       if (file->major_version >= 1 && file->minor_version >= 5)
       {
 	/* Attempt to parse a big data record */
-	return regfi_load_big_data(file, offset, length, cell_length, strict);
+	return regfi_load_big_data(file, offset, length, cell_length, 
+				   NULL, strict);
       }
       else
       {
@@ -2296,19 +2745,7 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file,
       }
     }
 
-    if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
-      goto fail;
-    ret_val.len = length;
-
-    read_length = length;
-    if((regfi_read(file->fd, ret_val.buf, &read_length) != 0)
-       || read_length != length)
-    {
-      regfi_add_message(file, REGFI_MSG_ERROR, "Could not read data block while"
-			" parsing data record at offset 0x%.8X.", offset);
-      talloc_free(ret_val.buf);
-      goto fail;
-    }
+    ret_val = regfi_parse_data(file, offset, length, strict);
   }
 
   return ret_val;
@@ -2321,117 +2758,348 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file,
 
 
 /******************************************************************************
+ * Parses the common case data records stored in a single cell.
+ ******************************************************************************/
+REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset,
+			      uint32_t length, bool strict)
+{
+  REGFI_BUFFER ret_val;
+  uint32_t read_length;
+
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+  
+  if(lseek(file->fd, offset+4, SEEK_SET) == -1)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not seek while "
+		      "reading data at offset 0x%.8X.", offset);
+    return ret_val;
+  }
+
+  if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
+    return ret_val;
+  ret_val.len = length;
+  
+  read_length = length;
+  if((regfi_read(file->fd, ret_val.buf, &read_length) != 0)
+     || read_length != length)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Could not read data block while"
+		      " parsing data record at offset 0x%.8X.", offset);
+    talloc_free(ret_val.buf);
+    ret_val.buf = NULL;
+    ret_val.buf = 0;
+  }
+
+  return ret_val;
+}
+
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset,
+				     uint32_t length, bool strict)
+{
+  uint8_t i;
+  REGFI_BUFFER ret_val;
+
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+
+  if(length > 4)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Data in offset but length > 4"
+		      " while parsing data record. (voffset=0x%.8X, length=%d)",
+		      voffset, length);
+    return ret_val;
+  }
+
+  if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
+    return ret_val;
+  ret_val.len = length;
+  
+  for(i = 0; i < length; i++)
+    ret_val.buf[i] = (uint8_t)((voffset >> i*8) & 0xFF);
+
+  return ret_val;
+}
+
+/******************************************************************************
 *******************************************************************************/
-REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, 
-				 uint32 offset, uint32 data_length, 
-				 uint32 cell_length, bool strict)
+REGFI_BUFFER regfi_parse_big_data_header(REGFI_FILE* file, uint32_t offset, 
+					 uint32_t max_size, bool strict)
 {
   REGFI_BUFFER ret_val;
-  uint16 num_chunks, i;
-  uint32 indirect_offset, indirect_length, chunk_length, chunk_offset;
-  uint32 read_length, data_left;
+  uint32_t cell_length;
   bool unalloc;
-  uint32* indirect_ptrs;
-  uint8* big_data_cell = (uint8*)malloc(cell_length*sizeof(uint8));
-  if(big_data_cell == NULL)
+
+  /* XXX: do something with unalloc? */
+  ret_val.buf = (uint8_t*)talloc_array(NULL, uint8_t, REGFI_BIG_DATA_MIN_LENGTH);
+  if(ret_val.buf == NULL)
+    goto fail;
+
+  if(REGFI_BIG_DATA_MIN_LENGTH > max_size)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Big data header exceeded max_size "
+		      "while parsing big data header at offset 0x%.8X.",offset);
     goto fail;
+  }
 
-  if(!regfi_parse_cell(file->fd, offset, big_data_cell, cell_length-4,
+  if(!regfi_parse_cell(file->fd, offset, ret_val.buf, REGFI_BIG_DATA_MIN_LENGTH,
 		       &cell_length, &unalloc))
   {
     regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
-		      " parsing big data record at offset 0x%.8X.", offset);
-    free(big_data_cell);
+		      " parsing big data header at offset 0x%.8X.", offset);
     goto fail;
   }
-  
-  if((big_data_cell[0] != 'd') || (big_data_cell[1] != 'b'))
+
+  if((ret_val.buf[0] != 'd') || (ret_val.buf[1] != 'b'))
   {
     regfi_add_message(file, REGFI_MSG_WARN, "Unknown magic number"
 		      " (0x%.2X, 0x%.2X) encountered while parsing"
-		      " big data record at offset 0x%.8X.", 
-		      big_data_cell[0], big_data_cell[1], offset);
-    free(big_data_cell);
+		      " big data header at offset 0x%.8X.", 
+		      ret_val.buf[0], ret_val.buf[1], offset);
     goto fail;
   }
-  num_chunks = SVAL(big_data_cell, 0x2);
-  /* XXX: Should check sanity of pointers here and in the indirect
-   *      block.  At least ensure they are a multiple of 8. 
-   */
-  indirect_offset = IVAL(big_data_cell, 0x4) + REGFI_REGF_SIZE;
-  free(big_data_cell);
 
-  indirect_ptrs = (uint32*)malloc(num_chunks*sizeof(uint32));
-  if(indirect_ptrs == NULL)
+  ret_val.len = REGFI_BIG_DATA_MIN_LENGTH;
+  return ret_val;
+
+ fail:
+  if(ret_val.buf != NULL)
+  {
+    talloc_free(ret_val.buf);
+    ret_val.buf = NULL;
+  }
+  ret_val.len = 0;
+  return ret_val;
+}
+
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+uint32_t* regfi_parse_big_data_indirect(REGFI_FILE* file, uint32_t offset,
+				      uint16_t num_chunks, bool strict)
+{
+  uint32_t* ret_val;
+  uint32_t indirect_length;
+  int32_t max_size;
+  uint16_t i;
+  bool unalloc;
+
+  /* XXX: do something with unalloc? */
+
+  max_size = regfi_calc_maxsize(file, offset);
+  if((max_size < 0) || (num_chunks*sizeof(uint32_t) + 4 > max_size))
+    return NULL;
+
+  ret_val = (uint32_t*)talloc_array(NULL, uint32_t, num_chunks);
+  if(ret_val == NULL)
     goto fail;
 
-  if(!regfi_parse_cell(file->fd, indirect_offset, (uint8*)indirect_ptrs,
-		       num_chunks*sizeof(uint32),
+  if(!regfi_parse_cell(file->fd, offset, (uint8_t*)ret_val,
+		       num_chunks*sizeof(uint32_t),
 		       &indirect_length, &unalloc))
   {
     regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
 		      " parsing big data indirect record at offset 0x%.8X.", 
 		      offset);
-    free(indirect_ptrs);
     goto fail;
   }
-  
-  if((ret_val.buf = talloc_array(NULL, uint8_t, data_length)) == NULL)
+
+  /* Convert pointers to proper endianess, verify they are aligned. */
+  for(i=0; i<num_chunks; i++)
   {
-    free(indirect_ptrs);
-    goto fail;
+    ret_val[i] = IVAL(ret_val, i*sizeof(uint32_t));
+    if((ret_val[i] & 0x00000007) != 0)
+      goto fail;
   }
-  data_left = data_length;
+  
+  return ret_val;
 
-  for(i=0; (i<num_chunks) && (data_left>0); i++)
+ fail:
+  if(ret_val != NULL)
+    talloc_free(ret_val);
+  return NULL;
+}
+
+
+/******************************************************************************
+ * Arguments:
+ *  file       --
+ *  offsets    -- list of virtual offsets.
+ *  num_chunks -- 
+ *  strict     --
+ *
+ * Returns:
+ *  A range_list with physical offsets and complete lengths 
+ *  (including cell headers) of associated cells.  
+ *  No data in range_list elements.
+ ******************************************************************************/
+range_list* regfi_parse_big_data_cells(REGFI_FILE* file, uint32_t* offsets,
+				       uint16_t num_chunks, bool strict)
+{
+  uint32_t cell_length, chunk_offset;
+  range_list* ret_val;
+  uint16_t i;
+  bool unalloc;
+  
+  /* XXX: do something with unalloc? */
+  ret_val = range_list_new();
+  if(ret_val == NULL)
+    goto fail;
+  
+  for(i=0; i<num_chunks; i++)
   {
-    /* Fix endianness of pointer and convert to physical offset */
-    chunk_offset = IVAL(indirect_ptrs, i*4) + REGFI_REGF_SIZE;
-    if(!regfi_parse_cell(file->fd, chunk_offset, NULL, 0, 
-			 &chunk_length, &unalloc))
+    chunk_offset = offsets[i]+REGFI_REGF_SIZE;
+    if(!regfi_parse_cell(file->fd, chunk_offset, NULL, 0,
+			 &cell_length, &unalloc))
     {
       regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
 			" parsing big data chunk at offset 0x%.8X.", 
 			chunk_offset);
-      if(strict)
-	goto chunk_fail;
-      else
-	break;
+      goto fail;
     }
 
-    /* XXX: This should be "chunk_length-4" to account for the 4 byte cell 
+    if(!range_list_add(ret_val, chunk_offset, cell_length, NULL))
+      goto fail;
+  }
+
+  return ret_val;
+
+ fail:
+  if(ret_val != NULL)
+    range_list_free(ret_val);
+  return NULL;
+}
+
+
+/******************************************************************************
+*******************************************************************************/
+REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, 
+				 uint32_t offset, uint32_t data_length, 
+				 uint32_t cell_length, range_list* used_ranges,
+				 bool strict)
+{
+  REGFI_BUFFER ret_val;
+  uint16_t num_chunks, i;
+  uint32_t read_length, data_left, tmp_len, indirect_offset;
+  uint32_t* indirect_ptrs = NULL;
+  REGFI_BUFFER bd_header;
+  range_list* bd_cells = NULL;
+  const range_list_element* cell_info;
+
+  ret_val.buf = NULL;
+
+  /* XXX: Add better error/warning messages */
+
+  bd_header = regfi_parse_big_data_header(file, offset, cell_length, strict);
+  if(bd_header.buf == NULL)
+    goto fail;
+
+  /* Keep track of used space for use by reglookup-recover */
+  if(used_ranges != NULL)
+    if(!range_list_add(used_ranges, offset, cell_length, NULL))
+      goto fail;
+
+  num_chunks = SVAL(bd_header.buf, 0x2);
+  indirect_offset = IVAL(bd_header.buf, 0x4) + REGFI_REGF_SIZE;
+  talloc_free(bd_header.buf);
+
+  indirect_ptrs = regfi_parse_big_data_indirect(file, indirect_offset,
+						num_chunks, strict);
+  if(indirect_ptrs == NULL)
+    goto fail;
+
+  if(used_ranges != NULL)
+    if(!range_list_add(used_ranges, indirect_offset, num_chunks*4+4, NULL))
+      goto fail;
+  
+  if((ret_val.buf = talloc_array(NULL, uint8_t, data_length)) == NULL)
+    goto fail;
+  data_left = data_length;
+
+  bd_cells = regfi_parse_big_data_cells(file, indirect_ptrs, num_chunks, strict);
+  if(bd_cells == NULL)
+    goto fail;
+
+  talloc_free(indirect_ptrs);
+  indirect_ptrs = NULL;
+  
+  for(i=0; (i<num_chunks) && (data_left>0); i++)
+  {
+    cell_info = range_list_get(bd_cells, i);
+    if(cell_info == NULL)
+      goto fail;
+
+    /* XXX: This should be "cell_info->length-4" to account for the 4 byte cell
      *      length.  However, it has been observed that some (all?) chunks
      *      have an additional 4 bytes of 0 at the end of their cells that 
      *      isn't part of the data, so we're trimming that off too. 
+     *      Perhaps it's just an 8 byte alignment requirement...
      */
-    if(chunk_length-8 >= data_left)
+    if(cell_info->length - 8 >= data_left)
+    {
+      if(i+1 != num_chunks)
+      {
+	regfi_add_message(file, REGFI_MSG_WARN, "Left over chunks detected "
+			  "while constructing big data at offset 0x%.8X "
+			  "(chunk offset 0x%.8X).", offset, cell_info->offset);
+      }
       read_length = data_left;
+    }
     else
-      read_length = chunk_length-8;
+      read_length = cell_info->length - 8;
+
 
+    if(read_length > regfi_calc_maxsize(file, cell_info->offset))
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "A chunk exceeded the maxsize "
+			"while constructing big data at offset 0x%.8X "
+			"(chunk offset 0x%.8X).", offset, cell_info->offset);
+      goto fail;
+    }
+
+    if(lseek(file->fd, cell_info->offset+sizeof(uint32_t), SEEK_SET) == -1)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not seek to chunk while "
+			"constructing big data at offset 0x%.8X "
+			"(chunk offset 0x%.8X).", offset, cell_info->offset);
+      goto fail;
+    }
+
+    tmp_len = read_length;
     if(regfi_read(file->fd, ret_val.buf+(data_length-data_left), 
-		  &read_length) != 0)
+		  &read_length) != 0 || (read_length != tmp_len))
     {
       regfi_add_message(file, REGFI_MSG_WARN, "Could not read data chunk while"
-			" constructing big data at offset 0x%.8X.", 
-			chunk_offset);
-      if(strict)
-	goto chunk_fail;
-      else
-	break;
+			" constructing big data at offset 0x%.8X"
+			" (chunk offset 0x%.8X).", offset, cell_info->offset);
+      goto fail;
     }
 
+    if(used_ranges != NULL)
+      if(!range_list_add(used_ranges, cell_info->offset,cell_info->length,NULL))
+	goto fail;
+
     data_left -= read_length;
   }
-  free(indirect_ptrs);
-  ret_val.len = data_length-data_left;
+  range_list_free(bd_cells);
 
+  ret_val.len = data_length-data_left;
   return ret_val;
 
- chunk_fail:
-  free(indirect_ptrs);
-  talloc_free(ret_val.buf);
  fail:
+  if(ret_val.buf != NULL)
+    talloc_free(ret_val.buf);
+  if(indirect_ptrs != NULL)
+    talloc_free(indirect_ptrs);
+  if(bd_cells != NULL)
+    range_list_free(bd_cells);
   ret_val.buf = NULL;
   ret_val.len = 0;
   return ret_val;
@@ -2443,7 +3111,7 @@ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
   range_list* ret_val;
   REGFI_HBIN* hbin;
   const range_list_element* hbins_elem;
-  uint32 i, num_hbins, curr_off, cell_len;
+  uint32_t i, num_hbins, curr_off, cell_len;
   bool is_unalloc;
 
   ret_val = range_list_new();
@@ -2465,7 +3133,7 @@ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
 			   &cell_len, &is_unalloc))
 	break;
       
-      if((cell_len == 0) || ((cell_len & 0xFFFFFFF8) != cell_len))
+      if((cell_len == 0) || ((cell_len & 0x00000007) != 0))
       {
 	regfi_add_message(file, REGFI_MSG_ERROR, "Bad cell length encountered"
 			  " while parsing unallocated cells at offset 0x%.8X.",
@@ -2490,3 +3158,104 @@ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
 
   return ret_val;
 }
+
+
+/* From lib/time.c */
+
+/****************************************************************************
+ Put a 8 byte filetime from a time_t
+ This takes real GMT as input and converts to kludge-GMT
+****************************************************************************/
+void regfi_unix2nt_time(REGFI_NTTIME *nt, time_t t)
+{
+  double d;
+  
+  if (t==0) 
+  {
+    nt->low = 0;
+    nt->high = 0;
+    return;
+  }
+  
+  if (t == TIME_T_MAX) 
+  {
+    nt->low = 0xffffffff;
+    nt->high = 0x7fffffff;
+    return;
+  }		
+  
+  if (t == -1) 
+  {
+    nt->low = 0xffffffff;
+    nt->high = 0xffffffff;
+    return;
+  }		
+  
+  /* this converts GMT to kludge-GMT */
+  /* XXX: This was removed due to difficult dependency requirements.  
+   *      So far, times appear to be correct without this adjustment, but 
+   *      that may be proven wrong with adequate testing. 
+   */
+  /* t -= TimeDiff(t) - get_serverzone(); */
+  
+  d = (double)(t);
+  d += TIME_FIXUP_CONSTANT;
+  d *= 1.0e7;
+  
+  nt->high = (uint32_t)(d * (1.0/(4.0*(double)(1<<30))));
+  nt->low  = (uint32_t)(d - ((double)nt->high)*4.0*(double)(1<<30));
+}
+
+
+/****************************************************************************
+ Interpret an 8 byte "filetime" structure to a time_t
+ It's originally in "100ns units since jan 1st 1601"
+
+ An 8 byte value of 0xffffffffffffffff will be returned as (time_t)0.
+
+ It appears to be kludge-GMT (at least for file listings). This means
+ its the GMT you get by taking a localtime and adding the
+ serverzone. This is NOT the same as GMT in some cases. This routine
+ converts this to real GMT.
+****************************************************************************/
+time_t regfi_nt2unix_time(const REGFI_NTTIME* nt)
+{
+  double d;
+  time_t ret;
+  /* The next two lines are a fix needed for the 
+     broken SCO compiler. JRA. */
+  time_t l_time_min = TIME_T_MIN;
+  time_t l_time_max = TIME_T_MAX;
+  
+  if (nt->high == 0 || (nt->high == 0xffffffff && nt->low == 0xffffffff))
+    return(0);
+  
+  d = ((double)nt->high)*4.0*(double)(1<<30);
+  d += (nt->low&0xFFF00000);
+  d *= 1.0e-7;
+  
+  /* now adjust by 369 years to make the secs since 1970 */
+  d -= TIME_FIXUP_CONSTANT;
+  
+  if (d <= l_time_min)
+    return (l_time_min);
+  
+  if (d >= l_time_max)
+    return (l_time_max);
+  
+  ret = (time_t)(d+0.5);
+  
+  /* this takes us from kludge-GMT to real GMT */
+  /* XXX: This was removed due to difficult dependency requirements.  
+   *      So far, times appear to be correct without this adjustment, but 
+   *      that may be proven wrong with adequate testing. 
+   */
+  /*
+    ret -= get_serverzone();
+    ret += LocTimeDiff(ret);
+  */
+
+  return(ret);
+}
+
+/* End of stuff from lib/time.c */
diff --git a/lib/smb_deps.c b/lib/smb_deps.c
deleted file mode 100644
index eb09aa4..0000000
--- a/lib/smb_deps.c
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * This file contains miscellaneous pieces of code which regfio.c
- * depends upon, from the Samba Subversion tree.  See:
- *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
- *
- * Copyright (C) 2005-2006,2009 Timothy D. Morgan
- * Copyright (C) 1992-2005 Samba development team 
- *               (see individual files under Subversion for details.)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 3 of the License.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id: smb_deps.c 147 2009-02-22 19:31:52Z tim $
- */
-
-#include "smb_deps.h"
-
-
-/* These act as replacements for numerous Samba memory allocation
- *   functions. 
- */
-void* zalloc(size_t size)
-{
-  void* ret_val = NULL;
-  if((size > 0) && (ret_val = (void*)malloc(size)) != NULL)
-    memset(ret_val, 0, size);
-
-  return ret_val;
-}
-
-void* zcalloc(size_t size, unsigned int count)
-{
-  return zalloc(size*count);
-}
-
-/* From lib/time.c */
-
-/****************************************************************************
- Put a 8 byte filetime from a time_t
- This takes real GMT as input and converts to kludge-GMT
-****************************************************************************/
-void unix_to_nt_time(NTTIME *nt, time_t t)
-{
-  double d;
-  
-  if (t==0) 
-  {
-    nt->low = 0;
-    nt->high = 0;
-    return;
-  }
-  
-  if (t == TIME_T_MAX) 
-  {
-    nt->low = 0xffffffff;
-    nt->high = 0x7fffffff;
-    return;
-  }		
-  
-  if (t == -1) 
-  {
-    nt->low = 0xffffffff;
-    nt->high = 0xffffffff;
-    return;
-  }		
-  
-  /* this converts GMT to kludge-GMT */
-  /* XXX: This was removed due to difficult dependency requirements.  
-   *      So far, times appear to be correct without this adjustment, but 
-   *      that may be proven wrong with adequate testing. 
-   */
-  /* t -= TimeDiff(t) - get_serverzone(); */
-  
-  d = (double)(t);
-  d += TIME_FIXUP_CONSTANT;
-  d *= 1.0e7;
-  
-  nt->high = (uint32)(d * (1.0/(4.0*(double)(1<<30))));
-  nt->low  = (uint32)(d - ((double)nt->high)*4.0*(double)(1<<30));
-}
-
-
-/****************************************************************************
- Interpret an 8 byte "filetime" structure to a time_t
- It's originally in "100ns units since jan 1st 1601"
-
- An 8 byte value of 0xffffffffffffffff will be returned as (time_t)0.
-
- It appears to be kludge-GMT (at least for file listings). This means
- its the GMT you get by taking a localtime and adding the
- serverzone. This is NOT the same as GMT in some cases. This routine
- converts this to real GMT.
-****************************************************************************/
-time_t nt_time_to_unix(const NTTIME* nt)
-{
-  double d;
-  time_t ret;
-  /* The next two lines are a fix needed for the 
-     broken SCO compiler. JRA. */
-  time_t l_time_min = TIME_T_MIN;
-  time_t l_time_max = TIME_T_MAX;
-  
-  if (nt->high == 0 || (nt->high == 0xffffffff && nt->low == 0xffffffff))
-    return(0);
-  
-  d = ((double)nt->high)*4.0*(double)(1<<30);
-  d += (nt->low&0xFFF00000);
-  d *= 1.0e-7;
-  
-  /* now adjust by 369 years to make the secs since 1970 */
-  d -= TIME_FIXUP_CONSTANT;
-  
-  if (d <= l_time_min)
-    return (l_time_min);
-  
-  if (d >= l_time_max)
-    return (l_time_max);
-  
-  ret = (time_t)(d+0.5);
-  
-  /* this takes us from kludge-GMT to real GMT */
-  /* XXX: This was removed due to difficult dependency requirements.  
-   *      So far, times appear to be correct without this adjustment, but 
-   *      that may be proven wrong with adequate testing. 
-   */
-  /*
-    ret -= get_serverzone();
-    ret += LocTimeDiff(ret);
-  */
-
-  return(ret);
-}
-
-/* End of stuff from lib/time.c */
diff --git a/lib/void_stack.c b/lib/void_stack.c
index 34c792c..bdb7e9b 100644
--- a/lib/void_stack.c
+++ b/lib/void_stack.c
@@ -1,9 +1,5 @@
 /*
- * This is a really simple implementation of a stack which stores chunks
- * of memory of any type.  It still needs work to eliminate memory
- * leaks. 
- *
- * Copyright (C) 2005,2007,2009 Timothy D. Morgan
+ * Copyright (C) 2005,2007,2009-2010 Timothy D. Morgan
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,9 +14,14 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: void_stack.c 150 2009-03-02 02:17:46Z tim $
+ * $Id: void_stack.c 169 2010-03-03 19:24:58Z tim $
+ */
+
+/**  
+ * @file
  */
 
+
 #include "void_stack.h"
 
 void_stack* void_stack_new(unsigned short max_size)
diff --git a/lib/winsec.c b/lib/winsec.c
index dfd57a7..2aea7ba 100644
--- a/lib/winsec.c
+++ b/lib/winsec.c
@@ -1,9 +1,6 @@
 /*
- * This file contains refactored Samba code used to interpret Windows
- * Security Descriptors. See:
- *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
  *
- * Copyright (C) 2005-2006,2009 Timothy D. Morgan
+ * Copyright (C) 2005-2006,2009-2010 Timothy D. Morgan
  * Copyright (C) 1992-2005 Samba development team 
  *               (see individual files under Subversion for details.)
  *
@@ -20,9 +17,11 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: winsec.c 148 2009-02-22 23:22:59Z tim $
+ * $Id: winsec.c 169 2010-03-03 19:24:58Z tim $
  */
 
+/** @file */
+
 #include "winsec.h"
 
 
diff --git a/src/common.c b/src/common.c
index 734ddb1..a0e40e5 100644
--- a/src/common.c
+++ b/src/common.c
@@ -18,7 +18,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: common.c 154 2009-06-03 15:21:47Z tim $
+ * $Id: common.c 172 2010-03-08 03:04:34Z tim $
  */
 
 #include <iconv.h>
@@ -28,7 +28,7 @@ const char* key_special_chars = ",\"\\/";
 const char* subfield_special_chars = ",\"\\|";
 const char* common_special_chars = ",\"\\";
 
-#define REGLOOKUP_VERSION "0.11.0"
+#define REGLOOKUP_VERSION "0.12.0"
 
 #define REGLOOKUP_EXIT_OK       0
 #define REGLOOKUP_EXIT_OSERR   71
@@ -144,81 +144,6 @@ static char* quote_string(const char* str, const char* special)
 
 
 /*
- * Convert from UTF-16LE to ASCII.  Accepts a Unicode buffer, uni, and
- * it's length, uni_max.  Writes ASCII to the buffer ascii, whose size
- * is ascii_max.  Writes at most (ascii_max-1) bytes to ascii, and null
- * terminates the string.  Returns the length of the data written to
- * ascii.  On error, returns a negative errno code.
- */
-static int uni_to_ascii(unsigned char* uni, char* ascii, 
-			uint32 uni_max, uint32 ascii_max)
-{
-  char* inbuf = (char*)uni;
-  char* outbuf = ascii;
-  size_t in_len = (size_t)uni_max;
-  size_t out_len = (size_t)(ascii_max-1);
-  int ret;
-
-  /* Set up conversion descriptor. */
-  conv_desc = iconv_open("US-ASCII//TRANSLIT", "UTF-16LE");
-
-  ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
-  if(ret == -1)
-  {
-    iconv_close(conv_desc);
-    return -errno;
-  }
-  *outbuf = '\0';
-
-  iconv_close(conv_desc);  
-  return ascii_max-out_len-1;
-}
-
-
-static char* quote_unicode(unsigned char* uni, uint32 length, 
-			   const char* special, char** error_msg)
-{
-  char* ret_val;
-  char* ascii = NULL;
-  char* tmp_err;
-  int ret_err;
-  *error_msg = NULL;
-
-  if(length+1 > 0)
-    ascii = malloc(length+1);
-  if(ascii == NULL)
-  {
-    *error_msg = (char*)malloc(27);
-    if(*error_msg == NULL)
-      return NULL;
-    strcpy(*error_msg, "Memory allocation failure.");
-    return NULL;
-  }
-  
-  ret_err = uni_to_ascii(uni, ascii, length, length+1);
-  if(ret_err < 0)
-  {
-    free(ascii);
-    tmp_err = strerror(-ret_err);
-    *error_msg = (char*)malloc(61+strlen(tmp_err));
-    if(*error_msg == NULL)
-      return NULL;
-
-    sprintf(*error_msg, 
-	    "Unicode conversion failed with '%s'. Quoting as binary.", tmp_err);
-    ret_val = quote_buffer(uni, length, special);
-  }
-  else
-  {
-    ret_val = quote_string(ascii, special);
-    free(ascii);
-  }
-  
-  return ret_val;
-}
-
-
-/*
  * Convert a data value to a string for display.  Returns NULL on error,
  * and the string to display if there is no error, or a non-fatal
  * error.  On any error (fatal or non-fatal) occurs, (*error_msg) will
@@ -228,172 +153,186 @@ static char* quote_unicode(unsigned char* uni, uint32 length,
  * is the responsibility of the caller to free both a non-NULL return
  * value, and a non-NULL (*error_msg).
  */
-/* XXX: Part of this function's logic should be pushed into the regfi API.
- *      The structures should be parsed and stored with VK records and only 
- *      escaped/encoded later in reglookup and reglookup-recover.
- */
-static char* data_to_ascii(unsigned char* datap, uint32 len, uint32 type, 
-			   char** error_msg)
+static char* data_to_ascii(REGFI_DATA* data, char** error_msg)
 {
-  char* asciip;
-  char* ascii;
-  char* ascii_tmp;
+  char* ret_val;
   char* cur_quoted;
-  char* tmp_err = NULL;
-  const char* delim;
-  uint32 i;
-  uint32 cur_str_len;
-  uint32 ascii_max;
-  uint32 str_rem, alen;
-  int ret_err;
-
-  if(datap == NULL)
+  char* tmp_ptr;
+  char* delim;
+  uint32_t ret_val_left, i, tmp_len;
+
+  if(data == NULL || data->size == 0)
   {
-    *error_msg = (char*)malloc(24);
+    *error_msg = (char*)malloc(37);
     if(*error_msg == NULL)
       return NULL;
-    strcpy(*error_msg, "Data pointer was NULL.");
+    strcpy(*error_msg, "Data pointer was NULL or size was 0.");
     return NULL;
   }
   *error_msg = NULL;
 
-  switch (type) 
+
+  if(data->interpreted_size == 0)
+  {
+    *error_msg = (char*)malloc(51);
+    if(*error_msg == NULL)
+      return NULL;
+    strcpy(*error_msg, "Data could not be interpreted, quoting raw buffer.");
+    return quote_buffer(data->raw, data->size, subfield_special_chars);
+  }
+
+  switch (data->type) 
   {
   case REG_SZ:
+    ret_val = quote_string((char*)data->interpreted.string, common_special_chars);
+    if(ret_val == NULL && (*error_msg = (char*)malloc(49)) != NULL)
+	strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
+
+    return ret_val;
+    break;
+
+    
   case REG_EXPAND_SZ:
-    /* REG_LINK is a symbolic link, stored as a unicode string. */
+    ret_val = quote_string((char*)data->interpreted.expand_string, 
+			   common_special_chars);
+    if(ret_val == NULL && (*error_msg = (char*)malloc(49)) != NULL)
+	strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
+
+    return ret_val;
+    break;
+
   case REG_LINK:
-    /* Sometimes values have binary stored in them.  If the unicode
-     * conversion fails, just quote it raw.
-     */
-    cur_quoted = quote_unicode(datap, len, common_special_chars, &tmp_err);
-    if(cur_quoted == NULL)
-    {
-      if(tmp_err == NULL && (*error_msg = (char*)malloc(49)) != NULL)
+    ret_val = quote_string((char*)data->interpreted.link, common_special_chars);
+    if(ret_val == NULL && (*error_msg = (char*)malloc(49)) != NULL)
 	strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
-      else if((*error_msg = (char*)malloc(42+strlen(tmp_err))) != NULL)
-      {
-	sprintf(*error_msg, "Buffer could not be quoted due to error: %s", 
-		tmp_err);
-	free(tmp_err);
-      }
-    }
-    else if (tmp_err != NULL)
-      *error_msg = tmp_err;
-    return cur_quoted;
+
+    return ret_val;
     break;
 
   case REG_DWORD:
-    ascii_max = sizeof(char)*(8+2+1);
-    ascii = malloc(ascii_max);
-    if(ascii == NULL)
+    ret_val = malloc(sizeof(char)*(8+2+1));
+    if(ret_val == NULL)
       return NULL;
 
-    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
-	     datap[3], datap[2], datap[1], datap[0]);
-    return ascii;
+    sprintf(ret_val, "0x%.8X", data->interpreted.dword);
+    return ret_val;
     break;
 
   case REG_DWORD_BE:
-    ascii_max = sizeof(char)*(8+2+1);
-    ascii = malloc(ascii_max);
-    if(ascii == NULL)
+    ret_val = malloc(sizeof(char)*(8+2+1));
+    if(ret_val == NULL)
       return NULL;
 
-    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
-	     datap[0], datap[1], datap[2], datap[3]);
-    return ascii;
+    sprintf(ret_val, "0x%.8X", data->interpreted.dword_be);
+    return ret_val;
     break;
 
   case REG_QWORD:
-    ascii_max = sizeof(char)*(16+2+1);
-    ascii = malloc(ascii_max);
-    if(ascii == NULL)
+    ret_val = malloc(sizeof(char)*(16+2+1));
+    if(ret_val == NULL)
       return NULL;
 
-    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X",
-	     datap[7], datap[6], datap[5], datap[4],
-	     datap[3], datap[2], datap[1], datap[0]);
-    return ascii;
+    sprintf(ret_val, "0x%.16llX", 
+	    (long long unsigned int)data->interpreted.qword);
+    return ret_val;
     break;
-    
+
   case REG_MULTI_SZ:
-    ascii_max = sizeof(char)*(len*4+1);
-    ascii_tmp = malloc(ascii_max);
-    if(ascii_tmp == NULL)
+    ret_val_left = data->interpreted_size*4+1;
+    ret_val = malloc(ret_val_left);
+    if(ret_val == NULL)
       return NULL;
 
-    /* Attempt to convert entire string from UTF-16LE to ASCII, 
-     * then parse and quote fields individually.
-     * If this fails, simply quote entire buffer as binary. 
-     */
-    ret_err = uni_to_ascii(datap, ascii_tmp, len, ascii_max);
-    if(ret_err < 0)
+    tmp_ptr = ret_val;
+    tmp_ptr[0] = '\0';
+    delim = "";
+    for(i=0; data->interpreted.multiple_string[i] != NULL; i++)
     {
-      tmp_err = strerror(-ret_err);
-      *error_msg = (char*)malloc(61+strlen(tmp_err));
-      if(*error_msg == NULL)
+      cur_quoted = quote_string((char*)data->interpreted.multiple_string[i],
+				subfield_special_chars);
+      if(cur_quoted != NULL && cur_quoted[0] != '\0')
       {
-	free(ascii_tmp);
-	return NULL;
-      }
-
-      sprintf(*error_msg, "MULTI_SZ unicode conversion"
-	      " failed with '%s'. Quoting as binary.", tmp_err);
-      ascii = quote_buffer(datap, len, subfield_special_chars);
-    }
-    else
-    {
-      ascii = malloc(ascii_max);
-      if(ascii == NULL)
-      {
-	free(ascii_tmp);
-	return NULL;
-      }
-      asciip = ascii;
-      asciip[0] = '\0';
-      str_rem = ascii_max;
-      delim = "";
-      for(i=0; i<ret_err; i+=cur_str_len+1)
-      {
-	cur_str_len = strlen(ascii_tmp+i);
-	if(ascii_tmp[i] != '\0')
-	{
-	  cur_quoted = quote_string(ascii_tmp+i, subfield_special_chars);
-	  if(cur_quoted != NULL)
-	  {
-	    alen = snprintf(asciip, str_rem, "%s%s", delim, cur_quoted);
-	    asciip += alen;
-	    str_rem -= alen;
-	    free(cur_quoted);
-	  }
-	}
-	delim = "|";
+	tmp_len = snprintf(tmp_ptr, ret_val_left, "%s%s",delim, cur_quoted);
+	tmp_ptr += tmp_len;
+	ret_val_left -= tmp_len;
+	free(cur_quoted);
       }
+      delim = "|";
     }
 
-    free(ascii_tmp);
-    return ascii;
+    return ret_val;
     break;
 
-  /* XXX: Dont know what to do with these yet, just print as binary... */
-  default:
-    *error_msg = (char*)malloc(65);
-    if(*error_msg == NULL)
-      return NULL;
-    sprintf(*error_msg,
-	    "Unrecognized registry data type (0x%.8X); quoting as binary.",
-	    type);
     
   case REG_NONE:
+    return quote_buffer(data->interpreted.none, data->interpreted_size,
+			common_special_chars);
+
+    break;
+
   case REG_RESOURCE_LIST:
+    return quote_buffer(data->interpreted.resource_list, data->interpreted_size,
+			common_special_chars);
+
+    break;
+
   case REG_FULL_RESOURCE_DESCRIPTOR:
+    return quote_buffer(data->interpreted.full_resource_descriptor, 
+			data->interpreted_size, common_special_chars);
+
+    break;
+
   case REG_RESOURCE_REQUIREMENTS_LIST:
+    return quote_buffer(data->interpreted.resource_requirements_list,
+			data->interpreted_size, common_special_chars);
+
+    break;
 
   case REG_BINARY:
-    return quote_buffer(datap, len, common_special_chars);
+    return quote_buffer(data->interpreted.binary, data->interpreted_size,
+			common_special_chars);
+
     break;
-  }
 
+  default:
+    /* This shouldn't happen, since the regfi routines won't interpret 
+     * unknown types, but just as a safety measure against library changes... 
+     */
+    *error_msg = (char*)malloc(65);
+    if(*error_msg == NULL)
+      return NULL;
+    sprintf(*error_msg,
+	    "Unrecognized registry data type (0x%.8X); quoting as binary.",
+	    data->type);
+    return quote_buffer(data->raw, data->size, common_special_chars);
+  }
+    
   return NULL;
 }
+
+
+static char* get_quoted_keyname(const REGFI_NK_REC* nk)
+{
+  char* ret_val;
+
+  if(nk->keyname == NULL)
+    ret_val = quote_buffer(nk->keyname_raw, nk->name_length, key_special_chars);
+  else
+    ret_val = quote_string(nk->keyname, key_special_chars);
+
+  return ret_val;
+}
+
+
+static char* get_quoted_valuename(const REGFI_VK_REC* vk)
+{
+  char* ret_val;
+
+  if(vk->valuename == NULL)
+    ret_val = quote_buffer(vk->valuename_raw, vk->name_length, 
+			   key_special_chars);
+  else
+    ret_val = quote_string(vk->valuename, key_special_chars);
+
+  return ret_val;
+}
diff --git a/src/reglookup-recover.c b/src/reglookup-recover.c
index b549a78..0cd06f2 100644
--- a/src/reglookup-recover.c
+++ b/src/reglookup-recover.c
@@ -1,7 +1,7 @@
 /*
  * This program attempts to recover deleted data structures in a registry hive.
  *
- * Copyright (C) 2008-2009 Timothy D. Morgan
+ * Copyright (C) 2008-2010 Timothy D. Morgan
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: reglookup-recover.c 152 2009-06-02 20:00:38Z tim $
+ * $Id: reglookup-recover.c 173 2010-03-08 03:39:09Z tim $
  */
 
 #include <stdio.h>
@@ -39,16 +39,16 @@ char* registry_file = NULL;
 #include "common.c"
 
 
-char* getQuotedData(int fd, uint32 offset, uint32 length)
+char* getQuotedData(int fd, uint32_t offset, uint32_t length)
 {
-  uint8* buf;
+  uint8_t* buf;
   char* quoted_buf;
-  uint32 len;
+  uint32_t len;
 
   if((lseek(fd, offset, SEEK_SET)) == -1)
     return NULL;
 
-  buf = (uint8*)malloc(length);
+  buf = (uint8_t*)malloc(length);
   if(buf == NULL)
     return NULL;
 
@@ -65,7 +65,7 @@ char* getQuotedData(int fd, uint32 offset, uint32 length)
   return quoted_buf;
 }
 
-
+/* XXX: Somewhere in here, need to start looking for and handling classnames */
 void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix)
 {
   char mtime[20];
@@ -74,11 +74,14 @@ void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix)
   char* quoted_name = NULL;
   char* quoted_raw = "";
 
-  *tmp_time = nt_time_to_unix(&nk->mtime);
+  *tmp_time = regfi_nt2unix_time(&nk->mtime);
   tmp_time_s = gmtime(tmp_time);
   strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
 
-  quoted_name = quote_string(nk->keyname, key_special_chars);
+  /* XXX: Add command line option to choose output encoding */
+  regfi_interpret_keyname(f, nk, REGFI_ENCODING_ASCII, true);
+
+  quoted_name = get_quoted_keyname(nk);
   if (quoted_name == NULL)
   {
     quoted_name = malloc(1*sizeof(char));
@@ -103,34 +106,18 @@ void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix)
 }
 
 
-void printValue(REGFI_FILE* f, const REGFI_VK_REC* vk, const char* prefix)
+void printValue(REGFI_FILE* f, REGFI_VK_REC* vk, const char* prefix)
 {
   char* quoted_value = NULL;
   char* quoted_name = NULL;
   char* quoted_raw = "";
   char* conv_error = NULL;
   const char* str_type = NULL;
-  uint32 size = vk->data_size;
 
-  /* Microsoft's documentation indicates that "available memory" is 
-   * the limit on value sizes.  Annoying.  We limit it to 1M which 
-   * should rarely be exceeded, unless the file is corrupt or 
-   * malicious. For more info, see:
-   *   http://msdn2.microsoft.com/en-us/library/ms724872.aspx
-   */
-  /* XXX: Should probably do something different here for this tool.
-   *      Also, It would be really nice if this message somehow included the
-   *      name of the current value we're having trouble with, since
-   *      stderr/stdout don't always sync nicely.
-   */
-  if(size > REGFI_VK_MAX_DATA_LENGTH)
-  {
-    fprintf(stderr, "WARN: value data size %d larger than "
-	    "%d, truncating...\n", size, REGFI_VK_MAX_DATA_LENGTH);
-    size = REGFI_VK_MAX_DATA_LENGTH;
-  }
+  /* XXX: Add command line option to choose output encoding */
+  regfi_interpret_valuename(f, vk, REGFI_ENCODING_ASCII, true);
   
-  quoted_name = quote_string(vk->valuename, key_special_chars);
+  quoted_name = get_quoted_valuename(vk);
   if (quoted_name == NULL)
   { /* Value names are NULL when we're looking at the "(default)" value.
      * Currently we just return a 0-length string to try an eliminate 
@@ -138,13 +125,21 @@ void printValue(REGFI_FILE* f, const REGFI_VK_REC* vk, const char* prefix)
      * in the output allows one to differentiate between the parent key and
      * this value.
      */
-    quoted_name = malloc(1*sizeof(char));
+    quoted_name = strdup("");
     if(quoted_name == NULL)
       bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
-    quoted_name[0] = '\0';
   }
 
-  quoted_value = data_to_ascii(vk->data, size, vk->type, &conv_error);
+  /* XXX: Add command line option to choose output encoding */
+  if(vk->data != NULL 
+     && !regfi_interpret_data(f, REGFI_ENCODING_ASCII, vk->type, vk->data))
+  {
+    fprintf(stderr, "WARN: Error occurred while interpreting data for VK record"
+	    " at offset 0x%.8X.\n", vk->offset);
+  }
+  printMsgs(f);
+
+  quoted_value = data_to_ascii(vk->data, &conv_error);
   if(quoted_value == NULL)
   {
     quoted_value = malloc(1*sizeof(char));
@@ -227,10 +222,10 @@ void printSK(REGFI_FILE* f, REGFI_SK_REC* sk)
 }
 
 
-int printCell(REGFI_FILE* f, uint32 offset)
+int printCell(REGFI_FILE* f, uint32_t offset)
 {
   char* quoted_buf;
-  uint32 cell_length;
+  uint32_t cell_length;
   bool unalloc;
 
   if(!regfi_parse_cell(f->fd, offset, NULL, 0, &cell_length, &unalloc))
@@ -257,43 +252,44 @@ int printCell(REGFI_FILE* f, uint32 offset)
 char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk)
 {
   void_stack* path_stack = void_stack_new(REGFI_MAX_DEPTH);
-  const REGFI_HBIN* hbin;
   REGFI_NK_REC* cur_ancestor;
   char* ret_val;
-  uint32 virt_offset, i, stack_size, ret_val_size, ret_val_used;
-  uint32 max_length;
+  uint32_t virt_offset, i, stack_size, ret_val_size, ret_val_used, offset;
+  int32_t max_size;
   REGFI_BUFFER* path_element;
   
   /* The path_stack size limit should guarantee that we don't recurse forever. */
   virt_offset = nk->parent_off;
   ret_val_size = 1; /* NUL */
   while(virt_offset != REGFI_OFFSET_NONE)
-  {  
-    hbin = regfi_lookup_hbin(f, virt_offset);
-    if(hbin == NULL)
+  {
+    offset = virt_offset+REGFI_REGF_SIZE;
+    max_size = regfi_calc_maxsize(f, offset);
+    if(max_size < 0)
       virt_offset = REGFI_OFFSET_NONE;
     else
     {
-      max_length = hbin->block_size + hbin->file_off 
-	- (virt_offset+REGFI_REGF_SIZE);
-      cur_ancestor = regfi_parse_nk(f, virt_offset+REGFI_REGF_SIZE, 
-				    max_length, true);
+      cur_ancestor = regfi_parse_nk(f, offset, max_size, true);
       printMsgs(f);
 
       if(cur_ancestor == NULL)
 	virt_offset = REGFI_OFFSET_NONE;
       else
       {
-	if(cur_ancestor->key_type & REGFI_NK_FLAG_ROOT)
+	if(cur_ancestor->flags & REGFI_NK_FLAG_ROOT)
 	  virt_offset = REGFI_OFFSET_NONE;
 	else
 	  virt_offset = cur_ancestor->parent_off;
 	
 	path_element = talloc(path_stack, REGFI_BUFFER);
 	if(path_element != NULL)
-	  path_element->buf = (uint8*)quote_string(cur_ancestor->keyname, 
-						   key_special_chars);
+	{
+	  /* XXX: Add command line option to choose output encoding */
+	  regfi_interpret_keyname(f, cur_ancestor, REGFI_ENCODING_ASCII, true);
 	  
+	  path_element->buf = (uint8_t*)get_quoted_keyname(cur_ancestor);
+	}
+ 
 	if(path_element == NULL || path_element->buf == NULL 
 	   || !void_stack_push(path_stack, path_element))
 	{
@@ -356,9 +352,9 @@ static void usage(void)
 }
 
 
-bool removeRange(range_list* rl, uint32 offset, uint32 length)
+bool removeRange(range_list* rl, uint32_t offset, uint32_t length)
 {
-  int32 rm_idx;
+  int32_t rm_idx;
   const range_list_element* cur_elem;
 
   rm_idx = range_list_find(rl, offset);
@@ -418,7 +414,7 @@ int extractVKs(REGFI_FILE* f,
 {
   const range_list_element* cur_elem;
   REGFI_VK_REC* vk;
-  uint32 i, j;
+  uint32_t i, j;
 
   for(i=0; i < range_list_size(unalloc_cells); i++)
   {
@@ -455,76 +451,136 @@ int extractVKs(REGFI_FILE* f,
 }
 
 
-int extractDataCells(REGFI_FILE* f,
+int extractDataCells(REGFI_FILE* file,
 		     range_list* unalloc_cells,
 		     range_list* unalloc_values)
 {
   const range_list_element* cur_elem;
   REGFI_VK_REC* vk;
-  const REGFI_HBIN* hbin;
+  range_list* bd_cells;
   REGFI_BUFFER data;
-  uint32 i, off, data_offset, data_maxsize;
+  uint32_t i, j, offset, cell_length, length;
+  int32_t max_size;
+  bool unalloc;
+
+  bd_cells = range_list_new();
+  if(bd_cells == NULL)
+    return 10;
 
+  data.buf = NULL;
+  data.len = 0;
   for(i=0; i<range_list_size(unalloc_values); i++)
   {
     cur_elem = range_list_get(unalloc_values, i);
     vk = (REGFI_VK_REC*)cur_elem->data;
     if(vk == NULL)
-      return 40;
+      return 11;
 
-    if(vk->data_size == 0)
-      vk->data = NULL;
-    else
+    length = vk->data_size;
+    vk->data = NULL;
+    if(vk->data_size != 0)
     {
-      off = vk->data_off+REGFI_REGF_SIZE;
+      offset = vk->data_off+REGFI_REGF_SIZE;
 
       if(vk->data_in_offset)
+	data = regfi_parse_little_data(file, vk->data_off, 
+				       length, false);
+      else
       {
-	data = regfi_load_data(f, vk->type, vk->data_off,
-			       vk->data_size, 4,
-			       vk->data_in_offset, false);
-	vk->data = data.buf;
-	vk->data_size = data.len;
-      }
-      else if(range_list_has_range(unalloc_cells, off, vk->data_size))
-      {
-	hbin = regfi_lookup_hbin(f, vk->data_off);
-	if(hbin)
+	max_size = regfi_calc_maxsize(file, offset);
+	if(max_size >= 0 
+	   && regfi_parse_cell(file->fd, offset, NULL, 0,
+			       &cell_length, &unalloc)
+	   && (cell_length & 0x00000007) == 0
+	   && cell_length <= max_size)
 	{
-	  data_offset = vk->data_off+REGFI_REGF_SIZE;
-	  data_maxsize = hbin->block_size + hbin->file_off - data_offset;
-	  data = regfi_load_data(f, vk->type, data_offset, 
-				 vk->data_size, data_maxsize, 
-				 vk->data_in_offset, false);
-	  vk->data = data.buf;
-	  vk->data_size = data.len;
-
-	  if(vk->data != NULL)
+	  if(cell_length - 4 < length)
 	  {
-	    /* XXX: The following may not make sense now in light of big data
-	     *      records.
-	     */
-	    /* XXX: This strict checking prevents partial recovery of data 
-	     *      cells.  Also, see code for regfi_load_data and note that
-	     *      lengths indicated in VK records are sometimes just plain 
-	     *      wrong.  Need a feedback mechanism to be more fuzzy with 
-	     *      data cell lengths and the ranges removed. 
-	     *
-	     *      The introduction of REGFI_BUFFER in regfi_load_data has 
-	     *      fixed some of this.  Should review again with respect to 
-	     *      the other issues mentioned above though.
+	    /* Multi-cell "big data" */
+
+	    /* XXX: All big data records thus far have been 16 bytes long.  
+	     *      Should we check for this precise size instead of just 
+	     *      relying upon the above check?
 	     */
-	    /* A data record was recovered. Remove from unalloc_cells. */
-	    if(!removeRange(unalloc_cells, off, vk->data_size))
-	      return 50;
+	    if (file->major_version >= 1 && file->minor_version >= 5)
+	    {
+	      /* Attempt to parse a big data record */
+	      data = regfi_load_big_data(file, offset, length, 
+					 cell_length, bd_cells, false);
+
+	      /* XXX: if this turns out NULL, should fall back to truncating cell */
+	      if(data.buf != NULL)
+	      {
+		for(j=0; j<range_list_size(bd_cells); j++)
+		{
+		  cur_elem = range_list_get(bd_cells, j);
+		  if(cur_elem == NULL)
+		    return 20;
+		  if(!range_list_has_range(unalloc_cells,
+					   cur_elem->offset, 
+					   cur_elem->length))
+		  {
+		    fprintf(stderr, 
+			    "WARN: Successfully parsed big data at offset"
+			    " 0x%.8X was rejected because some substructure"
+			    " (offset=0x%.8X) is allocated or used in other"
+			    " recovered structures.\n",
+			    offset, cur_elem->offset);
+		    talloc_free(data.buf);
+		    data.buf = NULL;
+		    data.len = 0;
+		    break;
+		  }
+		}
+		
+		if(data.buf != NULL)
+		{
+		  for(j=0; j<range_list_size(bd_cells); j++)
+		  {
+		    cur_elem = range_list_get(bd_cells, j);
+		    if(cur_elem == NULL)
+		      return 21;
+		    
+		    if(!removeRange(unalloc_cells, 
+				    cur_elem->offset,
+				    cur_elem->length))
+		    { return 22; }
+		  }
+		}
+	      }
+
+	    }
+	    else
+	    {
+	      fprintf(stderr, 
+		      "WARN: Data length (0x%.8X)"
+		      " larger than remaining cell length (0x%.8X)"
+		      " while parsing data record at offset 0x%.8X."
+		      " Truncating...\n",
+		      length, cell_length - 4, offset);
+	       length = cell_length - 4;
+	    }
+	  }
+	  
+	  /* Typical 1-cell data */
+	  if(range_list_has_range(unalloc_cells, offset, length))
+	  {
+	    data = regfi_parse_data(file, offset, length, false);
+	    if(data.buf != NULL)
+	      if(!removeRange(unalloc_cells, offset, length))
+		return 30;
 	  }
 	}
-	else
-	  vk->data = NULL;
       }
+      /* XXX: Need to come up with a different way to link these so the
+       *      vk->data item can be removed from the structure.
+       */
+      vk->data = regfi_buffer_to_data(data);
+      talloc_steal(vk, vk->data);
     }
   }
 
+  range_list_free(bd_cells);
   return 0;
 }
 
@@ -536,7 +592,7 @@ int extractKeys(REGFI_FILE* f,
 {
   const range_list_element* cur_elem;
   REGFI_NK_REC* key;
-  uint32 i, j;
+  uint32_t i, j;
   int error_code = 0;
 
   for(i=0; i < range_list_size(unalloc_cells); i++)
@@ -589,9 +645,9 @@ int extractValueLists(REGFI_FILE* f,
 {
   REGFI_NK_REC* nk;
   REGFI_VK_REC* vk;
-  const REGFI_HBIN* hbin;
   const range_list_element* cur_elem;
-  uint32 i, j, num_keys, off, values_length, max_length;
+  uint32_t i, j, num_keys, off, values_length;
+  int32_t max_size;
 
   num_keys=range_list_size(unalloc_keys);
   for(i=0; i<num_keys; i++)
@@ -603,14 +659,12 @@ int extractValueLists(REGFI_FILE* f,
 
     if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE))
     {
-      hbin = regfi_lookup_hbin(f, nk->values_off);
-      
-      if(hbin != NULL)
+      off = nk->values_off + REGFI_REGF_SIZE;
+      max_size = regfi_calc_maxsize(f, off);
+      if(max_size >= 0)
       {
-	off = nk->values_off + REGFI_REGF_SIZE;
-	max_length = hbin->block_size + hbin->file_off - off;
 	nk->values = regfi_load_valuelist(f, off, nk->num_values, 
-					  max_length, false);
+					  max_size, false);
 	if(nk->values != NULL && nk->values->elements != NULL)
 	{
 	  /* Number of elements in the value list may be shorter than advertised 
@@ -618,7 +672,7 @@ int extractValueLists(REGFI_FILE* f,
 	   * only throw out the whole value list if it bleeds into an already 
 	   * parsed structure.
 	   */
-	  values_length = (nk->values->num_values+1)*sizeof(uint32);
+	  values_length = (nk->values->num_values+1)*sizeof(uint32_t);
 	  if(values_length != (values_length & 0xFFFFFFF8))
 	    values_length = (values_length & 0xFFFFFFF8) + 8;
 
@@ -682,7 +736,7 @@ int extractSKs(REGFI_FILE* f,
 {
   const range_list_element* cur_elem;
   REGFI_SK_REC* sk;
-  uint32 i, j;
+  uint32_t i, j;
 
   for(i=0; i < range_list_size(unalloc_cells); i++)
   {
@@ -733,7 +787,7 @@ int main(int argc, char** argv)
   char* tmp_path;
   REGFI_NK_REC* tmp_key;
   REGFI_VK_REC* tmp_value;
-  uint32 argi, arge, i, j, ret, num_unalloc_keys;
+  uint32_t argi, arge, i, j, ret, num_unalloc_keys;
   
   /* Process command line arguments */
   if(argc < 2)
@@ -884,7 +938,10 @@ int main(int argc, char** argv)
     printKey(f, tmp_key, parent_paths[i]);
     if(tmp_key->num_values > 0 && tmp_key->values != NULL)
     {
-      tmp_name = quote_string(tmp_key->keyname, key_special_chars);
+      /* XXX: Add command line option to choose output encoding */
+      regfi_interpret_keyname(f, tmp_key, REGFI_ENCODING_ASCII, true);
+
+      tmp_name = get_quoted_keyname(tmp_key);
       tmp_path = (char*)malloc(strlen(parent_paths[i])+strlen(tmp_name)+2);
       if(tmp_path == NULL)
       {
diff --git a/src/reglookup.c b/src/reglookup.c
index e9d2a2c..9969065 100644
--- a/src/reglookup.c
+++ b/src/reglookup.c
@@ -1,7 +1,8 @@
 /*
  * A utility to read a Windows NT and later registry files.
  *
- * Copyright (C) 2005-2009 Timothy D. Morgan
+ * Copyright (C) 2005-2010 Timothy D. Morgan
+ * Copyright (C) 2010 Tobias Mueller (portions of '-i' code)
  * Copyright (C) 2002 Richard Sharpe, rsharpe at richardsharpe.com
  *
  * This program is free software; you can redistribute it and/or modify
@@ -17,7 +18,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: reglookup.c 150 2009-03-02 02:17:46Z tim $
+ * $Id: reglookup.c 172 2010-03-08 03:04:34Z tim $
  */
 
 
@@ -30,6 +31,7 @@
 #include "void_stack.h"
 
 /* Globals, influenced by command line parameters */
+bool print_value_mtime = false;
 bool print_verbose = false;
 bool print_security = false;
 bool print_header = true;
@@ -49,28 +51,18 @@ REGFI_FILE* f;
 #include "common.c"
 
 
-void printValue(const REGFI_VK_REC* vk, char* prefix)
+void printValue(REGFI_ITERATOR* iter, const REGFI_VK_REC* vk, char* prefix)
 {
+  REGFI_DATA* data;
   char* quoted_value = NULL;
   char* quoted_name = NULL;
   char* conv_error = NULL;
   const char* str_type = NULL;
-  uint32 size = vk->data_size;
-
-  /* Microsoft's documentation indicates that "available memory" is 
-   * the limit on value sizes.  Annoying.  We limit it to 1M which 
-   * should rarely be exceeded, unless the file is corrupt or 
-   * malicious. For more info, see:
-   *   http://msdn2.microsoft.com/en-us/library/ms724872.aspx
-   */
-  if(size > REGFI_VK_MAX_DATA_LENGTH)
-  {
-    fprintf(stderr, "WARN: value data size %d larger than "
-	    "%d, truncating...\n", size, REGFI_VK_MAX_DATA_LENGTH);
-    size = REGFI_VK_MAX_DATA_LENGTH;
-  }
-  
-  quoted_name = quote_string(vk->valuename, key_special_chars);
+  char mtime[20];
+  time_t tmp_time[1];
+  struct tm* tmp_time_s = NULL;
+
+  quoted_name = get_quoted_valuename(vk);
   if (quoted_name == NULL)
   { /* Value names are NULL when we're looking at the "(default)" value.
      * Currently we just return a 0-length string to try an eliminate 
@@ -83,16 +75,13 @@ void printValue(const REGFI_VK_REC* vk, char* prefix)
       bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
     quoted_name[0] = '\0';
   }
+  
+  data = regfi_iterator_fetch_data(iter, vk);
 
-  if(vk->data == NULL)
-  {
-    if(print_verbose)
-      fprintf(stderr, "INFO: While quoting value for '%s/%s', "
-	      "data pointer was NULL.\n", prefix, quoted_name);
-  }
-  else
+  printMsgs(iter->f);
+  if(data != NULL)
   {
-    quoted_value = data_to_ascii(vk->data, size, vk->type, &conv_error);
+    quoted_value = data_to_ascii(data, &conv_error);
     if(quoted_value == NULL)
     {
       if(conv_error == NULL)
@@ -102,29 +91,39 @@ void printValue(const REGFI_VK_REC* vk, char* prefix)
 	fprintf(stderr, "WARN: Could not quote value for '%s/%s'.  "
 		"Returned error: %s\n", prefix, quoted_name, conv_error);
     }
-    else if(conv_error != NULL && print_verbose)
-      fprintf(stderr, "INFO: While quoting value for '%s/%s', "
+    else if(conv_error != NULL)
+      fprintf(stderr, "WARN: While quoting value for '%s/%s', "
 	      "warning returned: %s\n", prefix, quoted_name, conv_error);
+    regfi_free_data(data);
   }
 
+  if(print_value_mtime)
+  {
+    *tmp_time = regfi_nt2unix_time(&iter->cur_key->mtime);
+    tmp_time_s = gmtime(tmp_time);
+    strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
+  }
+  else
+    mtime[0] = '\0';
+
   str_type = regfi_type_val2str(vk->type);
   if(print_security)
   {
     if(str_type == NULL)
-      printf("%s/%s,0x%.8X,%s,,,,,\n", prefix, quoted_name,
-	     vk->type, quoted_value);
+      printf("%s/%s,0x%.8X,%s,%s,,,,\n", prefix, quoted_name,
+	     vk->type, quoted_value, mtime);
     else
-      printf("%s/%s,%s,%s,,,,,\n", prefix, quoted_name,
-	     str_type, quoted_value);
+      printf("%s/%s,%s,%s,%s,,,,\n", prefix, quoted_name,
+	     str_type, quoted_value, mtime);
   }
   else
   {
     if(str_type == NULL)
-      printf("%s/%s,0x%.8X,%s,\n", prefix, quoted_name,
-	     vk->type, quoted_value);
+      printf("%s/%s,0x%.8X,%s,%s\n", prefix, quoted_name,
+	     vk->type, quoted_value, mtime);
     else
-      printf("%s/%s,%s,%s,\n", prefix, quoted_name,
-	     str_type, quoted_value);
+      printf("%s/%s,%s,%s,%s\n", prefix, quoted_name,
+	     str_type, quoted_value, mtime);
   }
 
   if(quoted_value != NULL)
@@ -142,7 +141,7 @@ char** splitPath(const char* s)
   const char* cur = s;
   char* next = NULL;
   char* copy;
-  uint32 ret_cur = 0;
+  uint32_t ret_cur = 0;
 
   ret_val = (char**)malloc((REGFI_MAX_DEPTH+1+1)*sizeof(char**));
   if (ret_val == NULL)
@@ -189,7 +188,7 @@ char** splitPath(const char* s)
 
 void freePath(char** path)
 {
-  uint32 i;
+  uint32_t i;
 
   if(path == NULL)
     return;
@@ -205,14 +204,14 @@ void freePath(char** path)
 char* iter2Path(REGFI_ITERATOR* i)
 {
   const REGFI_ITER_POSITION* cur;
-  uint32 buf_left = 127;
-  uint32 buf_len = buf_left+1;
-  uint32 name_len = 0;
-  uint32 grow_amt;
+  const REGFI_NK_REC* tmp_key;
+  uint32_t buf_left = 127;
+  uint32_t buf_len = buf_left+1;
+  uint32_t name_len = 0;
+  uint32_t grow_amt;
   char* buf;
   char* new_buf;
   char* name;
-  const char* cur_name;
   void_stack_iterator* iter;
   
   buf = (char*)malloc((buf_len)*sizeof(char));
@@ -240,17 +239,18 @@ char* iter2Path(REGFI_ITERATOR* i)
   {
     cur = void_stack_iterator_next(iter);
     if (cur == NULL)
-      cur_name = i->cur_key->keyname;
+      tmp_key = i->cur_key;
     else
-      cur_name = cur->nk->keyname;
+      tmp_key = cur->nk;
+
+    name = get_quoted_keyname(tmp_key);
 
     buf[buf_len-buf_left-1] = '/';
     buf_left -= 1;
-    name = quote_string(cur_name, key_special_chars);
     name_len = strlen(name);
     if(name_len+1 > buf_left)
     {
-      grow_amt = (uint32)(buf_len/2);
+      grow_amt = (uint32_t)(buf_len/2);
       buf_len += name_len+1+grow_amt-buf_left;
       if((new_buf = realloc(buf, buf_len)) == NULL)
       {
@@ -280,7 +280,7 @@ void printValueList(REGFI_ITERATOR* iter, char* prefix)
   while(value != NULL)
   {
     if(!type_filter_enabled || (value->type == type_filter))
-      printValue(value, prefix);
+      printValue(iter, value, prefix);
     regfi_free_value(value);
     value = regfi_iterator_next_value(iter);
     printMsgs(iter->f);
@@ -296,14 +296,14 @@ void printKey(REGFI_ITERATOR* iter, char* full_path)
   char* sacl = NULL;
   char* dacl = NULL;
   char* quoted_classname;
-  char* error_msg = NULL;
   char mtime[20];
   time_t tmp_time[1];
   struct tm* tmp_time_s = NULL;
   const REGFI_SK_REC* sk;
   const REGFI_NK_REC* k = regfi_iterator_cur_key(iter);
+  REGFI_CLASSNAME* classname;
 
-  *tmp_time = nt_time_to_unix(&k->mtime);
+  *tmp_time = regfi_nt2unix_time(&k->mtime);
   tmp_time_s = gmtime(tmp_time);
   strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
 
@@ -322,32 +322,31 @@ void printKey(REGFI_ITERATOR* iter, char* full_path)
     if(dacl == NULL)
       dacl = empty_str;
 
-    if(k->classname != NULL)
+    classname = regfi_iterator_fetch_classname(iter, k);
+    printMsgs(iter->f);
+    if(classname != NULL)
     {
-      quoted_classname = quote_unicode((uint8*)k->classname, k->classname_length,
-				       key_special_chars, &error_msg);
-      if(quoted_classname == NULL)
+      if(classname->interpreted == NULL)
       {
-	if(error_msg == NULL)
-	  fprintf(stderr, "ERROR: Could not quote classname"
-		  " for key '%s' due to unknown error.\n", full_path);
-	else
-	{
-	  fprintf(stderr, "ERROR: Could not quote classname"
-		  " for key '%s' due to error: %s\n", full_path, error_msg);
-	  free(error_msg);
-	}
+	fprintf(stderr, "WARN: Could not convert class name"
+		" charset for key '%s'.  Quoting raw...\n", full_path);
+	quoted_classname = quote_buffer(classname->raw, classname->size,
+					key_special_chars);
       }
-      else if (error_msg != NULL)
+      else
+	quoted_classname = quote_string(classname->interpreted, 
+					key_special_chars);
+
+      if(quoted_classname == NULL)
       {
-	if(print_verbose)
-	  fprintf(stderr, "INFO: While converting classname"
-		  " for key '%s': %s.\n", full_path, error_msg);
-	free(error_msg);
+	fprintf(stderr, "ERROR: Could not quote classname"
+		" for key '%s' due to unknown error.\n", full_path);
+	quoted_classname = empty_str;
       }
     }
     else
       quoted_classname = empty_str;
+    regfi_free_classname(classname);
 
     printMsgs(iter->f);
     printf("%s,KEY,,%s,%s,%s,%s,%s,%s\n", full_path, mtime, 
@@ -460,7 +459,7 @@ int retrievePath(REGFI_ITERATOR* iter, char** path)
   REGFI_VK_REC* value;
   char* tmp_path_joined;
   const char** tmp_path;
-  uint32 i;
+  uint32_t i;
   
   if(path == NULL)
     return -1;
@@ -510,7 +509,7 @@ int retrievePath(REGFI_ITERATOR* iter, char** path)
       bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Unexpected error before printValue.\n");
 
     if(!type_filter_enabled || (value->type == type_filter))
-      printValue(value, tmp_path_joined);
+      printValue(iter, value, tmp_path_joined);
 
     regfi_free_value(value);
     free(tmp_path);
@@ -554,6 +553,7 @@ static void usage(void)
   fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n");
   fprintf(stderr, "\t-p\t restrict output to elements below this path.\n");
   fprintf(stderr, "\t-t\t restrict results to this specific data type.\n");
+  fprintf(stderr, "\t-i\t includes parent key modification times with child values.\n");
   fprintf(stderr, "\n");
 }
 
@@ -563,7 +563,7 @@ int main(int argc, char** argv)
   char** path = NULL;
   REGFI_ITERATOR* iter;
   int retr_path_ret;
-  uint32 argi, arge;
+  uint32_t argi, arge;
 
   /* Process command line arguments */
   if(argc < 2)
@@ -611,6 +611,8 @@ int main(int argc, char** argv)
       print_security = false;
     else if (strcmp("-v", argv[argi]) == 0)
       print_verbose = true;
+    else if (strcmp("-i", argv[argi]) == 0)
+      print_value_mtime = true;
     else
     {
       usage();
@@ -631,9 +633,13 @@ int main(int argc, char** argv)
   if(print_verbose)
     regfi_set_message_mask(f, REGFI_MSG_INFO|REGFI_MSG_WARN|REGFI_MSG_ERROR);
 
-  iter = regfi_iterator_new(f);
+  /* XXX: add command line option to choose output encoding */
+  iter = regfi_iterator_new(f, REGFI_ENCODING_ASCII);
   if(iter == NULL)
+  {
+    printMsgs(f);
     bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Couldn't create registry iterator.\n");
+  }
 
   if(print_header)
   {

-- 
debian-forensics/reglookup



More information about the forensics-changes mailing list