diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..a0cbe33
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,7 @@
+
+
+	
+	
+	
+	
+
diff --git a/.project b/.project
new file mode 100644
index 0000000..42ac320
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+
+
+	java-libpst
+	
+	
+	
+	
+		
+			org.eclipse.jdt.core.javabuilder
+			
+			
+		
+	
+	
+		org.eclipse.jdt.core.javanature
+	
+
diff --git a/PropertyNames.txt b/PropertyNames.txt
index 572a87f..16e6278 100644
--- a/PropertyNames.txt
+++ b/PropertyNames.txt
@@ -135,6 +135,7 @@
 3FDE=PidTagInternetCodepage
 3FF1=PidTagMessageLocaleId
 3FFD=PidTagMessageCodepage
+3ff9=PidTagCreatorName
 4019=PidTagSenderFlags
 401A=PidTagSentRepresentingFlags
 401B=PidTagReceivedByFlags
@@ -170,6 +171,8 @@
 7FFD=PidTagAttachmentFlags
 7FFE=PidTagAttachmentHidden
 7FFF=PidTagAttachmentContactPhoto
+3FFA=PidTagLastModifiedName_W
+3FFB=PidTagLastModifierEntryId
 00000001=PidLidAttendeeCriticalChange
 00000002=PidLidWhere
 00000003=PidLidGlobalObjectId
diff --git a/com/pff/LZFu.java b/com/pff/LZFu.java
deleted file mode 100644
index c5a573b..0000000
--- a/com/pff/LZFu.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2010 Richard Johnson & Orin Eman
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * ---
- *
- * This file is part of java-libpst.
- *
- * java-libpst is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * java-libpst 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with java-libpst.  If not, see .
- *
- */
-
-package com.pff;
-
-import java.io.UnsupportedEncodingException;
-
-/**
- * An implementation of the LZFu algorithm to decompress RTF content
- * @author Richard Johnson
- */
-public class LZFu {
-
-	public static final String LZFU_HEADER = "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript \\fdecor MS Sans SerifSymbolArialTimes New RomanCourier{\\colortbl\\red0\\green0\\blue0\n\r\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx";
-
-	public static String decode(byte[] data)
-			throws PSTException
-	{
-
-		@SuppressWarnings("unused")
-		int compressedSize = (int)PSTObject.convertLittleEndianBytesToLong(data, 0, 4);
-		int uncompressedSize = (int)PSTObject.convertLittleEndianBytesToLong(data, 4, 8);
-		int compressionSig =  (int)PSTObject.convertLittleEndianBytesToLong(data, 8, 12);
-		@SuppressWarnings("unused")
-		int compressedCRC =  (int)PSTObject.convertLittleEndianBytesToLong(data, 12, 16);
-
-		if (compressionSig == 0x75465a4c) {
-			// we are compressed...
-			byte[] output = new byte[uncompressedSize];
-			int outputPosition = 0;
-			byte[] lzBuffer = new byte[4096];
-			// preload our buffer.
-			try {
-				byte[] bytes = LZFU_HEADER.getBytes("US-ASCII");
-				System.arraycopy(bytes, 0, lzBuffer, 0, LZFU_HEADER.length());
-			} catch (UnsupportedEncodingException e) {
-				e.printStackTrace();
-			}
-			int bufferPosition = LZFU_HEADER.length();
-			int currentDataPosition = 16;
-
-			// next byte is the flags,
-			while (currentDataPosition < data.length - 2 && outputPosition < output.length) {
-				int flags = data[currentDataPosition++] & 0xFF;
-				for (int x = 0; x < 8 && outputPosition < output.length; x++) {
-					boolean isRef = ((flags & 1) == 1);
-					flags >>= 1;
-					if (isRef) {
-						// get the starting point for the buffer and the
-						// length to read
-						int refOffsetOrig = data[currentDataPosition++] & 0xFF;
-						int refSizeOrig = data[currentDataPosition++] & 0xFF;
-						int refOffset = (refOffsetOrig << 4) | (refSizeOrig >>> 4);
-						int refSize = (refSizeOrig & 0xF) + 2;
-						//refOffset &= 0xFFF;
-						try {
-							// copy the data from the buffer
-							int index = refOffset;
-							for (int y = 0; y < refSize && outputPosition < output.length; y++) {
-								output[outputPosition++] = lzBuffer[index];
-								lzBuffer[bufferPosition] = lzBuffer[index];
-								bufferPosition++;
-								bufferPosition %= 4096;
-								++index;
-								index %= 4096;
-							}
-						} catch ( Exception e ) {
-							e.printStackTrace();
-						}
-
-					} else {
-						// copy the byte over
-						lzBuffer[bufferPosition] = 	data[currentDataPosition];
-						bufferPosition++;
-						bufferPosition %= 4096;
-						output[outputPosition++] = data[currentDataPosition++];
-					}
-				}
-			}
-
-			if ( outputPosition != uncompressedSize ) {
-				throw new PSTException(String.format("Error decompressing RTF! Expected %d bytes, got %d bytes\n", uncompressedSize, outputPosition));
-			}
-			return new String(output).trim();
-
-		} else if (compressionSig == 0x414c454d) {
-			// we are not compressed!
-			// just return the rest of the contents as a string
-			byte[] output = new byte[data.length-16];
-			System.arraycopy(data, 16, output, 0, data.length-16);
-			return new String(output).trim();
-		}
-
-		return "";
-	}
-}
diff --git a/com/pff/PSTUtils.java b/com/pff/PSTUtils.java
new file mode 100644
index 0000000..402b223
--- /dev/null
+++ b/com/pff/PSTUtils.java
@@ -0,0 +1,376 @@
+package com.pff;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Calendar;
+import java.util.Date;
+
+import com.pff.exceptions.PSTException;
+import com.pff.objects.sub.PSTTimeZone;
+
+public abstract class PSTUtils {
+
+	public static final String LZFU_HEADER = "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript \\fdecor MS Sans SerifSymbolArialTimes New RomanCourier{\\colortbl\\red0\\green0\\blue0\n\r\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx";
+
+
+	// substitution table for the compressible encryption type.
+	static int[] compEnc = {
+	    0x47, 0xf1, 0xb4, 0xe6, 0x0b, 0x6a, 0x72, 0x48, 0x85, 0x4e, 0x9e, 0xeb, 0xe2, 0xf8, 0x94, 0x53,
+	    0xe0, 0xbb, 0xa0, 0x02, 0xe8, 0x5a, 0x09, 0xab, 0xdb, 0xe3, 0xba, 0xc6, 0x7c, 0xc3, 0x10, 0xdd,
+	    0x39, 0x05, 0x96, 0x30, 0xf5, 0x37, 0x60, 0x82, 0x8c, 0xc9, 0x13, 0x4a, 0x6b, 0x1d, 0xf3, 0xfb,
+	    0x8f, 0x26, 0x97, 0xca, 0x91, 0x17, 0x01, 0xc4, 0x32, 0x2d, 0x6e, 0x31, 0x95, 0xff, 0xd9, 0x23,
+	    0xd1, 0x00, 0x5e, 0x79, 0xdc, 0x44, 0x3b, 0x1a, 0x28, 0xc5, 0x61, 0x57, 0x20, 0x90, 0x3d, 0x83,
+	    0xb9, 0x43, 0xbe, 0x67, 0xd2, 0x46, 0x42, 0x76, 0xc0, 0x6d, 0x5b, 0x7e, 0xb2, 0x0f, 0x16, 0x29,
+	    0x3c, 0xa9, 0x03, 0x54, 0x0d, 0xda, 0x5d, 0xdf, 0xf6, 0xb7, 0xc7, 0x62, 0xcd, 0x8d, 0x06, 0xd3,
+	    0x69, 0x5c, 0x86, 0xd6, 0x14, 0xf7, 0xa5, 0x66, 0x75, 0xac, 0xb1, 0xe9, 0x45, 0x21, 0x70, 0x0c,
+	    0x87, 0x9f, 0x74, 0xa4, 0x22, 0x4c, 0x6f, 0xbf, 0x1f, 0x56, 0xaa, 0x2e, 0xb3, 0x78, 0x33, 0x50,
+	    0xb0, 0xa3, 0x92, 0xbc, 0xcf, 0x19, 0x1c, 0xa7, 0x63, 0xcb, 0x1e, 0x4d, 0x3e, 0x4b, 0x1b, 0x9b,
+	    0x4f, 0xe7, 0xf0, 0xee, 0xad, 0x3a, 0xb5, 0x59, 0x04, 0xea, 0x40, 0x55, 0x25, 0x51, 0xe5, 0x7a,
+	    0x89, 0x38, 0x68, 0x52, 0x7b, 0xfc, 0x27, 0xae, 0xd7, 0xbd, 0xfa, 0x07, 0xf4, 0xcc, 0x8e, 0x5f,
+	    0xef, 0x35, 0x9c, 0x84, 0x2b, 0x15, 0xd5, 0x77, 0x34, 0x49, 0xb6, 0x12, 0x0a, 0x7f, 0x71, 0x88,
+	    0xfd, 0x9d, 0x18, 0x41, 0x7d, 0x93, 0xd8, 0x58, 0x2c, 0xce, 0xfe, 0x24, 0xaf, 0xde, 0xb8, 0x36,
+	    0xc8, 0xa1, 0x80, 0xa6, 0x99, 0x98, 0xa8, 0x2f, 0x0e, 0x81, 0x65, 0x73, 0xe4, 0xc2, 0xa2, 0x8a,
+	    0xd4, 0xe1, 0x11, 0xd0, 0x08, 0x8b, 0x2a, 0xf2, 0xed, 0x9a, 0x64, 0x3f, 0xc1, 0x6c, 0xf9, 0xec
+	};
+	
+	/**
+	 * Output a dump of data in hex format in the order it was read in
+	 * @param data
+	 * @param pretty
+	 */
+	public static void printHexFormatted(byte[] data, boolean pretty) {
+		printHexFormatted(data,pretty, new int[0]);
+	}
+	protected static void printHexFormatted(byte[] data, boolean pretty, int[] indexes) {
+		// groups of two
+		if (pretty) { System.out.println("---"); }
+		long tmpLongValue;
+		String line = "";
+		int nextIndex = 0;
+		int indexIndex = 0;
+		if (indexes.length > 0) {
+			nextIndex = indexes[0];
+			indexIndex++;
+		}
+		for (int x = 0; x < data.length; x++) {
+			tmpLongValue = (long)data[x] & 0xff;
+			
+			if (indexes.length > 0 &&
+				x == nextIndex &&
+				nextIndex < data.length)
+			{
+				System.out.print("+");
+				line += "+";
+				while (indexIndex < indexes.length-1 && indexes[indexIndex] <= nextIndex) 
+				{
+					indexIndex++;
+				}
+				nextIndex = indexes[indexIndex];
+				//indexIndex++;
+			}
+			
+			if (Character.isLetterOrDigit((char)tmpLongValue)) {
+				line += (char)tmpLongValue;
+			}
+			else
+			{
+				line += ".";
+			}
+			
+			if (Long.toHexString(tmpLongValue).length() < 2) {
+				System.out.print("0");
+			}
+			System.out.print(Long.toHexString(tmpLongValue));
+			if (x % 2 == 1 && pretty) {
+				System.out.print(" ");
+			}
+			if (x % 16 == 15 && pretty) {
+				System.out.print(" "+line);
+				System.out.println("");
+				line = "";
+			}
+		}
+		if (pretty) { System.out.println(" "+line); System.out.println("---"); System.out.println(data.length); } else {  }
+	}
+	
+
+	
+	/**
+	 * decode a lump of data that has been encrypted with the compressible encryption
+	 * @param data
+	 * @return decoded data
+	 */
+	public static byte[] decode(byte[] data) {
+		int temp;
+		for (int x = 0; x < data.length; x++) {
+			temp = data[x] & 0xff;
+			data[x] = (byte)compEnc[temp];
+		}
+		return data;
+	}
+	
+
+	public static byte[] encode(byte[] data) {
+		// create the encoding array...
+		int[] enc = new int[compEnc.length];
+		for (int x = 0; x < enc.length; x++) {
+			enc[compEnc[x]] = x;
+		}
+		
+		// now it's just the same as decode...
+		int temp;
+		for (int x = 0; x < data.length; x++) {
+			temp = data[x] & 0xff;
+			data[x] = (byte)enc[temp];
+		}
+		return data;
+	}
+
+	
+	/**
+	 * Utility function for converting little endian bytes into a usable java long
+	 * @param data
+	 * @return long version of the data
+	 */
+	public static long convertLittleEndianBytesToLong(byte[] data) {
+		return convertLittleEndianBytesToLong(data, 0, data.length);
+	}
+	/**
+	 * Utility function for converting little endian bytes into a usable java long
+	 * @param data
+	 * @param start
+	 * @param end
+	 * @return long version of the data
+	 */
+	public static long convertLittleEndianBytesToLong(byte[] data, int start, int end) {
+		
+		long offset = data[end-1] & 0xff;
+		long tmpLongValue;
+		for (int x = end-2; x >= start; x--) {
+			offset = offset << 8;
+			tmpLongValue = (long)data[x] & 0xff;
+			offset |= tmpLongValue;
+		}
+		
+		return offset;
+	}
+	
+	/**
+	 * Utility function for converting big endian bytes into a usable java long
+	 * @param data
+	 * @param start
+	 * @param end
+	 * @return long version of the data
+	 */
+	public static long convertBigEndianBytesToLong(byte[] data, int start, int end) {
+		
+		long offset = 0;
+		for ( int x = start; x < end; ++x ) {
+			offset = offset << 8;
+			offset |= ((long)data[x] & 0xFFL);
+		}
+		
+		return offset;
+	}
+/*
+	protected static boolean isPSTArray(byte[] data) {
+		return (data[0] == 1 && data[1] == 1);
+	}
+/**/	
+/*	
+	protected static int[] getBlockOffsets(RandomAccessFile in, byte[] data)
+		throws IOException, PSTException
+	{
+		// is the data an array?
+		if (!(data[0] == 1 && data[1] == 1))
+		{
+			throw new PSTException("Unable to process array, does not appear to be one!");
+		}
+
+		// we are an array!
+		// get the array items and merge them together
+		int numberOfEntries = (int)PSTObject.convertLittleEndianBytesToLong(data, 2, 4);
+		int[] output = new int[numberOfEntries];
+		int tableOffset = 8;
+		int blockOffset = 0;
+		for (int y = 0; y < numberOfEntries; y++) {
+			// get the offset identifier
+			long tableOffsetIdentifierIndex = PSTObject.convertLittleEndianBytesToLong(data, tableOffset, tableOffset+8);
+			// clear the last bit of the identifier.  Why so hard?
+			tableOffsetIdentifierIndex = (tableOffsetIdentifierIndex & 0xfffffffe);
+			OffsetIndexItem tableOffsetIdentifier = PSTObject.getOffsetIndexNode(in, tableOffsetIdentifierIndex);
+			blockOffset += tableOffsetIdentifier.size;
+			output[y] = blockOffset;
+			tableOffset += 8;
+		}
+
+		// replace the item data with the stuff from the array...
+		return output;
+	}
+/**/
+
+	
+
+	
+	
+	/**
+	 * the code below was taken from a random apache project
+	 * http://www.koders.com/java/fidA9D4930E7443F69F32571905DD4CA01E4D46908C.aspx
+	 * my bit-shifting isn't that 1337
+	 */
+	
+	/**
+     * 
The difference between the Windows epoch (1601-01-01
+     * 00:00:00) and the Unix epoch (1970-01-01 00:00:00) in
+     * milliseconds: 11644473600000L. (Use your favorite spreadsheet
+     * program to verify the correctness of this value. By the way,
+     * did you notice that you can tell from the epochs which
+     * operating system is the modern one? :-))
+     */
+    private static final long EPOCH_DIFF = 11644473600000L;
+	
+	/**
+     * Converts a Windows FILETIME into a {@link Date}. The Windows
+     * FILETIME structure holds a date and time associated with a
+     * file. The structure identifies a 64-bit integer specifying the
+     * number of 100-nanosecond intervals which have passed since
+     * January 1, 1601. This 64-bit value is split into the two double
+     * words stored in the structure.
+     *
+     * @param high The higher double word of the FILETIME structure.
+     * @param low The lower double word of the FILETIME structure.
+     * @return The Windows FILETIME as a {@link Date}.
+     */
+    public static Date filetimeToDate(final int high, final int low) {
+        final long filetime = ((long) high) << 32 | (low & 0xffffffffL);
+		//System.out.printf("0x%X\n", filetime);
+        final long ms_since_16010101 = filetime / (1000 * 10);
+        final long ms_since_19700101 = ms_since_16010101 - EPOCH_DIFF;
+        return new Date(ms_since_19700101);
+    }
+
+    public static Calendar apptTimeToCalendar(int minutes) {
+    	final long ms_since_16010101 = minutes * (60*1000L);
+        final long ms_since_19700101 = ms_since_16010101 - EPOCH_DIFF;
+        Calendar c = Calendar.getInstance(PSTTimeZone.utcTimeZone);
+        c.setTimeInMillis(ms_since_19700101);
+        return c;
+    }
+    
+    public static Calendar apptTimeToUTC(int minutes, PSTTimeZone tz) {
+		// Must convert minutes since 1/1/1601 in local time to UTC
+		// There's got to be a better way of doing this...
+		// First get a UTC calendar object that contains _local time_
+		Calendar cUTC = apptTimeToCalendar(minutes);
+		if ( tz != null ) {
+			// Create an empty Calendar object with the required time zone
+			Calendar cLocal = Calendar.getInstance(tz.getSimpleTimeZone());
+			cLocal.clear();
+			
+			// Now transfer the local date/time from the UTC calendar object
+			// to the object that knows about the time zone...
+			cLocal.set(cUTC.get(Calendar.YEAR),
+					   cUTC.get(Calendar.MONTH),
+					   cUTC.get(Calendar.DATE),
+					   cUTC.get(Calendar.HOUR_OF_DAY),
+					   cUTC.get(Calendar.MINUTE),
+					   cUTC.get(Calendar.SECOND));
+			
+			// Get the true UTC from the local time calendar object.
+			// Drop any milliseconds, they won't be printed anyway!
+			long utcs = cLocal.getTimeInMillis() / 1000;
+			
+			// Finally, set the true UTC in the UTC calendar object
+			cUTC.setTimeInMillis(utcs * 1000);
+		} // else hope for the best!
+
+		return cUTC;
+	}
+
+    
+    
+    
+    public static int getCompEnc(int value) {
+    	return compEnc[value];
+    }
+    
+    
+    public static String decodeLZFU(byte[] data) throws PSTException {
+
+		@SuppressWarnings("unused")
+		int compressedSize = (int)PSTUtils.convertLittleEndianBytesToLong(data, 0, 4);
+		int uncompressedSize = (int)PSTUtils.convertLittleEndianBytesToLong(data, 4, 8);
+		int compressionSig =  (int)PSTUtils.convertLittleEndianBytesToLong(data, 8, 12);
+		@SuppressWarnings("unused")
+		int compressedCRC =  (int)PSTUtils.convertLittleEndianBytesToLong(data, 12, 16);
+
+		if (compressionSig == 0x75465a4c) {
+			// we are compressed...
+			byte[] output = new byte[uncompressedSize];
+			int outputPosition = 0;
+			byte[] lzBuffer = new byte[4096];
+			// preload our buffer.
+			try {
+				byte[] bytes = LZFU_HEADER.getBytes("US-ASCII");
+				System.arraycopy(bytes, 0, lzBuffer, 0, LZFU_HEADER.length());
+			} catch (UnsupportedEncodingException e) {
+				e.printStackTrace();
+			}
+			int bufferPosition = LZFU_HEADER.length();
+			int currentDataPosition = 16;
+
+			// next byte is the flags,
+			while (currentDataPosition < data.length - 2 && outputPosition < output.length) {
+				int flags = data[currentDataPosition++] & 0xFF;
+				for (int x = 0; x < 8 && outputPosition < output.length; x++) {
+					boolean isRef = ((flags & 1) == 1);
+					flags >>= 1;
+					if (isRef) {
+						// get the starting point for the buffer and the
+						// length to read
+						int refOffsetOrig = data[currentDataPosition++] & 0xFF;
+						int refSizeOrig = data[currentDataPosition++] & 0xFF;
+						int refOffset = (refOffsetOrig << 4) | (refSizeOrig >>> 4);
+						int refSize = (refSizeOrig & 0xF) + 2;
+						//refOffset &= 0xFFF;
+						try {
+							// copy the data from the buffer
+							int index = refOffset;
+							for (int y = 0; y < refSize && outputPosition < output.length; y++) {
+								output[outputPosition++] = lzBuffer[index];
+								lzBuffer[bufferPosition] = lzBuffer[index];
+								bufferPosition++;
+								bufferPosition %= 4096;
+								++index;
+								index %= 4096;
+							}
+						} catch ( Exception e ) {
+							e.printStackTrace();
+						}
+
+					} else {
+						// copy the byte over
+						lzBuffer[bufferPosition] = 	data[currentDataPosition];
+						bufferPosition++;
+						bufferPosition %= 4096;
+						output[outputPosition++] = data[currentDataPosition++];
+					}
+				}
+			}
+
+			if ( outputPosition != uncompressedSize ) {
+				throw new PSTException(String.format("Error decompressing RTF! Expected %d bytes, got %d bytes\n", uncompressedSize, outputPosition));
+			}
+			return new String(output).trim();
+
+		} else if (compressionSig == 0x414c454d) {
+			// we are not compressed!
+			// just return the rest of the contents as a string
+			byte[] output = new byte[data.length-16];
+			System.arraycopy(data, 16, output, 0, data.length-16);
+			return new String(output).trim();
+		}
+
+		return "";
+	}
+    
+}
diff --git a/com/pff/PSTAppointmentException.java b/com/pff/exceptions/PSTAppointmentException.java
similarity index 60%
rename from com/pff/PSTAppointmentException.java
rename to com/pff/exceptions/PSTAppointmentException.java
index 9230070..8e88ba1 100644
--- a/com/pff/PSTAppointmentException.java
+++ b/com/pff/exceptions/PSTAppointmentException.java
@@ -32,12 +32,16 @@
  *
  */
 
-package com.pff;
+package com.pff.exceptions;
 
 import java.io.UnsupportedEncodingException;
 import java.util.Calendar;
 import java.util.Date;
 
+import com.pff.PSTUtils;
+import com.pff.objects.PSTAppointment;
+import com.pff.objects.sub.PSTTimeZone;
+
 /**
  * Class containing information on exceptions to a recurring appointment
  * @author Orin Eman
@@ -49,9 +53,9 @@ public class PSTAppointmentException {
 	// Access methods - return the value from the exception if
 	// OverrideFlags say it's present, otherwise the value from the appointment.
 	public String getSubject() {
-		if ( (OverrideFlags & 0x0001) != 0 ) {
+		if ( (overrideFlags & 0x0001) != 0 ) {
 			try {
-				return new String(WideCharSubject, "UTF-16LE");
+				return new String(wideCharSubject, "UTF-16LE");
 			} catch (UnsupportedEncodingException e) {
 				e.printStackTrace();
 			}
@@ -63,8 +67,8 @@ public String getSubject() {
 	
 	public int getMeetingType()
 	{
-		if ( (OverrideFlags & 0x0002) != 0 ) {
-			return MeetingType;
+		if ( (overrideFlags & 0x0002) != 0 ) {
+			return meetingType;
 		}
 		
 		return appt.getMeetingStatus();
@@ -72,7 +76,7 @@ public int getMeetingType()
 
 	
 	public int getReminderDelta() {
-		if ( (OverrideFlags & 0x0004) != 0 ) {
+		if ( (overrideFlags & 0x0004) != 0 ) {
 			return ReminderDelta;
 		}
 		
@@ -81,7 +85,7 @@ public int getReminderDelta() {
 
 	
 	public boolean getReminderSet() {
-		if ( (OverrideFlags & 0x0008) != 0 ) {
+		if ( (overrideFlags & 0x0008) != 0 ) {
 			return ReminderSet;
 		}
 		
@@ -90,9 +94,9 @@ public boolean getReminderSet() {
 	
 	
 	public String getLocation() {
-		if ( (OverrideFlags & 0x0010) != 0 ) {
+		if ( (overrideFlags & 0x0010) != 0 ) {
 			try {
-				return new String(WideCharLocation, "UTF-16LE");
+				return new String(wideCharLocation, "UTF-16LE");
 			} catch (UnsupportedEncodingException e) {
 				e.printStackTrace();
 			}
@@ -103,8 +107,8 @@ public String getLocation() {
 
 	
 	public int getBusyStatus() {
-		if ( (OverrideFlags & 0x0020) != 0 ) {
-			return BusyStatus;
+		if ( (overrideFlags & 0x0020) != 0 ) {
+			return busyStatus;
 		}
 		
 		return appt.getBusyStatus();
@@ -112,8 +116,8 @@ public int getBusyStatus() {
 	
 	
 	public boolean getSubType() {
-		if ( (OverrideFlags & 0x0080) != 0 ) {
-			return SubType;
+		if ( (overrideFlags & 0x0080) != 0 ) {
+			return subType;
 		}
 		
 		return appt.getSubType();
@@ -144,15 +148,15 @@ public Date getDTStamp() {
 	}
 	
 	public int getStartDateTime() {
-		return StartDateTime;
+		return startDateTime;
 	}
 	
 	public int getEndDateTime() {
-		return EndDateTime;
+		return endDateTime;
 	}
 	
 	public int getOriginalStartDate() {
-		return OriginalStartDate;
+		return originalStartDate;
 	}
 	
 	public int getAppointmentSequence(int def) {
@@ -170,28 +174,23 @@ public int getImportance(int def) {
 	}
 	
 	public byte[] getSubjectBytes() {
-		if ( (OverrideFlags & 0x0010) != 0 ) {
+		if ( (overrideFlags & 0x0010) != 0 ) {
 			return Subject;
 		}
-		
 		return null;
 	}
 	
 	public byte[] getLocationBytes() {
-		if ( (OverrideFlags & 0x0010) != 0 ) {
-			return Location;
+		if ( (overrideFlags & 0x0010) != 0 ) {
+			return location;
 		}
-
 		return null;
 	}
 	
 	public boolean attachmentsPresent() {
-		if ( (OverrideFlags & 0x0040) != 0 &&
-			  Attachment == 0x00000001 )
-		{
+		if ( (overrideFlags & 0x0040) != 0 &&  attachment == 0x00000001 ) {
 			return true;
 		}
-		
 		return false;
 	}
 	
@@ -207,109 +206,106 @@ public PSTAppointment getEmbeddedMessage() {
 		return embeddedMessage;
 	}
 
-	PSTAppointmentException(byte[] recurrencePattern,
-			int offset,
-			int writerVersion2,
-			PSTAppointment appt) {
+	public PSTAppointmentException(byte[] recurrencePattern, int offset, int writerVersion2, PSTAppointment appt) {
 		this.writerVersion2 = writerVersion2;
 		int initialOffset = offset;
 		this.appt = appt;
 		embeddedMessage = null;
 
-		StartDateTime = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		startDateTime = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 		offset += 4;
-		EndDateTime = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		endDateTime = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 		offset += 4;
-		OriginalStartDate = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		originalStartDate = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 		offset += 4;
-		OverrideFlags = (short)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+2);
+		overrideFlags = (short)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+2);
 		offset += 2;
 		
-		if ( (OverrideFlags & ARO_SUBJECT) != 0 ) {
+		if ( (overrideFlags & ARO_SUBJECT) != 0 ) {
 			//@SuppressWarnings("unused")
 			//short SubjectLength = (short)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+2);
 			offset += 2;
-			short SubjectLength2 = (short)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+2);
+			short SubjectLength2 = (short)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+2);
 			offset += 2;
 			Subject = new byte[SubjectLength2];
 			System.arraycopy(recurrencePattern, offset, Subject, 0, SubjectLength2);
 			offset += SubjectLength2;
 		}
 		
-		if ( (OverrideFlags & ARO_MEETINGTYPE) != 0 ) {
-			MeetingType = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		if ( (overrideFlags & ARO_MEETINGTYPE) != 0 ) {
+			meetingType = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 			offset += 4;
 		}
 
-		if ( (OverrideFlags & ARO_REMINDERDELTA) != 0 ) {
-			ReminderDelta = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		if ( (overrideFlags & ARO_REMINDERDELTA) != 0 ) {
+			ReminderDelta = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 			offset += 4;
 		}
 
-		if ( (OverrideFlags & ARO_REMINDER) != 0 ) {
-			ReminderSet = ((int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4) != 0);
+		if ( (overrideFlags & ARO_REMINDER) != 0 ) {
+			ReminderSet = ((int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4) != 0);
 			offset += 4;
 		}
 
-		if ( (OverrideFlags & ARO_LOCATION) != 0 ) {
+		if ( (overrideFlags & ARO_LOCATION) != 0 ) {
 			//@SuppressWarnings("unused")
 			//short LocationLength = (short)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+2);
 			offset += 2;
-			short LocationLength2 = (short)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+2);
+			short LocationLength2 = (short)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+2);
 			offset += 2;
-			Location = new byte[LocationLength2];
-			System.arraycopy(recurrencePattern, offset, Location, 0, LocationLength2);
+			location = new byte[LocationLength2];
+			System.arraycopy(recurrencePattern, offset, location, 0, LocationLength2);
 			offset += LocationLength2;
 		}
 
-		if ( (OverrideFlags & ARO_BUSYSTATUS) != 0 ) {
-			BusyStatus = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		if ( (overrideFlags & ARO_BUSYSTATUS) != 0 ) {
+			busyStatus = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 			offset += 4;
 		}
 
-		if ( (OverrideFlags & ARO_ATTACHMENT) != 0 ) {
-			Attachment = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		if ( (overrideFlags & ARO_ATTACHMENT) != 0 ) {
+			attachment = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 			offset += 4;
 		}
 
-		if ( (OverrideFlags & ARO_SUBTYPE) != 0 ) {
-			SubType = ((int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4) != 0);
+		if ( (overrideFlags & ARO_SUBTYPE) != 0 ) {
+			subType = ((int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4) != 0);
 			offset += 4;
 		}
 
 		length = offset - initialOffset;
 	}
 	
-	void ExtendedException(byte[] recurrencePattern, int offset) {
+	public void buildExtendedException(byte[] recurrencePattern, int offset) {
 		int initialOffset = offset;
 		
 		if ( writerVersion2 >= 0x00003009 ) {
-			int ChangeHighlightSize = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+			int ChangeHighlightSize = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 			offset += 4;
-			ChangeHighlightValue = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+			changeHighlightValue = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 			offset += ChangeHighlightSize;
 		}
 		
-		int ReservedBlockEESize = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		int ReservedBlockEESize = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 		offset += 4 + ReservedBlockEESize;
 
 		// See http://msdn.microsoft.com/en-us/library/cc979209(office.12).aspx
-		if ( (OverrideFlags & (ARO_SUBJECT|ARO_LOCATION)) != 0 ) {
+		if ( (overrideFlags & (ARO_SUBJECT|ARO_LOCATION)) != 0 ) {
 			// Same as regular Exception structure?
-			StartDateTime = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+			startDateTime = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 			offset += 4;
-			EndDateTime = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+			endDateTime = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 			offset += 4;
-			OriginalStartDate = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+			originalStartDate = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 			offset += 4;
 		}
 
-		if ( (OverrideFlags & ARO_SUBJECT) != 0 ) {
-			WideCharSubjectLength = (short)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+2);
+		if ( (overrideFlags & ARO_SUBJECT) != 0 ) {
+			wideCharSubjectLength = (short)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+2);
 			offset += 2;
-			WideCharSubject = new byte[WideCharSubjectLength * 2];
-			System.arraycopy(recurrencePattern, offset, WideCharSubject, 0, WideCharSubject.length);
-			offset += WideCharSubject.length;
+			wideCharSubject = new byte[wideCharSubjectLength * 2];
+			System.arraycopy(recurrencePattern, offset, wideCharSubject, 0, wideCharSubject.length);
+			offset += wideCharSubject.length;
 /*			
 			try {
 				String subject = new String(WideCharSubject, "UTF-16LE");
@@ -320,47 +316,47 @@ void ExtendedException(byte[] recurrencePattern, int offset) {
 /**/
 		}
 	
-		if ( (OverrideFlags & ARO_LOCATION) != 0 ) {
-			WideCharLocationLength = (short)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+2);
+		if ( (overrideFlags & ARO_LOCATION) != 0 ) {
+			wideCharLocationLength = (short)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+2);
 			offset += 2;
-			WideCharLocation = new byte[WideCharLocationLength*2];
-			System.arraycopy(recurrencePattern, offset, WideCharLocation, 0, WideCharLocation.length);
-			offset += WideCharLocation.length;
+			wideCharLocation = new byte[wideCharLocationLength*2];
+			System.arraycopy(recurrencePattern, offset, wideCharLocation, 0, wideCharLocation.length);
+			offset += wideCharLocation.length;
 		}
 		
 		// See http://msdn.microsoft.com/en-us/library/cc979209(office.12).aspx
-		if ( (OverrideFlags & (ARO_SUBJECT|ARO_LOCATION)) != 0 ) {
-			ReservedBlockEESize = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		if ( (overrideFlags & (ARO_SUBJECT|ARO_LOCATION)) != 0 ) {
+			ReservedBlockEESize = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 			offset += 4 + ReservedBlockEESize;
 		}
 
 		extendedLength = offset - initialOffset;
 	}
 	
-	void setEmbeddedMessage(PSTAppointment embeddedMessage) {
+	public void setEmbeddedMessage(PSTAppointment embeddedMessage) {
 		this.embeddedMessage = embeddedMessage;
 	}
 	
 	private int		writerVersion2;
-	private int		StartDateTime;
-	private int		EndDateTime;
-	private int		OriginalStartDate;
-	private short	OverrideFlags;
+	private int		startDateTime;
+	private int		endDateTime;
+	private int		originalStartDate;
+	private short	overrideFlags;
 	private byte[]	Subject = null;
-	private int		MeetingType;
+	private int		meetingType;
 	private int		ReminderDelta;
 	private boolean	ReminderSet;
-	private byte[]	Location = null;
-	private int		BusyStatus;
-	private int		Attachment;
-	private boolean	SubType;
+	private byte[]	location = null;
+	private int		busyStatus;
+	private int		attachment;
+	private boolean	subType;
 //	private int		AppointmentColor;	// Reserved - don't read from the PST file
 	@SuppressWarnings("unused")
-	private int		ChangeHighlightValue;
-	private int		WideCharSubjectLength = 0;
-	private byte[]	WideCharSubject = null;
-	private int		WideCharLocationLength = 0;
-	private byte[]	WideCharLocation = null;
+	private int		changeHighlightValue;
+	private int		wideCharSubjectLength = 0;
+	private byte[]	wideCharSubject = null;
+	private int		wideCharLocationLength = 0;
+	private byte[]	wideCharLocation = null;
 	private PSTAppointment	embeddedMessage = null;
 	private PSTAppointment	appt;
 	private int 	length;
@@ -368,12 +364,12 @@ void setEmbeddedMessage(PSTAppointment embeddedMessage) {
 	
 	
 	// Length of this ExceptionInfo structure in the PST file
-	int getLength() {
+	public int getLength() {
 		return length;
 	}
 
 	// Length of this ExtendedException structure in the PST file
-	int getExtendedLength() {
+	public int getExtendedLength() {
 		return extendedLength;
 	}
 
diff --git a/com/pff/PSTException.java b/com/pff/exceptions/PSTException.java
similarity index 93%
rename from com/pff/PSTException.java
rename to com/pff/exceptions/PSTException.java
index 36d570e..b573ced 100644
--- a/com/pff/PSTException.java
+++ b/com/pff/exceptions/PSTException.java
@@ -31,7 +31,7 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.exceptions;
 
 /**
  * Simple exception for PST File related errors
@@ -44,10 +44,10 @@ public class PSTException extends Exception
 	 */
 	private static final long serialVersionUID = 4284698344354718143L;
 	
-	PSTException(String error) {
+	public PSTException(String error) {
 		super(error);
 	}
-	PSTException(String error, Exception orig) {
+	public PSTException(String error, Exception orig) {
 		super(error, orig);
 	}
 }
diff --git a/com/pff/PSTActivity.java b/com/pff/objects/PSTActivity.java
similarity index 80%
rename from com/pff/PSTActivity.java
rename to com/pff/objects/PSTActivity.java
index d4cae3d..460bf2a 100644
--- a/com/pff/PSTActivity.java
+++ b/com/pff/objects/PSTActivity.java
@@ -31,11 +31,17 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.objects;
 
 import java.io.IOException;
-import java.util.HashMap;
 import java.util.Date;
+import java.util.HashMap;
+
+import com.pff.exceptions.PSTException;
+import com.pff.parsing.DescriptorIndexNode;
+import com.pff.parsing.PSTDescriptorItem;
+import com.pff.parsing.tables.PSTTableBC;
+import com.pff.source.PSTSource;
 
 /**
  * PSTActivity represents Journal entries 
@@ -49,7 +55,7 @@ public class PSTActivity extends PSTMessage {
 	 * @throws PSTException
 	 * @throws IOException
 	 */
-	public PSTActivity(PSTFile theFile, DescriptorIndexNode descriptorIndexNode)
+	public PSTActivity(PSTSource theFile, DescriptorIndexNode descriptorIndexNode)
 			throws PSTException, IOException {
 		super(theFile, descriptorIndexNode);
 	}
@@ -60,7 +66,7 @@ public PSTActivity(PSTFile theFile, DescriptorIndexNode descriptorIndexNode)
 	 * @param table
 	 * @param localDescriptorItems
 	 */
-	public PSTActivity(PSTFile theFile, DescriptorIndexNode folderIndexNode,
+	public PSTActivity(PSTSource theFile, DescriptorIndexNode folderIndexNode,
 			PSTTableBC table,
 			HashMap localDescriptorItems) {
 		super(theFile, folderIndexNode, table, localDescriptorItems);
@@ -70,61 +76,61 @@ public PSTActivity(PSTFile theFile, DescriptorIndexNode folderIndexNode,
 	 * Type
 	 */
 	public String getLogType() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008700, PSTFile.PSETID_Log));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008700, PSTSource.PSETID_Log));
 	}
 	/**
 	 * Start
 	 */
 	public Date getLogStart() {
-		return getDateItem(pstFile.getNameToIdMapItem(0x00008706, PSTFile.PSETID_Log));
+		return getDateItem(pstFile.getNameToIdMapItem(0x00008706, PSTSource.PSETID_Log));
 	}
 	/**
 	 * Duration
 	 */
 	public int getLogDuration() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008707, PSTFile.PSETID_Log));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008707, PSTSource.PSETID_Log));
 	}
 	/**
 	 * End
 	 */
 	public Date getLogEnd() {
-		return getDateItem(pstFile.getNameToIdMapItem(0x00008708, PSTFile.PSETID_Log));
+		return getDateItem(pstFile.getNameToIdMapItem(0x00008708, PSTSource.PSETID_Log));
 	}
 	/**
 	 * LogFlags
 	 */
 	public int getLogFlags() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x0000870c, PSTFile.PSETID_Log));
+		return getIntItem(pstFile.getNameToIdMapItem(0x0000870c, PSTSource.PSETID_Log));
 	}
 	/**
 	 * DocPrinted
 	 */
 	public boolean isDocumentPrinted() {
-		return (getBooleanItem(pstFile.getNameToIdMapItem(0x0000870e, PSTFile.PSETID_Log)));
+		return (getBooleanItem(pstFile.getNameToIdMapItem(0x0000870e, PSTSource.PSETID_Log)));
 	}
 	/**
 	 * DocSaved
 	 */
 	public boolean isDocumentSaved() {
-		return (getBooleanItem(pstFile.getNameToIdMapItem(0x0000870f, PSTFile.PSETID_Log)));
+		return (getBooleanItem(pstFile.getNameToIdMapItem(0x0000870f, PSTSource.PSETID_Log)));
 	}
 	/**
 	 * DocRouted
 	 */
 	public boolean isDocumentRouted() {
-		return (getBooleanItem(pstFile.getNameToIdMapItem(0x00008710, PSTFile.PSETID_Log)));
+		return (getBooleanItem(pstFile.getNameToIdMapItem(0x00008710, PSTSource.PSETID_Log)));
 	}
 	/**
 	 * DocPosted
 	 */
 	public boolean isDocumentPosted() {
-		return (getBooleanItem(pstFile.getNameToIdMapItem(0x00008711, PSTFile.PSETID_Log)));
+		return (getBooleanItem(pstFile.getNameToIdMapItem(0x00008711, PSTSource.PSETID_Log)));
 	}
 	/**
 	 * Type Description
 	 */
 	public String getLogTypeDesc() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008712, PSTFile.PSETID_Log));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008712, PSTSource.PSETID_Log));
 	}
 
 	public String toString() {
diff --git a/com/pff/PSTAppointment.java b/com/pff/objects/PSTAppointment.java
similarity index 62%
rename from com/pff/PSTAppointment.java
rename to com/pff/objects/PSTAppointment.java
index b82216e..df69cce 100644
--- a/com/pff/PSTAppointment.java
+++ b/com/pff/objects/PSTAppointment.java
@@ -31,59 +31,67 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.objects;
 
 import java.io.IOException;
 import java.util.Date;
 import java.util.HashMap;
 
+import com.pff.exceptions.PSTException;
+import com.pff.objects.sub.PSTGlobalObjectId;
+import com.pff.objects.sub.PSTTimeZone;
+import com.pff.parsing.DescriptorIndexNode;
+import com.pff.parsing.PSTDescriptorItem;
+import com.pff.parsing.tables.PSTTableBC;
+import com.pff.source.PSTSource;
+
 /**
  * PSTAppointment is for Calendar items
  * @author Richard Johnson
  */
 public class PSTAppointment extends PSTMessage {
 
-	PSTAppointment(PSTFile theFile, DescriptorIndexNode descriptorIndexNode)
+	public PSTAppointment(PSTSource theFile, DescriptorIndexNode descriptorIndexNode)
 		throws PSTException, IOException
 	{
 		super(theFile, descriptorIndexNode);
 	}
 
-	PSTAppointment(PSTFile theFile, DescriptorIndexNode folderIndexNode, PSTTableBC table, HashMap localDescriptorItems)
+	public PSTAppointment(PSTSource theFile, DescriptorIndexNode folderIndexNode, PSTTableBC table, HashMap localDescriptorItems)
 	{
 		super(theFile, folderIndexNode, table, localDescriptorItems);
 	}
 
 	public boolean getSendAsICAL() {
-		return (getBooleanItem(pstFile.getNameToIdMapItem(0x00008200, PSTFile.PSETID_Appointment)));
+		return (getBooleanItem(pstFile.getNameToIdMapItem(0x00008200, PSTSource.PSETID_Appointment)));
 	}
 	public int getBusyStatus()
 	{
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008205, PSTFile.PSETID_Appointment));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008205, PSTSource.PSETID_Appointment));
 	}
 	public boolean getShowAsBusy() {
 		return getBusyStatus() == 2;
 	}
 	public String getLocation() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008208, PSTFile.PSETID_Appointment));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008208, PSTSource.PSETID_Appointment));
 	}
 	public Date getStartTime() {
-		return getDateItem(pstFile.getNameToIdMapItem(0x0000820d, PSTFile.PSETID_Appointment));
+		return getDateItem(pstFile.getNameToIdMapItem(0x0000820d, PSTSource.PSETID_Appointment));
 	}
 	public PSTTimeZone getStartTimeZone() {
-		return getTimeZoneItem(pstFile.getNameToIdMapItem(0x0000825e, PSTFile.PSETID_Appointment));
+		return getTimeZoneItem(pstFile.getNameToIdMapItem(0x0000825e, PSTSource.PSETID_Appointment));
 	}
 	public Date getEndTime() {
-		return getDateItem(pstFile.getNameToIdMapItem(0x0000820e, PSTFile.PSETID_Appointment));
+		return getDateItem(pstFile.getNameToIdMapItem(0x0000820e, PSTSource.PSETID_Appointment));
 	}
 	public PSTTimeZone getEndTimeZone() {
-		return getTimeZoneItem(pstFile.getNameToIdMapItem(0x0000825f, PSTFile.PSETID_Appointment));
+		return getTimeZoneItem(pstFile.getNameToIdMapItem(0x0000825f, PSTSource.PSETID_Appointment));
 	}
 	
 	public PSTTimeZone getRecurrenceTimeZone() {
-		String desc = getStringItem(pstFile.getNameToIdMapItem(0x00008234, PSTFile.PSETID_Appointment));
+		String desc = getStringItem(pstFile.getNameToIdMapItem(0x00008234, PSTSource.PSETID_Appointment));
 		if ( desc!= null && desc.length() != 0 ) {
-			byte[] tzData = getBinaryItem(pstFile.getNameToIdMapItem(0x00008233, PSTFile.PSETID_Appointment));
+			byte[] tzData = getBinaryItem(pstFile.getNameToIdMapItem(0x00008233, PSTSource.PSETID_Appointment));
 			if ( tzData != null && tzData.length != 0 ) {
 				return new PSTTimeZone(desc, tzData);
 			}
@@ -91,103 +99,110 @@ public PSTTimeZone getRecurrenceTimeZone() {
 		return null;
 	}
 	public int getDuration() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008213, PSTFile.PSETID_Appointment));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008213, PSTSource.PSETID_Appointment));
 	}
 	public int getColor() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008214, PSTFile.PSETID_Appointment));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008214, PSTSource.PSETID_Appointment));
 	}
 	public boolean getSubType() {
-		return (getIntItem(pstFile.getNameToIdMapItem(0x00008215, PSTFile.PSETID_Appointment)) != 0);
+		return (getIntItem(pstFile.getNameToIdMapItem(0x00008215, PSTSource.PSETID_Appointment)) != 0);
 	}
 	public int getMeetingStatus() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008217, PSTFile.PSETID_Appointment));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008217, PSTSource.PSETID_Appointment));
 	}
 	public int getResponseStatus() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008218, PSTFile.PSETID_Appointment));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008218, PSTSource.PSETID_Appointment));
 	}
 	public boolean isRecurring() {
-		return getBooleanItem(pstFile.getNameToIdMapItem(0x00008223, PSTFile.PSETID_Appointment));
+		return getBooleanItem(pstFile.getNameToIdMapItem(0x00008223, PSTSource.PSETID_Appointment));
 	}
 	public Date getRecurrenceBase() {
-		return getDateItem(pstFile.getNameToIdMapItem(0x00008228, PSTFile.PSETID_Appointment));
+		return getDateItem(pstFile.getNameToIdMapItem(0x00008228, PSTSource.PSETID_Appointment));
 	}
 	public int getRecurrenceType() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008231, PSTFile.PSETID_Appointment));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008231, PSTSource.PSETID_Appointment));
 	}
 	public String getRecurrencePattern() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008232, PSTFile.PSETID_Appointment));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008232, PSTSource.PSETID_Appointment));
 	}
 	public byte[] getRecurrenceStructure() {
-		return getBinaryItem(pstFile.getNameToIdMapItem(0x00008216, PSTFile.PSETID_Appointment));
+		return getBinaryItem(pstFile.getNameToIdMapItem(0x00008216, PSTSource.PSETID_Appointment));
 	}
 	public byte[] getTimezone() {
-		return getBinaryItem(pstFile.getNameToIdMapItem(0x00008233, PSTFile.PSETID_Appointment));
+		return getBinaryItem(pstFile.getNameToIdMapItem(0x00008233, PSTSource.PSETID_Appointment));
 	}
 	public String getAllAttendees() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008238, PSTFile.PSETID_Appointment));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008238, PSTSource.PSETID_Appointment));
 	}
 	public String getToAttendees() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x0000823b, PSTFile.PSETID_Appointment));
+		return getStringItem(pstFile.getNameToIdMapItem(0x0000823b, PSTSource.PSETID_Appointment));
 	}
 	public String getCCAttendees() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x0000823c, PSTFile.PSETID_Appointment));
+		return getStringItem(pstFile.getNameToIdMapItem(0x0000823c, PSTSource.PSETID_Appointment));
 	}
 	public int getAppointmentSequence() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008201, PSTFile.PSETID_Appointment));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008201, PSTSource.PSETID_Appointment));
 	}
 	
 	// online meeting properties
 	public boolean isOnlineMeeting() {
-		return (getBooleanItem(pstFile.getNameToIdMapItem(0x00008240, PSTFile.PSETID_Appointment)));
+		return (getBooleanItem(pstFile.getNameToIdMapItem(0x00008240, PSTSource.PSETID_Appointment)));
 	}
 	public int getNetMeetingType() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008241, PSTFile.PSETID_Appointment));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008241, PSTSource.PSETID_Appointment));
 	}
 	public String getNetMeetingServer() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008242, PSTFile.PSETID_Appointment));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008242, PSTSource.PSETID_Appointment));
 	}
 	public String getNetMeetingOrganizerAlias() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008243, PSTFile.PSETID_Appointment));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008243, PSTSource.PSETID_Appointment));
 	}
 	public boolean getNetMeetingAutostart() {
-		return (getIntItem(pstFile.getNameToIdMapItem(0x00008245, PSTFile.PSETID_Appointment)) != 0);
+		return (getIntItem(pstFile.getNameToIdMapItem(0x00008245, PSTSource.PSETID_Appointment)) != 0);
 	}
 	public boolean getConferenceServerAllowExternal() {
-		return (getBooleanItem(pstFile.getNameToIdMapItem(0x00008246, PSTFile.PSETID_Appointment)));
+		return (getBooleanItem(pstFile.getNameToIdMapItem(0x00008246, PSTSource.PSETID_Appointment)));
 	}
 	public String getNetMeetingDocumentPathName() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008247, PSTFile.PSETID_Appointment));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008247, PSTSource.PSETID_Appointment));
 	}
 	public String getNetShowURL() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008248, PSTFile.PSETID_Appointment));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008248, PSTSource.PSETID_Appointment));
 	}
 	public Date getAttendeeCriticalChange() {
-		return getDateItem(pstFile.getNameToIdMapItem(0x00000001, PSTFile.PSETID_Meeting));
+		return getDateItem(pstFile.getNameToIdMapItem(0x00000001, PSTSource.PSETID_Meeting));
 	}
 	public Date getOwnerCriticalChange() {
-		return getDateItem(pstFile.getNameToIdMapItem(0x0000001a, PSTFile.PSETID_Meeting));
+		return getDateItem(pstFile.getNameToIdMapItem(0x0000001a, PSTSource.PSETID_Meeting));
 	}
 	public String getConferenceServerPassword() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008249, PSTFile.PSETID_Appointment));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008249, PSTSource.PSETID_Appointment));
 	}
 	
 	public boolean getAppointmentCounterProposal() {
-		return (getBooleanItem(pstFile.getNameToIdMapItem(0x00008257, PSTFile.PSETID_Appointment)));
+		return (getBooleanItem(pstFile.getNameToIdMapItem(0x00008257, PSTSource.PSETID_Appointment)));
 	}
 
 	public boolean isSilent() {
-		return (getBooleanItem(pstFile.getNameToIdMapItem(0x00000004, PSTFile.PSETID_Meeting)));
+		return (getBooleanItem(pstFile.getNameToIdMapItem(0x00000004, PSTSource.PSETID_Meeting)));
 	}
 
 	public String getRequiredAttendees() {
-		return getStringItem(this.pstFile.getNameToIdMapItem(0x00000006, PSTFile.PSETID_Meeting));
+		return getStringItem(this.pstFile.getNameToIdMapItem(0x00000006, PSTSource.PSETID_Meeting));
 	}
 	
 	public int getLocaleId() {
 		return getIntItem(0x3ff1);
 	}
 
-	public byte[] getGlobalObjectId() {
-		return getBinaryItem(pstFile.getNameToIdMapItem(0x00000003, PSTFile.PSETID_Meeting));
+	/*public byte[] getGlobalObjectId() {
+		return getBinaryItem(pstFile.getNameToIdMapItem(0x00000003, PSTSource.PSETID_Meeting));
+	}*/
+	public PSTGlobalObjectId getGlobalObjectId() {
+	return new PSTGlobalObjectId(getBinaryItem(pstFile.getNameToIdMapItem(0x00000003, PSTSource.PSETID_Meeting)));
+	}
+	
+	public PSTGlobalObjectId getCleanGlobalObjectId() {
+		return new PSTGlobalObjectId(getBinaryItem(pstFile.getNameToIdMapItem(0x00000023, PSTSource.PSETID_Meeting)));
 	}
 }
diff --git a/com/pff/PSTAttachment.java b/com/pff/objects/PSTAttachment.java
similarity index 92%
rename from com/pff/PSTAttachment.java
rename to com/pff/objects/PSTAttachment.java
index 2b2f1db..3e03643 100644
--- a/com/pff/PSTAttachment.java
+++ b/com/pff/objects/PSTAttachment.java
@@ -31,11 +31,19 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.objects;
 
 import java.io.*;
 import java.util.*;
 
+import com.pff.PSTUtils;
+import com.pff.exceptions.PSTException;
+import com.pff.parsing.PSTDescriptorItem;
+import com.pff.parsing.PSTNodeInputStream;
+import com.pff.parsing.tables.PSTTableBC;
+import com.pff.parsing.tables.PSTTableBCItem;
+import com.pff.source.PSTSource;
+
 
 /**
  * Class containing attachment information
@@ -43,7 +51,7 @@
  */
 public class PSTAttachment extends PSTObject {
 	
-	PSTAttachment(PSTFile theFile, PSTTableBC table, HashMap localDescriptorItems) {
+	PSTAttachment(PSTSource theFile, PSTTableBC table, HashMap localDescriptorItems) {
 		super(theFile, null, table, localDescriptorItems);
 	}
 	
@@ -74,11 +82,11 @@ public PSTMessage getEmbeddedPSTMessage()
 					throw new PSTException("External reference in getEmbeddedPSTMessage()!\n");
 				}
 			} else if ( item.entryValueType == 0x000D ) {
-				int descriptorItem = (int)PSTObject.convertLittleEndianBytesToLong(item.data, 0, 4);
+				int descriptorItem = (int)PSTUtils.convertLittleEndianBytesToLong(item.data, 0, 4);
 				//PSTObject.printHexFormatted(item.data, true);
 				PSTDescriptorItem descriptorItemNested = this.localDescriptorItems.get(descriptorItem);
 				in = new PSTNodeInputStream(this.pstFile, descriptorItemNested);
-				this.localDescriptorItems.putAll(pstFile.getPSTDescriptorItems(descriptorItemNested.subNodeOffsetIndexIdentifier));
+				this.localDescriptorItems.putAll(pstFile.getPSTDescriptorItems(descriptorItemNested.getSubNodeOffsetIndexIdentifier()));
 				/*
 				if ( descriptorItemNested != null ) {
 					try {
diff --git a/com/pff/PSTContact.java b/com/pff/objects/PSTContact.java
similarity index 96%
rename from com/pff/PSTContact.java
rename to com/pff/objects/PSTContact.java
index 7a1bd10..a3170cd 100644
--- a/com/pff/PSTContact.java
+++ b/com/pff/objects/PSTContact.java
@@ -31,12 +31,18 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.objects;
 
 import java.io.IOException;
 import java.util.Date;
 import java.util.HashMap;
 
+import com.pff.exceptions.PSTException;
+import com.pff.parsing.DescriptorIndexNode;
+import com.pff.parsing.PSTDescriptorItem;
+import com.pff.parsing.tables.PSTTableBC;
+import com.pff.source.PSTSource;
+
 
 /**
  * Class for Contacts
@@ -50,7 +56,7 @@ public class PSTContact extends PSTMessage {
 	 * @throws PSTException
 	 * @throws IOException
 	 */
-	public PSTContact(PSTFile theFile, DescriptorIndexNode descriptorIndexNode)
+	public PSTContact(PSTSource theFile, DescriptorIndexNode descriptorIndexNode)
 		throws PSTException, IOException {
 		super(theFile, descriptorIndexNode);
 	}
@@ -61,7 +67,7 @@ public PSTContact(PSTFile theFile, DescriptorIndexNode descriptorIndexNode)
 	 * @param table
 	 * @param localDescriptorItems
 	 */
-	public PSTContact(PSTFile theFile, DescriptorIndexNode folderIndexNode, PSTTableBC table, HashMap localDescriptorItems) {
+	public PSTContact(PSTSource theFile, DescriptorIndexNode folderIndexNode, PSTTableBC table, HashMap localDescriptorItems) {
 		super(theFile, folderIndexNode, table, localDescriptorItems);
 	}
 	
@@ -416,7 +422,7 @@ public String getNote() {
 	 }
 	 
 	 String getNamedStringItem(int key) {
-		 int id = pstFile.getNameToIdMapItem(key, PSTFile.PSETID_Address);
+		 int id = pstFile.getNameToIdMapItem(key, PSTSource.PSETID_Address);
 		 if ( id != -1 ) {
 			 return getStringItem(id);
 		 }
@@ -548,7 +554,7 @@ public String getOtherAddress() {
 	  * Selected Mailing Address 
 	  */
 	 public int getPostalAddressId() {
-	 	return getIntItem(pstFile.getNameToIdMapItem(0x00008022, PSTFile.PSETID_Address));
+	 	return getIntItem(pstFile.getNameToIdMapItem(0x00008022, PSTSource.PSETID_Address));
 	 }
 
 	 /**
diff --git a/com/pff/PSTFolder.java b/com/pff/objects/PSTFolder.java
similarity index 92%
rename from com/pff/PSTFolder.java
rename to com/pff/objects/PSTFolder.java
index d3fc0a4..68d00ff 100644
--- a/com/pff/PSTFolder.java
+++ b/com/pff/objects/PSTFolder.java
@@ -31,9 +31,24 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
-import java.io.*;
-import java.util.*;
+package com.pff.objects;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+
+import com.pff.exceptions.PSTException;
+import com.pff.parsing.DescriptorIndexNode;
+import com.pff.parsing.PSTDescriptorItem;
+import com.pff.parsing.PSTNodeInputStream;
+import com.pff.parsing.tables.PSTTable7C;
+import com.pff.parsing.tables.PSTTable7CItem;
+import com.pff.parsing.tables.PSTTableBC;
+import com.pff.source.PSTSource;
 
 
 /**
@@ -51,7 +66,7 @@ public class PSTFolder extends PSTObject {
 	 * @throws PSTException
 	 * @throws IOException
 	 */
-	PSTFolder(PSTFile theFile, DescriptorIndexNode descriptorIndexNode)
+	public PSTFolder(PSTSource theFile, DescriptorIndexNode descriptorIndexNode)
 		throws PSTException, IOException
 	{
 		super(theFile, descriptorIndexNode);
@@ -64,7 +79,7 @@ public class PSTFolder extends PSTObject {
 	 * @param folderIndexNode
 	 * @param table
 	 */
-	PSTFolder(PSTFile theFile, DescriptorIndexNode folderIndexNode, PSTTableBC table, HashMap localDescriptorItems) {
+	PSTFolder(PSTSource theFile, DescriptorIndexNode folderIndexNode, PSTTableBC table, HashMap localDescriptorItems) {
 		super(theFile, folderIndexNode, table, localDescriptorItems);
 	}
 	
@@ -75,11 +90,11 @@ public class PSTFolder extends PSTObject {
 	 * @throws PSTException
 	 * @throws IOException
 	 */
-	public Vector getSubFolders()
+	public ArrayList getSubFolders()
 		throws PSTException, IOException
 	{
 		initSubfoldersTable();
-		Vector output = new Vector();
+		ArrayList output = new ArrayList();
 		if (this.hasSubfolders()) {
 			try {
 				List> itemMapSet = subfoldersTable.getItems();
@@ -126,7 +141,7 @@ private void initSubfoldersTable()
 	 * internal vars for the tracking of things..
 	 */
 	private int currentEmailIndex = 0;
-	private LinkedHashSet otherItems = null;
+	//private LinkedHashSet otherItems = null;
 
 	private PSTTable7C emailsTable = null;
 	private LinkedList fallbackEmailsTable = null;
@@ -198,12 +213,12 @@ private void initEmailsTable()
 	 * @throws PSTException
 	 * @throws IOException
 	 */
-	public Vector getChildren(int numberToReturn)
+	public ArrayList getChildren(int numberToReturn)
 		throws PSTException, IOException
 	{
 		initEmailsTable();
 
-		Vector output = new Vector();
+		ArrayList output = new ArrayList();
 		if (emailsTable != null) {
 			List> rows = this.emailsTable.getItems(currentEmailIndex, numberToReturn);
 
diff --git a/com/pff/PSTMessage.java b/com/pff/objects/PSTMessage.java
similarity index 91%
rename from com/pff/PSTMessage.java
rename to com/pff/objects/PSTMessage.java
index 2644276..4cf58e9 100644
--- a/com/pff/PSTMessage.java
+++ b/com/pff/objects/PSTMessage.java
@@ -31,12 +31,25 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.objects;
 
 import java.io.IOException;
 import java.util.Date;
 import java.util.HashMap;
 
+import com.pff.PSTUtils;
+import com.pff.exceptions.PSTException;
+import com.pff.objects.sub.PSTConversationIndex;
+import com.pff.objects.sub.PSTRecipient;
+import com.pff.parsing.DescriptorIndexNode;
+import com.pff.parsing.PSTDescriptorItem;
+import com.pff.parsing.PSTNodeInputStream;
+import com.pff.parsing.tables.PSTTable7C;
+import com.pff.parsing.tables.PSTTable7CItem;
+import com.pff.parsing.tables.PSTTableBC;
+import com.pff.parsing.tables.PSTTableBCItem;
+import com.pff.source.PSTSource;
+
 /**
  * PST Message contains functions that are common across most MAPI objects.
  * Note that many of these functions may not be applicable for the item in question,
@@ -51,13 +64,13 @@ public class PSTMessage extends PSTObject {
 	public static final int IMPORTANCE_NORMAL = 1;
 	public static final int IMPORTANCE_HIGH = 2;
 
-	PSTMessage(PSTFile theFile, DescriptorIndexNode descriptorIndexNode)
+	PSTMessage(PSTSource theFile, DescriptorIndexNode descriptorIndexNode)
 			throws PSTException, IOException
 	{
 		super(theFile, descriptorIndexNode);
 	}
 
-	PSTMessage(PSTFile theFile, DescriptorIndexNode folderIndexNode, PSTTableBC table, HashMap localDescriptorItems)
+	PSTMessage(PSTSource theFile, DescriptorIndexNode folderIndexNode, PSTTableBC table, HashMap localDescriptorItems)
 	{
 		super(theFile, folderIndexNode, table, localDescriptorItems);
 	}
@@ -71,12 +84,12 @@ public String getRTFBody()
 			// is it a reference?
 			PSTTableBCItem item = this.items.get(0x1009);
 			if (item.data.length > 0) {
-				return (LZFu.decode(item.data));
+				return (PSTUtils.decodeLZFU(item.data));
 			}
 			int ref = item.entryValueReference;
 			PSTDescriptorItem descItem = this.localDescriptorItems.get(ref);
 			if ( descItem != null ) {
-				return LZFu.decode(descItem.getData());
+				return PSTUtils.decodeLZFU(descItem.getData());
 			}
 		}
 		
@@ -317,8 +330,8 @@ public boolean getMessageCcMe () {
 	/**
 	 * Message addressed to me ASCII or Unicode string
 	 */
-	public String getMessageRecipMe () {
-		return this.getStringItem(0x0059);
+	public boolean getMessageRecipMe () {
+		return this.getIntItem(0x0059) != 0;
 	}
 	/**
 	 * Response requested Boolean
@@ -585,6 +598,10 @@ public Date getMessageDeliveryTime() {
 //		return (this.getIntItem(0x0e17) & 0x2000) != 0;
 //	}
 	
+	public int getNativeBodyType() {
+		return this.getIntItem(0x1016);
+	}
+	
 	/**
 	 * Message content properties
 	 */
@@ -599,7 +616,7 @@ public String getBody() {
 			cpItem = this.items.get(0x3FDE); // PidTagInternetCodepage
 		}
 		if (cpItem != null) {
-			cp = PSTFile.getInternetCodePageCharset(cpItem.entryValueReference);
+			cp = PSTSource.getInternetCodePageCharset(cpItem.entryValueReference);
 		}
 		return this.getStringItem(0x1000, 0, cp);
 	}
@@ -649,7 +666,7 @@ public String getBodyHTML() {
 			cpItem = this.items.get(0x3FFD); // PidTagMessageCodepage
 		}
 		if (cpItem != null) {
-			cp = PSTFile.getInternetCodePageCharset(cpItem.entryValueReference);
+			cp = PSTSource.getInternetCodePageCharset(cpItem.entryValueReference);
 		}
 		return this.getStringItem(0x1013, 0, cp);
 	}
@@ -759,8 +776,8 @@ private void processRecipients()
 			{
 				PSTDescriptorItem item = this.localDescriptorItems.get(recipientTableKey);
 				HashMap descriptorItems = null;
-				if (item.subNodeOffsetIndexIdentifier > 0) {
-					descriptorItems =pstFile.getPSTDescriptorItems(item.subNodeOffsetIndexIdentifier);
+				if (item.getSubNodeOffsetIndexIdentifier() > 0) {
+					descriptorItems =pstFile.getPSTDescriptorItems(item.getSubNodeOffsetIndexIdentifier());
 				}
 				recipientTable = new PSTTable7C(new PSTNodeInputStream(pstFile, item), descriptorItems);
 			}
@@ -811,8 +828,8 @@ private void processAttachments()
 		{
 			PSTDescriptorItem item = this.localDescriptorItems.get(attachmentTableKey);
 			HashMap descriptorItems = null;
-			if (item.subNodeOffsetIndexIdentifier > 0) {
-				descriptorItems =pstFile.getPSTDescriptorItems(item.subNodeOffsetIndexIdentifier);
+			if (item.getSubNodeOffsetIndexIdentifier() > 0) {
+				descriptorItems =pstFile.getPSTDescriptorItems(item.getSubNodeOffsetIndexIdentifier());
 			}
 			attachmentTable = new PSTTable7C(new PSTNodeInputStream(pstFile, item), descriptorItems);
 		}
@@ -822,13 +839,13 @@ private void processAttachments()
 	 * Start date Filetime
 	 */
 	public Date getTaskStartDate() {
-		return getDateItem(pstFile.getNameToIdMapItem(0x00008104, PSTFile.PSETID_Task));
+		return getDateItem(pstFile.getNameToIdMapItem(0x00008104, PSTSource.PSETID_Task));
 	}
 	/**
 	 * Due date Filetime
 	 */
 	public Date getTaskDueDate() {
-		return getDateItem(pstFile.getNameToIdMapItem(0x00008105, PSTFile.PSETID_Task));
+		return getDateItem(pstFile.getNameToIdMapItem(0x00008105, PSTSource.PSETID_Task));
 	}
 	
 	/**
@@ -836,11 +853,11 @@ public Date getTaskDueDate() {
 	 * @return
 	 */
 	public boolean getReminderSet() {
-		return getBooleanItem(pstFile.getNameToIdMapItem(0x00008503, PSTFile.PSETID_Common));
+		return getBooleanItem(pstFile.getNameToIdMapItem(0x00008503, PSTSource.PSETID_Common));
 	}
 	
 	public int getReminderDelta() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008501, PSTFile.PSETID_Common));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008501, PSTSource.PSETID_Common));
 	}
 	
 	/**
@@ -871,7 +888,7 @@ public String[] getColorCategories()
 					categories = new String[categoryCount];
 					int[] offsets = new int[categoryCount];
 					for (int x = 0; x < categoryCount; x++) {
-						offsets[x] = (int)PSTObject.convertBigEndianBytesToLong(item.data, (x*4)+1, (x+1)*4+1);
+						offsets[x] = (int)PSTUtils.convertBigEndianBytesToLong(item.data, (x*4)+1, (x+1)*4+1);
 					}
 					for (int x = 0; x < offsets.length -1; x++) {
 						int start = offsets[x];
@@ -958,8 +975,8 @@ public PSTAttachment getAttachment(int attachmentNumber)
 			// note that all the information that was in the c7 table is repeated in the eb table in attachment data.
 			// so no need to pass it...
 			HashMap attachmentDescriptorItems = new HashMap();
-			if (descriptorItem.subNodeOffsetIndexIdentifier > 0) {
-				attachmentDescriptorItems = pstFile.getPSTDescriptorItems(descriptorItem.subNodeOffsetIndexIdentifier);
+			if (descriptorItem.getSubNodeOffsetIndexIdentifier() > 0) {
+				attachmentDescriptorItems = pstFile.getPSTDescriptorItems(descriptorItem.getSubNodeOffsetIndexIdentifier());
 			}
 			return new PSTAttachment(this.pstFile, attachmentDetailsTable, attachmentDescriptorItems);
 		}
@@ -1014,4 +1031,16 @@ public String toString() {
 			this.localDescriptorItems;
 	}
 	
+	
+	public byte[] getConversationId() {
+		return getBinaryItem(0x3013);
+	}
+	
+	public PSTConversationIndex getConversationIndex() {
+		return new PSTConversationIndex(getBinaryItem(0x0071));
+	}
+	
+	public boolean isConversationIndexTracking() {
+		return getBooleanItem(0x3016, false);
+	}
 }
diff --git a/com/pff/PSTMessageStore.java b/com/pff/objects/PSTMessageStore.java
similarity index 79%
rename from com/pff/PSTMessageStore.java
rename to com/pff/objects/PSTMessageStore.java
index 553593f..b231aab 100644
--- a/com/pff/PSTMessageStore.java
+++ b/com/pff/objects/PSTMessageStore.java
@@ -31,11 +31,17 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.objects;
 
 import java.io.*;
 import java.util.*;
 
+import com.pff.PSTUtils;
+import com.pff.exceptions.PSTException;
+import com.pff.parsing.DescriptorIndexNode;
+import com.pff.parsing.tables.PSTTableBCItem;
+import com.pff.source.PSTSource;
+
 
 /**
  * Object that represents the message store.
@@ -44,7 +50,7 @@
  */
 public class PSTMessageStore extends PSTObject {
 	
-	PSTMessageStore(PSTFile theFile, DescriptorIndexNode descriptorIndexNode)
+	public PSTMessageStore(PSTSource theFile, DescriptorIndexNode descriptorIndexNode)
 		throws PSTException, IOException
 	{
 		super(theFile, descriptorIndexNode);
@@ -60,10 +66,10 @@ public UUID getTagRecordKeyAsUUID() {
 			PSTTableBCItem item = this.items.get(guidEntryType);
 			int offset = 0;
 			byte[] bytes = item.data;
-			long mostSigBits = (PSTObject.convertLittleEndianBytesToLong(bytes, offset, offset+4) << 32) |
-								(PSTObject.convertLittleEndianBytesToLong(bytes, offset+4, offset+6) << 16) |
-								PSTObject.convertLittleEndianBytesToLong(bytes, offset+6, offset+8);
-			long leastSigBits = PSTObject.convertBigEndianBytesToLong(bytes, offset+8, offset+16);
+			long mostSigBits = (PSTUtils.convertLittleEndianBytesToLong(bytes, offset, offset+4) << 32) |
+								(PSTUtils.convertLittleEndianBytesToLong(bytes, offset+4, offset+6) << 16) |
+								PSTUtils.convertLittleEndianBytesToLong(bytes, offset+6, offset+8);
+			long leastSigBits = PSTUtils.convertBigEndianBytesToLong(bytes, offset+8, offset+16);
 			return new UUID(mostSigBits, leastSigBits);
 		}
 		return null;
diff --git a/com/pff/PSTObject.java b/com/pff/objects/PSTObject.java
similarity index 60%
rename from com/pff/PSTObject.java
rename to com/pff/objects/PSTObject.java
index c89aa48..aab6c34 100644
--- a/com/pff/PSTObject.java
+++ b/com/pff/objects/PSTObject.java
@@ -31,11 +31,21 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.objects;
 
 import java.io.IOException;
 import java.util.*;
 
+import com.pff.PSTUtils;
+import com.pff.exceptions.PSTException;
+import com.pff.objects.sub.PSTTimeZone;
+import com.pff.parsing.DescriptorIndexNode;
+import com.pff.parsing.PSTDescriptorItem;
+import com.pff.parsing.PSTNodeInputStream;
+import com.pff.parsing.tables.PSTTableBC;
+import com.pff.parsing.tables.PSTTableBCItem;
+import com.pff.source.PSTSource;
+
 /**
  * PST Object is the root class of all PST Items.
  * It also provides a number of static utility functions.  The most important of which is the
@@ -71,7 +81,7 @@ public String getItemsString() {
 		return items.toString();
 	}
 	
-	protected PSTFile pstFile;
+	protected PSTSource pstFile;
 	protected byte[] data;
 	protected DescriptorIndexNode descriptorIndexNode;
 	protected HashMap items;
@@ -79,7 +89,7 @@ public String getItemsString() {
 	
 	protected LinkedHashMap> children;
 	
-	protected PSTObject(PSTFile theFile, DescriptorIndexNode descriptorIndexNode)
+	protected PSTObject(PSTSource theFile, DescriptorIndexNode descriptorIndexNode)
 		throws PSTException, IOException
 	{
 		this.pstFile = theFile;
@@ -104,7 +114,7 @@ protected PSTObject(PSTFile theFile, DescriptorIndexNode descriptorIndexNode)
 	 * @param folderIndexNode
 	 * @param table
 	 */
-	protected PSTObject(PSTFile theFile, DescriptorIndexNode folderIndexNode, PSTTableBC table, HashMap localDescriptorItems) {
+	protected PSTObject(PSTSource theFile, DescriptorIndexNode folderIndexNode, PSTTableBC table, HashMap localDescriptorItems) {
 		this.pstFile = theFile;
 		this.descriptorIndexNode = folderIndexNode;
 		this.items = table.getItems();
@@ -129,7 +139,11 @@ public DescriptorIndexNode getDescriptorNode() {
 	 * @return item's descriptor node identifier
 	 */
 	public long getDescriptorNodeId() {
-		return this.descriptorIndexNode.descriptorIdentifier;
+		//return this.descriptorIndexNode.descriptorIdentifier;
+		if (this.descriptorIndexNode != null) { // Prevent null pointer exceptions for embedded messages
+			return this.descriptorIndexNode.descriptorIdentifier;
+		}
+		return 0;
 	}
 
 	public int getNodeType() {
@@ -168,7 +182,7 @@ protected double getDoubleItem(int identifier) {
 	protected double getDoubleItem(int identifier, double defaultValue) {
 		if (this.items.containsKey(identifier)) {
 			PSTTableBCItem item = this.items.get(identifier);
-			long longVersion = PSTObject.convertLittleEndianBytesToLong(item.data);
+			long longVersion = PSTUtils.convertLittleEndianBytesToLong(item.data);
 			return Double.longBitsToDouble(longVersion);
 		}
 		return defaultValue;
@@ -187,7 +201,7 @@ protected long getLongItem(int identifier, long defaultValue) {
 			} else if ( item.entryValueType == 0x0014 ){
 				// we are a long
 				if ( item.data != null && item.data.length == 8 ) {
-					return PSTObject.convertLittleEndianBytesToLong(item.data, 0, 8);
+					return PSTUtils.convertLittleEndianBytesToLong(item.data, 0, 8);
 				} else {
 					System.err.printf("Invalid data length for long id 0x%04X\n", identifier);
 					// Return the default value for now...
@@ -237,7 +251,7 @@ protected String getStringItem(int identifier, int stringType, String codepage)
 				} catch (Exception e) {
 					System.err.printf("Exception %s decoding string %s: %s\n",
 							e.toString(),
-							PSTFile.getPropertyDescription(identifier, stringType), data != null ? data.toString() : "null");
+							PSTSource.getPropertyDescription(identifier, stringType), data != null ? data.toString() : "null");
 					return "";
 				}
 				//System.out.printf("PSTObject.getStringItem - item isn't a string: 0x%08X\n", identifier);
@@ -292,7 +306,7 @@ private String getStringCodepage() {
 			cpItem = this.items.get(0x3FDE); // PidTagInternetCodepage
 		}
 		if (cpItem != null) {
-			return PSTFile.getInternetCodePageCharset(cpItem.entryValueReference);
+			return PSTSource.getInternetCodePageCharset(cpItem.entryValueReference);
 		}
 		return null;
 	}
@@ -303,10 +317,10 @@ public Date getDateItem(int identifier) {
 			if (item.data.length == 0 ) {
 				return new Date(0);
 			}
-			int high = (int)PSTObject.convertLittleEndianBytesToLong(item.data, 4, 8);
-			int low = (int)PSTObject.convertLittleEndianBytesToLong(item.data, 0, 4);
+			int high = (int)PSTUtils.convertLittleEndianBytesToLong(item.data, 4, 8);
+			int low = (int)PSTUtils.convertLittleEndianBytesToLong(item.data, 0, 4);
 			 
-			return PSTObject.filetimeToDate(high, low);
+			return PSTUtils.filetimeToDate(high, low);
 		}
 		return null;
 	}
@@ -398,209 +412,7 @@ public Date getLastModificationTime() {
 	}
 
 	
-	/**
-	 * Static stuff below
-	 * ------------------
-	 */
-
-	// substitution table for the compressible encryption type.
-	static int[] compEnc = {
-	    0x47, 0xf1, 0xb4, 0xe6, 0x0b, 0x6a, 0x72, 0x48, 0x85, 0x4e, 0x9e, 0xeb, 0xe2, 0xf8, 0x94, 0x53,
-	    0xe0, 0xbb, 0xa0, 0x02, 0xe8, 0x5a, 0x09, 0xab, 0xdb, 0xe3, 0xba, 0xc6, 0x7c, 0xc3, 0x10, 0xdd,
-	    0x39, 0x05, 0x96, 0x30, 0xf5, 0x37, 0x60, 0x82, 0x8c, 0xc9, 0x13, 0x4a, 0x6b, 0x1d, 0xf3, 0xfb,
-	    0x8f, 0x26, 0x97, 0xca, 0x91, 0x17, 0x01, 0xc4, 0x32, 0x2d, 0x6e, 0x31, 0x95, 0xff, 0xd9, 0x23,
-	    0xd1, 0x00, 0x5e, 0x79, 0xdc, 0x44, 0x3b, 0x1a, 0x28, 0xc5, 0x61, 0x57, 0x20, 0x90, 0x3d, 0x83,
-	    0xb9, 0x43, 0xbe, 0x67, 0xd2, 0x46, 0x42, 0x76, 0xc0, 0x6d, 0x5b, 0x7e, 0xb2, 0x0f, 0x16, 0x29,
-	    0x3c, 0xa9, 0x03, 0x54, 0x0d, 0xda, 0x5d, 0xdf, 0xf6, 0xb7, 0xc7, 0x62, 0xcd, 0x8d, 0x06, 0xd3,
-	    0x69, 0x5c, 0x86, 0xd6, 0x14, 0xf7, 0xa5, 0x66, 0x75, 0xac, 0xb1, 0xe9, 0x45, 0x21, 0x70, 0x0c,
-	    0x87, 0x9f, 0x74, 0xa4, 0x22, 0x4c, 0x6f, 0xbf, 0x1f, 0x56, 0xaa, 0x2e, 0xb3, 0x78, 0x33, 0x50,
-	    0xb0, 0xa3, 0x92, 0xbc, 0xcf, 0x19, 0x1c, 0xa7, 0x63, 0xcb, 0x1e, 0x4d, 0x3e, 0x4b, 0x1b, 0x9b,
-	    0x4f, 0xe7, 0xf0, 0xee, 0xad, 0x3a, 0xb5, 0x59, 0x04, 0xea, 0x40, 0x55, 0x25, 0x51, 0xe5, 0x7a,
-	    0x89, 0x38, 0x68, 0x52, 0x7b, 0xfc, 0x27, 0xae, 0xd7, 0xbd, 0xfa, 0x07, 0xf4, 0xcc, 0x8e, 0x5f,
-	    0xef, 0x35, 0x9c, 0x84, 0x2b, 0x15, 0xd5, 0x77, 0x34, 0x49, 0xb6, 0x12, 0x0a, 0x7f, 0x71, 0x88,
-	    0xfd, 0x9d, 0x18, 0x41, 0x7d, 0x93, 0xd8, 0x58, 0x2c, 0xce, 0xfe, 0x24, 0xaf, 0xde, 0xb8, 0x36,
-	    0xc8, 0xa1, 0x80, 0xa6, 0x99, 0x98, 0xa8, 0x2f, 0x0e, 0x81, 0x65, 0x73, 0xe4, 0xc2, 0xa2, 0x8a,
-	    0xd4, 0xe1, 0x11, 0xd0, 0x08, 0x8b, 0x2a, 0xf2, 0xed, 0x9a, 0x64, 0x3f, 0xc1, 0x6c, 0xf9, 0xec
-	};
-	
-	/**
-	 * Output a dump of data in hex format in the order it was read in
-	 * @param data
-	 * @param pretty
-	 */
-	public static void printHexFormatted(byte[] data, boolean pretty) {
-		printHexFormatted(data,pretty, new int[0]);
-	}
-	protected static void printHexFormatted(byte[] data, boolean pretty, int[] indexes) {
-		// groups of two
-		if (pretty) { System.out.println("---"); }
-		long tmpLongValue;
-		String line = "";
-		int nextIndex = 0;
-		int indexIndex = 0;
-		if (indexes.length > 0) {
-			nextIndex = indexes[0];
-			indexIndex++;
-		}
-		for (int x = 0; x < data.length; x++) {
-			tmpLongValue = (long)data[x] & 0xff;
-			
-			if (indexes.length > 0 &&
-				x == nextIndex &&
-				nextIndex < data.length)
-			{
-				System.out.print("+");
-				line += "+";
-				while (indexIndex < indexes.length-1 && indexes[indexIndex] <= nextIndex) 
-				{
-					indexIndex++;
-				}
-				nextIndex = indexes[indexIndex];
-				//indexIndex++;
-			}
-			
-			if (Character.isLetterOrDigit((char)tmpLongValue)) {
-				line += (char)tmpLongValue;
-			}
-			else
-			{
-				line += ".";
-			}
-			
-			if (Long.toHexString(tmpLongValue).length() < 2) {
-				System.out.print("0");
-			}
-			System.out.print(Long.toHexString(tmpLongValue));
-			if (x % 2 == 1 && pretty) {
-				System.out.print(" ");
-			}
-			if (x % 16 == 15 && pretty) {
-				System.out.print(" "+line);
-				System.out.println("");
-				line = "";
-			}
-		}
-		if (pretty) { System.out.println(" "+line); System.out.println("---"); System.out.println(data.length); } else {  }
-	}
-	
-
-	
-	/**
-	 * decode a lump of data that has been encrypted with the compressible encryption
-	 * @param data
-	 * @return decoded data
-	 */
-	protected static byte[] decode(byte[] data) {
-		int temp;
-		for (int x = 0; x < data.length; x++) {
-			temp = data[x] & 0xff;
-			data[x] = (byte)compEnc[temp];
-		}
-		
-		return data;
-	}
-	
-
-	protected static byte[] encode(byte[] data) {
-		// create the encoding array...
-		int[] enc = new int[compEnc.length];
-		for (int x = 0; x < enc.length; x++) {
-			enc[compEnc[x]] = x;
-		}
-		
-		// now it's just the same as decode...
-		int temp;
-		for (int x = 0; x < data.length; x++) {
-			temp = data[x] & 0xff;
-			data[x] = (byte)enc[temp];
-		}
-		
-		return data;
-	}
-
 	
-	/**
-	 * Utility function for converting little endian bytes into a usable java long
-	 * @param data
-	 * @return long version of the data
-	 */
-	public static long convertLittleEndianBytesToLong(byte[] data) {
-		return convertLittleEndianBytesToLong(data, 0, data.length);
-	}
-	/**
-	 * Utility function for converting little endian bytes into a usable java long
-	 * @param data
-	 * @param start
-	 * @param end
-	 * @return long version of the data
-	 */
-	public static long convertLittleEndianBytesToLong(byte[] data, int start, int end) {
-		
-		long offset = data[end-1] & 0xff;
-		long tmpLongValue;
-		for (int x = end-2; x >= start; x--) {
-			offset = offset << 8;
-			tmpLongValue = (long)data[x] & 0xff;
-			offset |= tmpLongValue;
-		}
-		
-		return offset;
-	}
-	
-	/**
-	 * Utility function for converting big endian bytes into a usable java long
-	 * @param data
-	 * @param start
-	 * @param end
-	 * @return long version of the data
-	 */
-	public static long convertBigEndianBytesToLong(byte[] data, int start, int end) {
-		
-		long offset = 0;
-		for ( int x = start; x < end; ++x ) {
-			offset = offset << 8;
-			offset |= ((long)data[x] & 0xFFL);
-		}
-		
-		return offset;
-	}
-/*
-	protected static boolean isPSTArray(byte[] data) {
-		return (data[0] == 1 && data[1] == 1);
-	}
-/**/	
-/*	
-	protected static int[] getBlockOffsets(RandomAccessFile in, byte[] data)
-		throws IOException, PSTException
-	{
-		// is the data an array?
-		if (!(data[0] == 1 && data[1] == 1))
-		{
-			throw new PSTException("Unable to process array, does not appear to be one!");
-		}
-
-		// we are an array!
-		// get the array items and merge them together
-		int numberOfEntries = (int)PSTObject.convertLittleEndianBytesToLong(data, 2, 4);
-		int[] output = new int[numberOfEntries];
-		int tableOffset = 8;
-		int blockOffset = 0;
-		for (int y = 0; y < numberOfEntries; y++) {
-			// get the offset identifier
-			long tableOffsetIdentifierIndex = PSTObject.convertLittleEndianBytesToLong(data, tableOffset, tableOffset+8);
-			// clear the last bit of the identifier.  Why so hard?
-			tableOffsetIdentifierIndex = (tableOffsetIdentifierIndex & 0xfffffffe);
-			OffsetIndexItem tableOffsetIdentifier = PSTObject.getOffsetIndexNode(in, tableOffsetIdentifierIndex);
-			blockOffset += tableOffsetIdentifier.size;
-			output[y] = blockOffset;
-			tableOffset += 8;
-		}
-
-		// replace the item data with the stuff from the array...
-		return output;
-	}
-/**/
-
 	/**
 	 * Detect and load a PST Object from a file with the specified descriptor index
 	 * @param theFile
@@ -609,10 +421,10 @@ protected static int[] getBlockOffsets(RandomAccessFile in, byte[] data)
 	 * @throws IOException
 	 * @throws PSTException
 	 */
-	public static PSTObject detectAndLoadPSTObject(PSTFile theFile, long descriptorIndex)
+	public static PSTObject detectAndLoadPSTObject(PSTSource theFile, long descriptorIndex)
 		throws IOException, PSTException
 	{
-		return PSTObject.detectAndLoadPSTObject(theFile, theFile.getDescriptorIndexNode(descriptorIndex));
+		return detectAndLoadPSTObject(theFile, theFile.getDescriptorIndexNode(descriptorIndex));
 	}
 
 	/**
@@ -623,7 +435,7 @@ public static PSTObject detectAndLoadPSTObject(PSTFile theFile, long descriptorI
 	 * @throws IOException
 	 * @throws PSTException
 	 */
-	static PSTObject detectAndLoadPSTObject(PSTFile theFile, DescriptorIndexNode folderIndexNode)
+	public static PSTObject detectAndLoadPSTObject(PSTSource theFile, DescriptorIndexNode folderIndexNode)
 		throws IOException, PSTException
 	{
 		int nidType = (folderIndexNode.descriptorIdentifier & 0x1F);
@@ -639,7 +451,7 @@ static PSTObject detectAndLoadPSTObject(PSTFile theFile, DescriptorIndexNode fol
 			if ( nidType == 0x02 || nidType == 0x03 ) {
 				return new PSTFolder(theFile, folderIndexNode, table, localDescriptorItems);
 			} else  {
-				return PSTObject.createAppropriatePSTMessageObject(theFile, folderIndexNode, table, localDescriptorItems);
+				return createAppropriatePSTMessageObject(theFile, folderIndexNode, table, localDescriptorItems);
 			}
 		}
 		else
@@ -647,8 +459,10 @@ static PSTObject detectAndLoadPSTObject(PSTFile theFile, DescriptorIndexNode fol
 			throw new PSTException("Unknown child type with offset id: "+folderIndexNode.localDescriptorsOffsetIndexIdentifier);
 		}
 	}
-
-	static PSTMessage createAppropriatePSTMessageObject(PSTFile theFile, DescriptorIndexNode folderIndexNode, PSTTableBC table, HashMap localDescriptorItems)
+	
+	
+	
+	static PSTMessage createAppropriatePSTMessageObject(PSTSource theFile, DescriptorIndexNode folderIndexNode, PSTTableBC table, HashMap localDescriptorItems)
 	{
 
 		PSTTableBCItem item = table.getItems().get(0x001a);
@@ -679,7 +493,8 @@ static PSTMessage createAppropriatePSTMessageObject(PSTFile theFile, DescriptorI
 		return new PSTMessage(theFile, folderIndexNode, table, localDescriptorItems);
 	}
 
-	static String guessPSTObjectType(PSTFile theFile, DescriptorIndexNode folderIndexNode)
+	
+	public static String guessPSTObjectType(PSTSource theFile, DescriptorIndexNode folderIndexNode)
 		throws IOException, PSTException
 	{
 
@@ -734,79 +549,5 @@ else if (key.intValue() >= 0x3c00 &&
 		}
 		return "Unknown";
 	}
-	
-	/**
-	 * the code below was taken from a random apache project
-	 * http://www.koders.com/java/fidA9D4930E7443F69F32571905DD4CA01E4D46908C.aspx
-	 * my bit-shifting isn't that 1337
-	 */
-	
-	/**
-     * The difference between the Windows epoch (1601-01-01
-     * 00:00:00) and the Unix epoch (1970-01-01 00:00:00) in
-     * milliseconds: 11644473600000L. (Use your favorite spreadsheet
-     * program to verify the correctness of this value. By the way,
-     * did you notice that you can tell from the epochs which
-     * operating system is the modern one? :-))
-     */
-    private static final long EPOCH_DIFF = 11644473600000L;
-	
-	/**
-     * Converts a Windows FILETIME into a {@link Date}. The Windows
-     * FILETIME structure holds a date and time associated with a
-     * file. The structure identifies a 64-bit integer specifying the
-     * number of 100-nanosecond intervals which have passed since
-     * January 1, 1601. This 64-bit value is split into the two double
-     * words stored in the structure.
-     *
-     * @param high The higher double word of the FILETIME structure.
-     * @param low The lower double word of the FILETIME structure.
-     * @return The Windows FILETIME as a {@link Date}.
-     */
-    protected static Date filetimeToDate(final int high, final int low)
-    {
-        final long filetime = ((long) high) << 32 | (low & 0xffffffffL);
-		//System.out.printf("0x%X\n", filetime);
-        final long ms_since_16010101 = filetime / (1000 * 10);
-        final long ms_since_19700101 = ms_since_16010101 - EPOCH_DIFF;
-        return new Date(ms_since_19700101);
-    }
 
-    public static Calendar apptTimeToCalendar(int minutes) {
-    	final long ms_since_16010101 = minutes * (60*1000L);
-        final long ms_since_19700101 = ms_since_16010101 - EPOCH_DIFF;
-        Calendar c = Calendar.getInstance(PSTTimeZone.utcTimeZone);
-        c.setTimeInMillis(ms_since_19700101);
-        return c;
-    }
-    
-    public static Calendar apptTimeToUTC(int minutes, PSTTimeZone tz) {
-		// Must convert minutes since 1/1/1601 in local time to UTC
-		// There's got to be a better way of doing this...
-		// First get a UTC calendar object that contains _local time_
-		Calendar cUTC = PSTObject.apptTimeToCalendar(minutes);
-		if ( tz != null ) {
-			// Create an empty Calendar object with the required time zone
-			Calendar cLocal = Calendar.getInstance(tz.getSimpleTimeZone());
-			cLocal.clear();
-			
-			// Now transfer the local date/time from the UTC calendar object
-			// to the object that knows about the time zone...
-			cLocal.set(cUTC.get(Calendar.YEAR),
-					   cUTC.get(Calendar.MONTH),
-					   cUTC.get(Calendar.DATE),
-					   cUTC.get(Calendar.HOUR_OF_DAY),
-					   cUTC.get(Calendar.MINUTE),
-					   cUTC.get(Calendar.SECOND));
-			
-			// Get the true UTC from the local time calendar object.
-			// Drop any milliseconds, they won't be printed anyway!
-			long utcs = cLocal.getTimeInMillis() / 1000;
-			
-			// Finally, set the true UTC in the UTC calendar object
-			cUTC.setTimeInMillis(utcs * 1000);
-		} // else hope for the best!
-
-		return cUTC;
-	}
 }
diff --git a/com/pff/PSTRss.java b/com/pff/objects/PSTRss.java
similarity index 84%
rename from com/pff/PSTRss.java
rename to com/pff/objects/PSTRss.java
index 85c830c..bec2e93 100644
--- a/com/pff/PSTRss.java
+++ b/com/pff/objects/PSTRss.java
@@ -31,11 +31,17 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.objects;
 
 import java.io.IOException;
 import java.util.HashMap;
 
+import com.pff.exceptions.PSTException;
+import com.pff.parsing.DescriptorIndexNode;
+import com.pff.parsing.PSTDescriptorItem;
+import com.pff.parsing.tables.PSTTableBC;
+import com.pff.source.PSTSource;
+
 /**
  * Object that represents a RSS item
  * @author Richard Johnson
@@ -48,7 +54,7 @@ public class PSTRss extends PSTMessage {
 	 * @throws PSTException
 	 * @throws IOException
 	 */
-	public PSTRss(PSTFile theFile, DescriptorIndexNode descriptorIndexNode)
+	public PSTRss(PSTSource theFile, DescriptorIndexNode descriptorIndexNode)
 			throws PSTException, IOException {
 		super(theFile, descriptorIndexNode);
 	}
@@ -59,7 +65,7 @@ public PSTRss(PSTFile theFile, DescriptorIndexNode descriptorIndexNode)
 	 * @param table
 	 * @param localDescriptorItems
 	 */
-	public PSTRss(PSTFile theFile, DescriptorIndexNode folderIndexNode,
+	public PSTRss(PSTSource theFile, DescriptorIndexNode folderIndexNode,
 			PSTTableBC table,
 			HashMap localDescriptorItems) {
 		super(theFile, folderIndexNode, table, localDescriptorItems);
@@ -69,43 +75,43 @@ public PSTRss(PSTFile theFile, DescriptorIndexNode folderIndexNode,
 	 * Channel
 	 */
 	public String getPostRssChannelLink() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008900, PSTFile.PSETID_PostRss));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008900, PSTSource.PSETID_PostRss));
 	}
 	/**
 	 * Item link
 	 */
 	public String getPostRssItemLink() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008901, PSTFile.PSETID_PostRss));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008901, PSTSource.PSETID_PostRss));
 	}
 	/**
 	 * Item hash Integer 32-bit signed
 	 */
 	public int getPostRssItemHash() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008902, PSTFile.PSETID_PostRss));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008902, PSTSource.PSETID_PostRss));
 	}
 	/**
 	 * Item GUID
 	 */
 	public String getPostRssItemGuid() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008903, PSTFile.PSETID_PostRss));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008903, PSTSource.PSETID_PostRss));
 	}
 	/**
 	 * Channel GUID
 	 */
 	public String getPostRssChannel() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008904, PSTFile.PSETID_PostRss));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008904, PSTSource.PSETID_PostRss));
 	}
 	/**
 	 * Item XML
 	 */
 	public String getPostRssItemXml() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008905, PSTFile.PSETID_PostRss));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008905, PSTSource.PSETID_PostRss));
 	}
 	/**
 	 * Subscription
 	 */
 	public String getPostRssSubscription() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008906, PSTFile.PSETID_PostRss));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008906, PSTSource.PSETID_PostRss));
 	}
 
 	public String toString() {
diff --git a/com/pff/PSTTask.java b/com/pff/objects/PSTTask.java
similarity index 81%
rename from com/pff/PSTTask.java
rename to com/pff/objects/PSTTask.java
index c58888b..1477aec 100644
--- a/com/pff/PSTTask.java
+++ b/com/pff/objects/PSTTask.java
@@ -31,11 +31,17 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.objects;
 
 import java.io.IOException;
-import java.util.HashMap;
 import java.util.Date;
+import java.util.HashMap;
+
+import com.pff.exceptions.PSTException;
+import com.pff.parsing.DescriptorIndexNode;
+import com.pff.parsing.PSTDescriptorItem;
+import com.pff.parsing.tables.PSTTableBC;
+import com.pff.source.PSTSource;
 
 /**
  * Object that represents Task items
@@ -49,7 +55,7 @@ public class PSTTask extends PSTMessage {
 	 * @throws PSTException
 	 * @throws IOException
 	 */
-	public PSTTask(PSTFile theFile, DescriptorIndexNode descriptorIndexNode)
+	public PSTTask(PSTSource theFile, DescriptorIndexNode descriptorIndexNode)
 			throws PSTException, IOException {
 		super(theFile, descriptorIndexNode);
 	}
@@ -60,7 +66,7 @@ public PSTTask(PSTFile theFile, DescriptorIndexNode descriptorIndexNode)
 	 * @param table
 	 * @param localDescriptorItems
 	 */
-	public PSTTask(PSTFile theFile, DescriptorIndexNode folderIndexNode,
+	public PSTTask(PSTSource theFile, DescriptorIndexNode folderIndexNode,
 			PSTTableBC table,
 			HashMap localDescriptorItems) {
 		super(theFile, folderIndexNode, table, localDescriptorItems);
@@ -70,98 +76,98 @@ public PSTTask(PSTFile theFile, DescriptorIndexNode folderIndexNode,
 	 * Status Integer 32-bit signed 0x0 => Not started
 	 */
 	public int getTaskStatus() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008101, PSTFile.PSETID_Task));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008101, PSTSource.PSETID_Task));
 	}
 	/**
 	 * Percent Complete Floating point double precision (64-bit)
 	 */
 	public double getPercentComplete() {
-		return getDoubleItem(pstFile.getNameToIdMapItem(0x00008102, PSTFile.PSETID_Task));
+		return getDoubleItem(pstFile.getNameToIdMapItem(0x00008102, PSTSource.PSETID_Task));
 	}
 	/**
 	 * Is team task Boolean
 	 */
 	public boolean isTeamTask() {
-		return getBooleanItem(pstFile.getNameToIdMapItem(0x00008103, PSTFile.PSETID_Task));
+		return getBooleanItem(pstFile.getNameToIdMapItem(0x00008103, PSTSource.PSETID_Task));
 	}
 	
 	/**
 	 * Date completed Filetime
 	 */
 	public Date getTaskDateCompleted() {
-		return getDateItem(pstFile.getNameToIdMapItem(0x0000810f, PSTFile.PSETID_Task));
+		return getDateItem(pstFile.getNameToIdMapItem(0x0000810f, PSTSource.PSETID_Task));
 	}
 	/**
 	 * Actual effort in minutes Integer 32-bit signed
 	 */
 	public int getTaskActualEffort() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008110, PSTFile.PSETID_Task));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008110, PSTSource.PSETID_Task));
 	}
 	/**
 	 * Total effort in minutes Integer 32-bit signed
 	 */
 	public int getTaskEstimatedEffort() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008111, PSTFile.PSETID_Task));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008111, PSTSource.PSETID_Task));
 	}
 	/**
 	 * Task version Integer 32-bit signed FTK: Access count
 	 */
 	public int getTaskVersion() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008112, PSTFile.PSETID_Task));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008112, PSTSource.PSETID_Task));
 	}
 	/**
 	 * Complete Boolean
 	 */
 	public boolean isTaskComplete() {
-		return getBooleanItem(pstFile.getNameToIdMapItem(0x0000811c, PSTFile.PSETID_Task));
+		return getBooleanItem(pstFile.getNameToIdMapItem(0x0000811c, PSTSource.PSETID_Task));
 	}
 	/**
 	 * Owner ASCII or Unicode string
 	 */
 	public String getTaskOwner() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x0000811f, PSTFile.PSETID_Task));
+		return getStringItem(pstFile.getNameToIdMapItem(0x0000811f, PSTSource.PSETID_Task));
 	}
 	/**
 	 * Delegator ASCII or Unicode string
 	 */
 	public String getTaskAssigner() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008121, PSTFile.PSETID_Task));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008121, PSTSource.PSETID_Task));
 	}
 	/**
 	 * Unknown ASCII or Unicode string
 	 */
 	public String getTaskLastUser() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008122, PSTFile.PSETID_Task));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008122, PSTSource.PSETID_Task));
 	}
 	/**
 	 * Ordinal Integer 32-bit signed
 	 */
 	public int getTaskOrdinal() {
-		return this.getIntItem(pstFile.getNameToIdMapItem(0x00008123, PSTFile.PSETID_Task));
+		return this.getIntItem(pstFile.getNameToIdMapItem(0x00008123, PSTSource.PSETID_Task));
 	}
 	/**
 	 * Is recurring Boolean
 	 */
 	public boolean isTaskFRecurring() {
-		return getBooleanItem(pstFile.getNameToIdMapItem(0x00008126, PSTFile.PSETID_Task));
+		return getBooleanItem(pstFile.getNameToIdMapItem(0x00008126, PSTSource.PSETID_Task));
 	}
 	/**
 	 * Role ASCII or Unicode string
 	 */
 	public String getTaskRole() {
-		return getStringItem(pstFile.getNameToIdMapItem(0x00008127, PSTFile.PSETID_Task));
+		return getStringItem(pstFile.getNameToIdMapItem(0x00008127, PSTSource.PSETID_Task));
 	}
 	/**
 	 * Ownership Integer 32-bit signed
 	 */
 	public int getTaskOwnership() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x00008129, PSTFile.PSETID_Task));
+		return getIntItem(pstFile.getNameToIdMapItem(0x00008129, PSTSource.PSETID_Task));
 	}
 	/**
 	 * Delegation State
 	 */
 	public int getAcceptanceState() {
-		return getIntItem(pstFile.getNameToIdMapItem(0x0000812a, PSTFile.PSETID_Task));
+		return getIntItem(pstFile.getNameToIdMapItem(0x0000812a, PSTSource.PSETID_Task));
 	}
 
 	public String toString() {
diff --git a/com/pff/PSTAppointmentRecurrence.java b/com/pff/objects/sub/PSTAppointmentRecurrence.java
similarity index 76%
rename from com/pff/PSTAppointmentRecurrence.java
rename to com/pff/objects/sub/PSTAppointmentRecurrence.java
index 4d06c3d..f78f138 100644
--- a/com/pff/PSTAppointmentRecurrence.java
+++ b/com/pff/objects/sub/PSTAppointmentRecurrence.java
@@ -31,7 +31,7 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.objects.sub;
 
 /*
 import java.text.SimpleDateFormat;
@@ -41,6 +41,12 @@
 import java.util.Date;
 import java.util.SimpleTimeZone;
 
+import com.pff.PSTUtils;
+import com.pff.exceptions.PSTAppointmentException;
+import com.pff.objects.PSTAppointment;
+import com.pff.objects.PSTAttachment;
+import com.pff.objects.PSTMessage;
+
 /**
  * Class containing recurrence information for a recurring appointment
  * @author Orin Eman
@@ -52,6 +58,14 @@ public class PSTAppointmentRecurrence {
 
 	// Access methods
 
+	public Calendar[] getDeletedInstanceDates() {
+		return DeletedInstanceDates;
+	}
+	
+	public Calendar[] getModifiedInstanceDates() {
+		return ModifiedInstanceDates;
+	}
+	
 	public short getExceptionCount() {
 		return ExceptionCount;
 	}
@@ -132,34 +146,34 @@ public PSTAppointmentRecurrence(byte[] recurrencePattern, PSTAppointment appt, P
 		SimpleTimeZone stz = RecurrenceTimeZone.getSimpleTimeZone();
 
 		// Read the structure
-		RecurFrequency = (short)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, 4, 6);
-		PatternType = (short)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, 6, 8);
-		CalendarType = (short)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, 8, 10);
-		FirstDateTime = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, 10, 14);
-		Period = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, 14, 18);
-		SlidingFlag = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, 18, 22);
+		RecurFrequency = (short)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, 4, 6);
+		PatternType = (short)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, 6, 8);
+		CalendarType = (short)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, 8, 10);
+		FirstDateTime = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, 10, 14);
+		Period = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, 14, 18);
+		SlidingFlag = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, 18, 22);
 		int offset = 22;
 		if ( PatternType != 0 ) {
-			PatternSpecific = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+			PatternSpecific = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 			offset += 4;
 			if ( PatternType == 0x0003 || PatternType == 0x000B ) {
-				PatternSpecificNth = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+				PatternSpecificNth = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 				offset += 4;
 			}
 		}
-		EndType = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		EndType = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 		offset += 4;
-		OccurrenceCount = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		OccurrenceCount = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 		offset += 4;
-		FirstDOW = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		FirstDOW = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 		offset += 4;
 
-		DeletedInstanceCount = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		DeletedInstanceCount = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 		offset += 4;
 		DeletedInstanceDates = new Calendar[DeletedInstanceCount];
 		for ( int i = 0; i < DeletedInstanceCount; ++i ) {
-			DeletedInstanceDates[i] = PSTObject.apptTimeToUTC(
-					(int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4),
+			DeletedInstanceDates[i] = PSTUtils.apptTimeToUTC(
+					(int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4),
 					RecurrenceTimeZone);
 			offset += 4;
 /*
@@ -169,12 +183,12 @@ public PSTAppointmentRecurrence(byte[] recurrencePattern, PSTAppointment appt, P
 /**/		
 		}
 		
-		ModifiedInstanceCount = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		ModifiedInstanceCount = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 		offset += 4;
 		ModifiedInstanceDates = new Calendar[ModifiedInstanceCount];
 		for ( int i = 0; i < ModifiedInstanceCount; ++i ) {
-			ModifiedInstanceDates[i] = PSTObject.apptTimeToUTC(
-					(int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4),
+			ModifiedInstanceDates[i] = PSTUtils.apptTimeToUTC(
+					(int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4),
 					RecurrenceTimeZone);
 			offset += 4;
 /*
@@ -184,19 +198,19 @@ public PSTAppointmentRecurrence(byte[] recurrencePattern, PSTAppointment appt, P
 /**/		
 		}
 		
-		StartDate = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		StartDate = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 		offset += 4;
-		EndDate = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		EndDate = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 		offset += 4 + 4;	// Skip ReaderVersion2
 		
-		writerVersion2 = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		writerVersion2 = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 		offset += 4;
 
-		StartTimeOffset = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		StartTimeOffset = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 		offset += 4;
-		EndTimeOffset = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+		EndTimeOffset = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 		offset += 4;
-		ExceptionCount = (short)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+2);
+		ExceptionCount = (short)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+2);
 		offset += 2;
 
 		// Read exceptions
@@ -207,13 +221,13 @@ public PSTAppointmentRecurrence(byte[] recurrencePattern, PSTAppointment appt, P
 		}
 		
 		if ( (offset + 4) <= recurrencePattern.length ) {
-			int ReservedBlock1Size = (int)PSTObject.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
+			int ReservedBlock1Size = (int)PSTUtils.convertLittleEndianBytesToLong(recurrencePattern, offset, offset+4);
 			offset += 4 + (ReservedBlock1Size * 4);
 		}
 		
 		// Read extended exception info
 		for ( int i = 0; i < ExceptionCount; ++i ) {
-			Exceptions[i].ExtendedException(recurrencePattern, offset);
+			Exceptions[i].buildExtendedException(recurrencePattern, offset);
 			offset += Exceptions[i].getExtendedLength();
 /*
 			Calendar c = PSTObject.apptTimeToUTC(Exceptions[i].getStartDateTime(), RecurrenceTimeZone);
diff --git a/com/pff/PSTRecipient.java b/com/pff/objects/sub/PSTRecipient.java
similarity index 96%
rename from com/pff/PSTRecipient.java
rename to com/pff/objects/sub/PSTRecipient.java
index 5d9bc8e..4e7ebe3 100644
--- a/com/pff/PSTRecipient.java
+++ b/com/pff/objects/sub/PSTRecipient.java
@@ -31,11 +31,13 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.objects.sub;
 
 //import java.util.Date;
 import java.util.HashMap;
 
+import com.pff.parsing.tables.PSTTable7CItem;
+
 /**
  * Class containing recipient information
  * @author Orin Eman
@@ -50,8 +52,7 @@ public class PSTRecipient {
 	public static final int MAPI_CC = 2;
 	public static final int MAPI_BCC = 3;
 	
-	PSTRecipient(HashMap recipientDetails)
-	{
+	public PSTRecipient(HashMap recipientDetails) {
 		details = recipientDetails;
 	}
 	
diff --git a/com/pff/PSTTimeZone.java b/com/pff/objects/sub/PSTTimeZone.java
similarity index 77%
rename from com/pff/PSTTimeZone.java
rename to com/pff/objects/sub/PSTTimeZone.java
index 063d2ca..152a424 100644
--- a/com/pff/PSTTimeZone.java
+++ b/com/pff/objects/sub/PSTTimeZone.java
@@ -31,11 +31,13 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.objects.sub;
 
 import java.util.Calendar;
 import java.util.SimpleTimeZone;
 
+import com.pff.PSTUtils;
+
 /**
  * Class containing time zone information
  * @author Orin Eman
@@ -44,21 +46,23 @@
  */
 
 public class PSTTimeZone {
-	PSTTimeZone(byte [] timeZoneData) {
+	
+	
+	public PSTTimeZone(byte [] timeZoneData) {
 		this.rule = null;
 		name = "";
 
 		try {
-			int headerLen = (int)PSTObject.convertLittleEndianBytesToLong(timeZoneData, 2, 4);
-			int nameLen = 2*(int)PSTObject.convertLittleEndianBytesToLong(timeZoneData, 6, 8);
+			int headerLen = (int)PSTUtils.convertLittleEndianBytesToLong(timeZoneData, 2, 4);
+			int nameLen = 2*(int)PSTUtils.convertLittleEndianBytesToLong(timeZoneData, 6, 8);
 			name = new String(timeZoneData, 8, nameLen, "UTF-16LE");
 			int ruleOffset = 8+nameLen;
-			int nRules = (int)PSTObject.convertLittleEndianBytesToLong(timeZoneData, ruleOffset, ruleOffset+2);
+			int nRules = (int)PSTUtils.convertLittleEndianBytesToLong(timeZoneData, ruleOffset, ruleOffset+2);
 
 			ruleOffset = 4 + headerLen;
 			for ( int rule = 0; rule < nRules; ++rule ) {
 				// Is this rule the effective rule?
-				int flags = (int)PSTObject.convertLittleEndianBytesToLong(timeZoneData, ruleOffset+4, ruleOffset+6);
+				int flags = (int)PSTUtils.convertLittleEndianBytesToLong(timeZoneData, ruleOffset+4, ruleOffset+6);
 				if ( (flags & 0x0002) != 0 ) {
 					this.rule = new TZRule(timeZoneData, ruleOffset+6);
 					break;
@@ -74,7 +78,7 @@ public class PSTTimeZone {
 		}
 	}
 	
-	PSTTimeZone(String name, byte[] timeZoneData) {
+	public PSTTimeZone(String name, byte[] timeZoneData) {
 		this.name = name;
 		this.rule = null;
 		
@@ -181,14 +185,14 @@ public class SYSTEMTIME {
 		}
 
 		SYSTEMTIME(byte[] timeZoneData, int offset) {
-			wYear = (short)(PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset, offset+2)&0x7FFF);
-			wMonth = (short)(PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset+2, offset+4)&0x7FFF);
-			wDayOfWeek = (short)(PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset+4, offset+6)&0x7FFF);
-			wDay = (short)(PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset+6, offset+8)&0x7FFF);
-			wHour = (short)(PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset+8, offset+10)&0x7FFF);
-			wMinute = (short)(PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset+10, offset+12)&0x7FFF);
-			wSecond = (short)(PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset+12, offset+14)&0x7FFF);
-			wMilliseconds = (short)(PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset+14, offset+16)&0x7FFF);
+			wYear = (short)(PSTUtils.convertLittleEndianBytesToLong(timeZoneData, offset, offset+2)&0x7FFF);
+			wMonth = (short)(PSTUtils.convertLittleEndianBytesToLong(timeZoneData, offset+2, offset+4)&0x7FFF);
+			wDayOfWeek = (short)(PSTUtils.convertLittleEndianBytesToLong(timeZoneData, offset+4, offset+6)&0x7FFF);
+			wDay = (short)(PSTUtils.convertLittleEndianBytesToLong(timeZoneData, offset+6, offset+8)&0x7FFF);
+			wHour = (short)(PSTUtils.convertLittleEndianBytesToLong(timeZoneData, offset+8, offset+10)&0x7FFF);
+			wMinute = (short)(PSTUtils.convertLittleEndianBytesToLong(timeZoneData, offset+10, offset+12)&0x7FFF);
+			wSecond = (short)(PSTUtils.convertLittleEndianBytesToLong(timeZoneData, offset+12, offset+14)&0x7FFF);
+			wMilliseconds = (short)(PSTUtils.convertLittleEndianBytesToLong(timeZoneData, offset+14, offset+16)&0x7FFF);
 		}
 		
 		boolean isEqual(SYSTEMTIME rhs) {
@@ -223,10 +227,10 @@ private class TZRule {
 			this.dtStart = dtStart;
 			InitBiases(timeZoneData, offset);
 			@SuppressWarnings("unused")
-			short wStandardYear = (short)PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset+12, offset+14);
+			short wStandardYear = (short)PSTUtils.convertLittleEndianBytesToLong(timeZoneData, offset+12, offset+14);
 			startStandard = new SYSTEMTIME(timeZoneData, offset+14);
 			@SuppressWarnings("unused")
-			short wDaylightYear = (short)PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset+30, offset+32);
+			short wDaylightYear = (short)PSTUtils.convertLittleEndianBytesToLong(timeZoneData, offset+30, offset+32);
 			startDaylight = new SYSTEMTIME(timeZoneData, offset+32);
 		}
 
@@ -238,9 +242,9 @@ private class TZRule {
 		}
 		
 		private void InitBiases(byte[] timeZoneData, int offset) {
-			lBias = (int)PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset, offset+4);
-			lStandardBias = (int)PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset+4, offset+8);
-			lDaylightBias = (int)PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset+8, offset+12);
+			lBias = (int)PSTUtils.convertLittleEndianBytesToLong(timeZoneData, offset, offset+4);
+			lStandardBias = (int)PSTUtils.convertLittleEndianBytesToLong(timeZoneData, offset+4, offset+8);
+			lDaylightBias = (int)PSTUtils.convertLittleEndianBytesToLong(timeZoneData, offset+8, offset+12);
 		}
 		
 		boolean isEqual(TZRule rhs) {
diff --git a/com/pff/DescriptorIndexNode.java b/com/pff/parsing/DescriptorIndexNode.java
similarity index 72%
rename from com/pff/DescriptorIndexNode.java
rename to com/pff/parsing/DescriptorIndexNode.java
index 8e97b76..6b13daf 100644
--- a/com/pff/DescriptorIndexNode.java
+++ b/com/pff/parsing/DescriptorIndexNode.java
@@ -31,10 +31,14 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.parsing;
 
 import java.io.IOException;
 
+import com.pff.PSTUtils;
+import com.pff.exceptions.PSTException;
+import com.pff.source.PSTSource;
+
 /**
  * DescriptorIndexNode is a leaf item from the Descriptor index b-tree
  * It is like a pointer to an element in the PST file, everything has one...
@@ -53,21 +57,21 @@ public class DescriptorIndexNode {
 	 * parse the data out into something meaningful
 	 * @param data
 	 */
-	DescriptorIndexNode(byte[] data, int pstFileType) {
+	public DescriptorIndexNode(byte[] data, int pstFileType) {
 		// parse it out
 		// first 4 bytes
-		if (pstFileType == PSTFile.PST_TYPE_ANSI) {
-			descriptorIdentifier = (int)PSTObject.convertLittleEndianBytesToLong(data, 0, 4);
-			dataOffsetIndexIdentifier = (int)PSTObject.convertLittleEndianBytesToLong(data, 4, 8);
-			localDescriptorsOffsetIndexIdentifier = (int)PSTObject.convertLittleEndianBytesToLong(data, 8, 12);
-			parentDescriptorIndexIdentifier = (int)PSTObject.convertLittleEndianBytesToLong(data, 12, 16);
+		if (pstFileType == PSTSource.PST_TYPE_ANSI) {
+			descriptorIdentifier = (int)PSTUtils.convertLittleEndianBytesToLong(data, 0, 4);
+			dataOffsetIndexIdentifier = (int)PSTUtils.convertLittleEndianBytesToLong(data, 4, 8);
+			localDescriptorsOffsetIndexIdentifier = (int)PSTUtils.convertLittleEndianBytesToLong(data, 8, 12);
+			parentDescriptorIndexIdentifier = (int)PSTUtils.convertLittleEndianBytesToLong(data, 12, 16);
 			//itemType = (int)PSTObject.convertLittleEndianBytesToLong(data, 28, 32);
 		} else {
-			descriptorIdentifier = (int)PSTObject.convertLittleEndianBytesToLong(data, 0, 4);
-			dataOffsetIndexIdentifier = (int)PSTObject.convertLittleEndianBytesToLong(data, 8, 16);
-			localDescriptorsOffsetIndexIdentifier = (int)PSTObject.convertLittleEndianBytesToLong(data, 16, 24);
-			parentDescriptorIndexIdentifier = (int)PSTObject.convertLittleEndianBytesToLong(data, 24, 28);
-			itemType = (int)PSTObject.convertLittleEndianBytesToLong(data, 28, 32);
+			descriptorIdentifier = (int)PSTUtils.convertLittleEndianBytesToLong(data, 0, 4);
+			dataOffsetIndexIdentifier = (int)PSTUtils.convertLittleEndianBytesToLong(data, 8, 16);
+			localDescriptorsOffsetIndexIdentifier = (int)PSTUtils.convertLittleEndianBytesToLong(data, 16, 24);
+			parentDescriptorIndexIdentifier = (int)PSTUtils.convertLittleEndianBytesToLong(data, 24, 28);
+			itemType = (int)PSTUtils.convertLittleEndianBytesToLong(data, 28, 32);
 		}
 	}
 
@@ -82,7 +86,7 @@ void readData(PSTFile file)
 	 *
 	 */
 
-	PSTNodeInputStream getNodeInputStream(PSTFile pstFile)
+	PSTNodeInputStream getNodeInputStream(PSTSource pstFile)
 			throws IOException, PSTException
 	{
 		return new PSTNodeInputStream(pstFile,pstFile.getOffsetIndexNode(dataOffsetIndexIdentifier));
diff --git a/com/pff/OffsetIndexItem.java b/com/pff/parsing/OffsetIndexItem.java
similarity index 67%
rename from com/pff/OffsetIndexItem.java
rename to com/pff/parsing/OffsetIndexItem.java
index 76f1808..6e7635c 100644
--- a/com/pff/OffsetIndexItem.java
+++ b/com/pff/parsing/OffsetIndexItem.java
@@ -31,34 +31,38 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.parsing;
+
+import com.pff.PSTUtils;
+import com.pff.source.PSTSource;
 
 /**
  * OffsetIndexItem is a leaf item from the Offset index b-tree
  * Only really used internally to get the file offset for items
  * @author Richard Johnson
  */
-class OffsetIndexItem {
+public class OffsetIndexItem {
 	long indexIdentifier;
 	long fileOffset;
 	int size;
 	long cRef;
 	
-	OffsetIndexItem(byte[] data, int pstFileType) {
-		if (pstFileType == PSTFile.PST_TYPE_ANSI) {
-			indexIdentifier = PSTObject.convertLittleEndianBytesToLong(data, 0, 4);
-			fileOffset = PSTObject.convertLittleEndianBytesToLong(data, 4, 8);
-			size = (int)PSTObject.convertLittleEndianBytesToLong(data, 8, 10);
-			cRef = (int)PSTObject.convertLittleEndianBytesToLong(data, 10, 12);
+	public OffsetIndexItem(byte[] data, int pstFileType) {
+		if (pstFileType == PSTSource.PST_TYPE_ANSI) {
+			indexIdentifier = PSTUtils.convertLittleEndianBytesToLong(data, 0, 4);
+			fileOffset = PSTUtils.convertLittleEndianBytesToLong(data, 4, 8);
+			size = (int)PSTUtils.convertLittleEndianBytesToLong(data, 8, 10);
+			cRef = (int)PSTUtils.convertLittleEndianBytesToLong(data, 10, 12);
 		} else {
-			indexIdentifier = PSTObject.convertLittleEndianBytesToLong(data, 0, 8);
-			fileOffset = PSTObject.convertLittleEndianBytesToLong(data, 8, 16);
-			size = (int)PSTObject.convertLittleEndianBytesToLong(data, 16, 18);
-			cRef = (int)PSTObject.convertLittleEndianBytesToLong(data, 16, 18);
+			indexIdentifier = PSTUtils.convertLittleEndianBytesToLong(data, 0, 8);
+			fileOffset = PSTUtils.convertLittleEndianBytesToLong(data, 8, 16);
+			size = (int)PSTUtils.convertLittleEndianBytesToLong(data, 16, 18);
+			cRef = (int)PSTUtils.convertLittleEndianBytesToLong(data, 16, 18);
 		}
 		//System.out.println("Data size: "+data.length);
 		
 	}
+	
 
 	@Override
 	public String toString() {
@@ -68,4 +72,23 @@ public String toString() {
 			"cRef: "+cRef+" (0x"+Long.toHexString(cRef)+" bin:"+Long.toBinaryString(cRef)+")\n"+
 			"Size: "+size+" (0x"+Long.toHexString(size)+")";
 	}
+
+
+	public long getIndexIdentifier() {
+		return indexIdentifier;
+	}
+
+
+	public long getFileOffset() {
+		return fileOffset;
+	}
+
+
+	public int getSize() {
+		return size;
+	}
+	
+	
+	
+	
 }
diff --git a/com/pff/PSTDescriptorItem.java b/com/pff/parsing/PSTDescriptorItem.java
similarity index 69%
rename from com/pff/PSTDescriptorItem.java
rename to com/pff/parsing/PSTDescriptorItem.java
index 3d67dc7..96a861d 100644
--- a/com/pff/PSTDescriptorItem.java
+++ b/com/pff/parsing/PSTDescriptorItem.java
@@ -31,40 +31,41 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.parsing;
 
 import java.io.IOException;
-import java.util.*;
+
+import com.pff.PSTUtils;
+import com.pff.exceptions.PSTException;
+import com.pff.source.PSTSource;
+
 
 /**
  * The descriptor items contain information that describes a PST object.
  * This is like extended table entries, usually when the data cannot fit in a traditional table item.
  * @author Richard Johnson
  */
-class PSTDescriptorItem
+public class PSTDescriptorItem
 {
-	PSTDescriptorItem(byte[] data, int offset, PSTFile pstFile)
-	{
+	public PSTDescriptorItem(byte[] data, int offset, PSTSource pstFile) {
 		this.pstFile = pstFile;
 
-		if (pstFile.getPSTFileType() == PSTFile.PST_TYPE_ANSI) {
-			descriptorIdentifier = (int)PSTObject.convertLittleEndianBytesToLong(data, offset, offset+4);
-			offsetIndexIdentifier = ((int)PSTObject.convertLittleEndianBytesToLong(data, offset+4, offset+8))
+		if (pstFile.getPSTFileType() == PSTSource.PST_TYPE_ANSI) {
+			descriptorIdentifier = (int)PSTUtils.convertLittleEndianBytesToLong(data, offset, offset+4);
+			offsetIndexIdentifier = ((int)PSTUtils.convertLittleEndianBytesToLong(data, offset+4, offset+8))
 										& 0xfffffffe;
-			subNodeOffsetIndexIdentifier = (int)PSTObject.convertLittleEndianBytesToLong(data, offset+8, offset+12)
+			subNodeOffsetIndexIdentifier = (int)PSTUtils.convertLittleEndianBytesToLong(data, offset+8, offset+12)
 										& 0xfffffffe;
 		} else {
-			descriptorIdentifier = (int)PSTObject.convertLittleEndianBytesToLong(data, offset, offset+4);
-			offsetIndexIdentifier = ((int)PSTObject.convertLittleEndianBytesToLong(data, offset+8, offset+16))
+			descriptorIdentifier = (int)PSTUtils.convertLittleEndianBytesToLong(data, offset, offset+4);
+			offsetIndexIdentifier = ((int)PSTUtils.convertLittleEndianBytesToLong(data, offset+8, offset+16))
 										& 0xfffffffe;
-			subNodeOffsetIndexIdentifier = (int)PSTObject.convertLittleEndianBytesToLong(data, offset+16, offset+24)
+			subNodeOffsetIndexIdentifier = (int)PSTUtils.convertLittleEndianBytesToLong(data, offset+16, offset+24)
 										& 0xfffffffe;
 		}
 	}
 
-	public byte[] getData()
-		throws IOException, PSTException
-	{
+	public byte[] getData() throws IOException, PSTException {
 		if ( dataBlockData != null ) {
 			return dataBlockData;
 		}
@@ -76,11 +77,8 @@ public byte[] getData()
 		return dataBlockData;
 	}
 	
-	public int[] getBlockOffsets()
-		throws IOException, PSTException
-	{
+	public int[] getBlockOffsets()	throws IOException, PSTException {
 		if ( dataBlockOffsets != null ) {
-
 			return dataBlockOffsets;
 		}
 		Long[] offsets = pstFile.readLeaf(offsetIndexIdentifier).getBlockOffsets();
@@ -91,14 +89,14 @@ public int[] getBlockOffsets()
 		return offsetsOut;
 	}
 	
-	public int getDataSize()
-		throws IOException, PSTException
-	{
+	public int getDataSize() throws IOException, PSTException {
 		return pstFile.getLeafSize(offsetIndexIdentifier);
 	}
 	
 	// Public data
 	int descriptorIdentifier;
+	
+
 	int offsetIndexIdentifier;
 	int subNodeOffsetIndexIdentifier;
 
@@ -106,7 +104,7 @@ public int getDataSize()
 	//private PSTFile.PSTFileBlock dataBlock = null;
 	byte[] dataBlockData = null;
 	int[] dataBlockOffsets = null;
-	private PSTFile pstFile;
+	private PSTSource pstFile;
 
 	@Override
 	public String toString() {
@@ -118,5 +116,17 @@ public String toString() {
 			
 		
 	}
+	
+	
+	public int getDescriptorIdentifier() {
+		return descriptorIdentifier;
+	}
+
+	public int getSubNodeOffsetIndexIdentifier() {
+		return subNodeOffsetIndexIdentifier;
+	}
+	
+	
+	
 
 }
diff --git a/com/pff/PSTNodeInputStream.java b/com/pff/parsing/PSTNodeInputStream.java
similarity index 80%
rename from com/pff/PSTNodeInputStream.java
rename to com/pff/parsing/PSTNodeInputStream.java
index 3c36218..707126f 100644
--- a/com/pff/PSTNodeInputStream.java
+++ b/com/pff/parsing/PSTNodeInputStream.java
@@ -32,10 +32,15 @@
  *
  */
 
-package com.pff;
+package com.pff.parsing;
 
-import java.io.*;
-import java.util.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.LinkedList;
+
+import com.pff.PSTUtils;
+import com.pff.exceptions.PSTException;
+import com.pff.source.PSTSource;
 
 /**
  * this input stream basically "maps" an input stream on top of the random access file
@@ -43,8 +48,8 @@
  */
 public class PSTNodeInputStream extends InputStream {
 
-	private RandomAccessFile in;
-	private PSTFile pstFile;
+	//private _RandomAccessPSTSource in;
+	private PSTSource pstFile;
 	private LinkedList skipPoints = new LinkedList();
 	private LinkedList indexItems = new LinkedList();
 	private int currentBlock = 0;
@@ -56,14 +61,14 @@ public class PSTNodeInputStream extends InputStream {
 
 	private boolean encrypted = false;
 
-	PSTNodeInputStream(PSTFile pstFile, byte[] attachmentData) {
+	public PSTNodeInputStream(PSTSource pstFile, byte[] attachmentData) {
 		this.allData = attachmentData;
 		this.length = this.allData.length;
-		encrypted = pstFile.getEncryptionType() == PSTFile.ENCRYPTION_TYPE_COMPRESSIBLE;
+		encrypted = pstFile.getEncryptionType() == PSTSource.ENCRYPTION_TYPE_COMPRESSIBLE;
 		this.currentBlock = 0;
 		this.currentLocation = 0;
 	}
-	PSTNodeInputStream(PSTFile pstFile, byte[] attachmentData, boolean encrypted) {
+	public PSTNodeInputStream(PSTSource pstFile, byte[] attachmentData, boolean encrypted) {
 		this.allData = attachmentData;
 		this.encrypted = encrypted;
 		this.length = this.allData.length;
@@ -71,12 +76,12 @@ public class PSTNodeInputStream extends InputStream {
 		this.currentLocation = 0;
 	}
 
-	PSTNodeInputStream(PSTFile pstFile, PSTDescriptorItem descriptorItem)
+	public PSTNodeInputStream(PSTSource pstFile, PSTDescriptorItem descriptorItem)
 			throws IOException, PSTException
 	{
-		this.in = pstFile.getFileHandle();
+		//this.in = pstFile.getFileHandle();
 		this.pstFile = pstFile;
-		this.encrypted = pstFile.getEncryptionType() == PSTFile.ENCRYPTION_TYPE_COMPRESSIBLE;
+		this.encrypted = pstFile.getEncryptionType() == PSTSource.ENCRYPTION_TYPE_COMPRESSIBLE;
 
 		// we want to get the first block of data and see what we are dealing with
 		OffsetIndexItem offsetItem = pstFile.getOffsetIndexNode(descriptorItem.offsetIndexIdentifier);
@@ -86,12 +91,12 @@ public class PSTNodeInputStream extends InputStream {
 
 	}
 
-	PSTNodeInputStream(PSTFile pstFile, OffsetIndexItem offsetItem)
+	public PSTNodeInputStream(PSTSource pstFile, OffsetIndexItem offsetItem)
 			throws IOException, PSTException
 	{
-		this.in = pstFile.getFileHandle();
+		//this.in = pstFile.getFileHandle();
 		this.pstFile = pstFile;
-		this.encrypted = pstFile.getEncryptionType() == PSTFile.ENCRYPTION_TYPE_COMPRESSIBLE;
+		this.encrypted = pstFile.getEncryptionType() == PSTSource.ENCRYPTION_TYPE_COMPRESSIBLE;
 		loadFromOffsetItem(offsetItem);
 		this.currentBlock = 0;
 		this.currentLocation = 0;
@@ -102,9 +107,11 @@ private void loadFromOffsetItem(OffsetIndexItem offsetItem)
 	{
 		boolean bInternal = (offsetItem.indexIdentifier & 0x02) != 0;
 
-		in.seek(offsetItem.fileOffset);
+		//System.out.println("PSTNodeInputStream - loadFromOffsetItem 0 => seek "+ offsetItem.fileOffset);
+		
+		pstFile.getRASource().seek(offsetItem.fileOffset);
 		byte[] data = new byte[offsetItem.size];
-		in.read(data);
+		pstFile.getRASource().read(data);
 
 		if ( bInternal ) {
 			// All internal blocks are at least 8 bytes long...
@@ -116,7 +123,7 @@ private void loadFromOffsetItem(OffsetIndexItem offsetItem)
 			{
 				bInternal = false;
 				// we are a block, or xxblock
-				length = PSTObject.convertLittleEndianBytesToLong(data, 4, 8);
+				length = PSTUtils.convertLittleEndianBytesToLong(data, 4, 8);
 				// go through all of the blocks and create skip points.
 				this.getBlockSkipPoints(data);
 				return;
@@ -143,23 +150,25 @@ private void getBlockSkipPoints(byte[] data)
 			throw new PSTException("Unable to process XBlock, incorrect identifier");
 		}
 
-		int numberOfEntries = (int)PSTObject.convertLittleEndianBytesToLong(data, 2, 4);
+		int numberOfEntries = (int)PSTUtils.convertLittleEndianBytesToLong(data, 2, 4);
 
 		int arraySize = 8;
-		if (this.pstFile.getPSTFileType() == PSTFile.PST_TYPE_ANSI) {
+		if (this.pstFile.getPSTFileType() == PSTSource.PST_TYPE_ANSI) {
 			arraySize = 4;
 		}
 		if (data[1] == 0x2) {
 			// XXBlock
 			int offset = 8;
 			for (int x = 0; x < numberOfEntries; x++) {
-				long bid = PSTObject.convertLittleEndianBytesToLong(data, offset, offset+arraySize);
+				long bid = PSTUtils.convertLittleEndianBytesToLong(data, offset, offset+arraySize);
 				bid &= 0xfffffffe;
 				// get the details in this block and
 				OffsetIndexItem offsetItem = this.pstFile.getOffsetIndexNode(bid);
-				in.seek(offsetItem.fileOffset);
+				//System.out.println("PSTNodeInputStream - getBlockSkipPoints 0 => seek "+ offsetItem.fileOffset);
+				
+				pstFile.getRASource().seek(offsetItem.fileOffset);
 				byte[] blockData = new byte[offsetItem.size];
-				in.read(blockData);
+				pstFile.getRASource().read(blockData);
 				this.getBlockSkipPoints(blockData);
 				offset += arraySize;
 			}
@@ -167,7 +176,7 @@ private void getBlockSkipPoints(byte[] data)
 			// normal XBlock
 			int offset = 8;
 			for (int x = 0; x < numberOfEntries; x++) {
-				long bid = PSTObject.convertLittleEndianBytesToLong(data, offset, offset+arraySize);
+				long bid = PSTUtils.convertLittleEndianBytesToLong(data, offset, offset+arraySize);
 				bid &= 0xfffffffe;
 				// get the details in this block and add it to the list
 				OffsetIndexItem offsetItem = pstFile.getOffsetIndexNode(bid);
@@ -197,7 +206,7 @@ public int read()
 			int value = this.allData[(int)this.currentLocation] & 0xFF;
 			this.currentLocation++;
 			if (this.encrypted) {
-				value = PSTObject.compEnc[value];
+				value = PSTUtils.getCompEnc(value);
 			}
 			return value;
 		}
@@ -218,16 +227,16 @@ public int read()
 
 		// get the next byte.
 		long pos = (item.fileOffset + (this.currentLocation - skipPoint));
-		if (in.getFilePointer()  != pos) {
-			in.seek(pos);
+		if (pstFile.getRASource().position()  != pos) {
+			pstFile.getRASource().seek(pos);
 		}
 
-		int output = in.read();
+		int output = pstFile.getRASource().read();
 		if (output < 0) {
 			return -1;
 		}
 		if (this.encrypted) {
-			output = PSTObject.compEnc[output];
+			output = PSTUtils.getCompEnc(output);
 		}
 
 		this.currentLocation++;
@@ -235,7 +244,7 @@ public int read()
 		return output;
 	}
 
-	private int totalLoopCount = 0;
+	//private int totalLoopCount = 0;
 
 	/**
 	 * Read a block from the input stream.
@@ -263,14 +272,14 @@ public int read(byte[] output)
 			if (output.length >= bytesRemaining) {
 				System.arraycopy(this.allData, (int)this.currentLocation, output, 0, bytesRemaining);
 				if (this.encrypted) {
-					PSTObject.decode(output);
+					PSTUtils.decode(output);
 				}
 				this.currentLocation += bytesRemaining; // should be = to this.length
 				return bytesRemaining;
 			} else {
 				System.arraycopy(this.allData, (int)this.currentLocation, output, 0, output.length);
 				if (this.encrypted) {
-					PSTObject.decode(output);
+					PSTUtils.decode(output);
 				}
 				this.currentLocation += output.length;
 				return output.length;
@@ -287,7 +296,10 @@ public int read(byte[] output)
 			OffsetIndexItem offset = this.indexItems.get(this.currentBlock);
 			long skipPoint = this.skipPoints.get(currentBlock);
 			int currentPosInBlock = (int)(this.currentLocation - skipPoint);
-			in.seek(offset.fileOffset + currentPosInBlock);
+			
+			//System.out.println("PSTNodeInputStream - read 0 => seek "+ (offset.fileOffset + currentPosInBlock));
+			
+			pstFile.getRASource().seek(offset.fileOffset + currentPosInBlock);
 
 			long nextSkipPoint = skipPoint + offset.size;
 			int bytesRemaining = (output.length - totalBytesFilled);
@@ -300,7 +312,7 @@ public int read(byte[] output)
 			if (nextSkipPoint >= this.currentLocation + bytesRemaining) {
 				// we can fill the output with the rest of our current block!
 				byte[] chunk = new byte[bytesRemaining];
-				in.read(chunk);
+				pstFile.getRASource().read(chunk);
 
 				System.arraycopy(chunk, 0, output, totalBytesFilled, bytesRemaining);
 				totalBytesFilled += bytesRemaining;
@@ -311,18 +323,18 @@ public int read(byte[] output)
 				// we need to read out a whole chunk and keep going
 				int bytesToRead = offset.size - currentPosInBlock;
 				byte[] chunk = new byte[bytesToRead];
-				in.read(chunk);
+				pstFile.getRASource().read(chunk);
 				System.arraycopy(chunk, 0, output, totalBytesFilled, bytesToRead);
 				totalBytesFilled += bytesToRead;
 				this.currentBlock++;
 				this.currentLocation += bytesToRead;
 			}
-			totalLoopCount++;
+			//totalLoopCount++;
 		}
 
 		// decode the array if required
 		if (this.encrypted) {
-			PSTObject.decode(output);
+			PSTUtils.decode(output);
 		}
 
 		// fill up our chunk
@@ -430,7 +442,9 @@ public void seek(long location)
 			blockStart = this.indexItems.get(currentBlock).fileOffset;
 		}
 		long newFilePos = blockStart + (location - skipPoint);
-		this.in.seek(newFilePos);
+		
+		//System.out.println("PSTNodeInputStream - seek 0 => seek "+ newFilePos);
+		pstFile.getRASource().seek(newFilePos);
 
 	}
 
@@ -440,10 +454,10 @@ public long seekAndReadLong(long location, int bytes)
 		this.seek(location);
 		byte[] buffer = new byte[bytes];
 		this.read(buffer);
-		return PSTObject.convertLittleEndianBytesToLong(buffer);
+		return PSTUtils.convertLittleEndianBytesToLong(buffer);
 	}
 
-	public PSTFile getPSTFile() {
+	public PSTSource getPSTFile() {
 		return this.pstFile;
 	}
 
diff --git a/com/pff/PSTTable.java b/com/pff/parsing/tables/PSTTable.java
similarity index 96%
rename from com/pff/PSTTable.java
rename to com/pff/parsing/tables/PSTTable.java
index 1e27667..6892703 100644
--- a/com/pff/PSTTable.java
+++ b/com/pff/parsing/tables/PSTTable.java
@@ -31,11 +31,16 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.parsing.tables;
 
 import java.io.IOException;
 import java.util.HashMap;
 
+import com.pff.PSTUtils;
+import com.pff.exceptions.PSTException;
+import com.pff.parsing.PSTDescriptorItem;
+import com.pff.parsing.PSTNodeInputStream;
+
 
 /**
  * The PST Table is the workhorse of the whole system.
@@ -81,8 +86,8 @@ protected PSTTable(PSTNodeInputStream in, HashMap su
 		in.read(headdata);
 		if ((int)headdata[2] != 0xffffffec) {
 			//System.out.println(in.isEncrypted());
-			PSTObject.decode(headdata);
-			PSTObject.printHexFormatted(headdata, true);
+			PSTUtils.decode(headdata);
+			PSTUtils.printHexFormatted(headdata, true);
 			throw new PSTException("Unable to parse table, bad table type...");
 		}
 
@@ -131,7 +136,7 @@ protected PSTTable(PSTNodeInputStream in, HashMap su
 			headerNodeInfo.in.seek(headerNodeInfo.startOffset);
 			byte[] tmp = new byte[1024];
 			headerNodeInfo.in.read(tmp);
-			PSTObject.printHexFormatted(tmp, true);
+			PSTUtils.printHexFormatted(tmp, true);
 			//System.out.println(PSTObject.compEnc[headerByte]);
 			throw new PSTException("Unable to parse table, can't find BTHHEADER header information: "+headerByte);
 		}
diff --git a/com/pff/PSTTable7C.java b/com/pff/parsing/tables/PSTTable7C.java
similarity index 94%
rename from com/pff/PSTTable7C.java
rename to com/pff/parsing/tables/PSTTable7C.java
index e092ebf..883dd16 100644
--- a/com/pff/PSTTable7C.java
+++ b/com/pff/parsing/tables/PSTTable7C.java
@@ -31,20 +31,27 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.parsing.tables;
 
 /*
 import java.io.UnsupportedEncodingException;
 /**/
-import java.util.*;
-import java.io.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import com.pff.exceptions.PSTException;
+import com.pff.parsing.PSTDescriptorItem;
+import com.pff.parsing.PSTNodeInputStream;
+import com.pff.source.PSTSource;
 
 /**
  * Specific functions for the 7c table type ("Table Context").
  * This is used for attachments.
  * @author Richard Johnson
  */
-class PSTTable7C extends PSTTable {
+public class PSTTable7C extends PSTTable {
 
 	private final int BLOCK_SIZE = 8176;
 
@@ -56,12 +63,12 @@ class PSTTable7C extends PSTTable {
 	private int TCI_1b = 0;
 	private int overrideCol = -1;
 	
-	protected PSTTable7C(PSTNodeInputStream in, HashMap subNodeDescriptorItems)
+	public PSTTable7C(PSTNodeInputStream in, HashMap subNodeDescriptorItems)
 		throws PSTException, java.io.IOException
 	{
 		this(in, subNodeDescriptorItems, -1);
 	}
-	protected PSTTable7C(PSTNodeInputStream in, HashMap subNodeDescriptorItems, int entityToExtract)
+	public PSTTable7C(PSTNodeInputStream in, HashMap subNodeDescriptorItems, int entityToExtract)
 		throws PSTException, java.io.IOException
 	{
 		super(in, subNodeDescriptorItems);
@@ -154,16 +161,15 @@ protected PSTTable7C(PSTNodeInputStream in, HashMap
 	 * get all the items parsed out of this table.
 	 * @return
 	 */
-	List> getItems()
-			throws PSTException, IOException
-	{
+	public List> getItems() throws PSTException, IOException {
 		if ( items == null ) {
 			items = getItems(-1, -1);
 		}
 		return items;
 	}
+	
 
-	List> getItems(int startAtRecord, int numberOfRecordsToReturn)
+	public List> getItems(int startAtRecord, int numberOfRecordsToReturn)
 			throws PSTException, IOException
 	{
 		List> itemList = new ArrayList>();
@@ -194,7 +200,7 @@ List> getItems(int startAtRecord, int numberOfR
 		{
 			HashMap currentItem = new HashMap();
 			// add on some padding for block boundries?
-			if (rowNodeInfo.in.getPSTFile().getPSTFileType() == PSTFile.PST_TYPE_ANSI) {
+			if (rowNodeInfo.in.getPSTFile().getPSTFileType() == PSTSource.PST_TYPE_ANSI) {
 				if (currentValueArrayStart >= BLOCK_SIZE) {
 					currentValueArrayStart = currentValueArrayStart + (4) * (currentValueArrayStart / BLOCK_SIZE);
 				}
diff --git a/com/pff/PSTTable7CItem.java b/com/pff/parsing/tables/PSTTable7CItem.java
similarity index 94%
rename from com/pff/PSTTable7CItem.java
rename to com/pff/parsing/tables/PSTTable7CItem.java
index 16b9877..0d0fec3 100644
--- a/com/pff/PSTTable7CItem.java
+++ b/com/pff/parsing/tables/PSTTable7CItem.java
@@ -31,13 +31,13 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.parsing.tables;
 
 /**
  * Items found in the 7c tables
  * @author Richard Johnson
  */
-class PSTTable7CItem extends PSTTableItem
+public class PSTTable7CItem extends PSTTableItem
 {
 	
 	public String toString() {
diff --git a/com/pff/PSTTableBC.java b/com/pff/parsing/tables/PSTTableBC.java
similarity index 90%
rename from com/pff/PSTTableBC.java
rename to com/pff/parsing/tables/PSTTableBC.java
index f681248..22a56aa 100644
--- a/com/pff/PSTTableBC.java
+++ b/com/pff/parsing/tables/PSTTableBC.java
@@ -31,26 +31,29 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.parsing.tables;
 
 import java.util.HashMap;
 
+import com.pff.PSTUtils;
+import com.pff.exceptions.PSTException;
+import com.pff.parsing.PSTDescriptorItem;
+import com.pff.parsing.PSTNodeInputStream;
+
 
 /**
  * The BC Table type. (Property Context)
  * Used by pretty much everything.
  * @author Richard Johnson
  */
-class PSTTableBC extends PSTTable {
+public class PSTTableBC extends PSTTable {
 	
 	private HashMap items = new HashMap();
 
 	private StringBuilder descBuffer = new StringBuilder();
 	private boolean isDescNotYetInitiated = false;
 
-	PSTTableBC(PSTNodeInputStream in)
-		throws PSTException, java.io.IOException
-	{
+	public PSTTableBC(PSTNodeInputStream in) throws PSTException, java.io.IOException {
 		super(in, new HashMap());
 		//data = null;	// No direct access to data!
 		
@@ -81,11 +84,11 @@ class PSTTableBC extends PSTTable {
 			
 			PSTTableBCItem item = new PSTTableBCItem();
 			item.itemIndex = x;
-			item.entryType =(int)PSTObject.convertLittleEndianBytesToLong(keyTableInfo, offset+0, offset+2);
+			item.entryType =(int)PSTUtils.convertLittleEndianBytesToLong(keyTableInfo, offset+0, offset+2);
 			//item.entryType =(int)in.seekAndReadLong(offset, 2);
-			item.entryValueType = (int)PSTObject.convertLittleEndianBytesToLong(keyTableInfo, offset+2, offset+4);
+			item.entryValueType = (int)PSTUtils.convertLittleEndianBytesToLong(keyTableInfo, offset+2, offset+4);
 			//item.entryValueType = (int)in.seekAndReadLong(offset+2, 2);
-			item.entryValueReference = (int)PSTObject.convertLittleEndianBytesToLong(keyTableInfo, offset+4, offset+8);
+			item.entryValueReference = (int)PSTUtils.convertLittleEndianBytesToLong(keyTableInfo, offset+4, offset+8);
 			//item.entryValueReference = (int)in.seekAndReadLong(offset+4, 4);
 
 			// Data is in entryValueReference for all types <= 4 bytes long
diff --git a/com/pff/PSTTableBCItem.java b/com/pff/parsing/tables/PSTTableBCItem.java
similarity index 94%
rename from com/pff/PSTTableBCItem.java
rename to com/pff/parsing/tables/PSTTableBCItem.java
index 4f7b330..bf33aa0 100644
--- a/com/pff/PSTTableBCItem.java
+++ b/com/pff/parsing/tables/PSTTableBCItem.java
@@ -31,13 +31,13 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.parsing.tables;
 
 /**
  * Items within the BC Table
  * @author Richard Johnson
  */
-class PSTTableBCItem extends PSTTableItem
+public class PSTTableBCItem extends PSTTableItem
 {
 
 	public String toString() {
diff --git a/com/pff/PSTTableItem.java b/com/pff/parsing/tables/PSTTableItem.java
similarity index 91%
rename from com/pff/PSTTableItem.java
rename to com/pff/parsing/tables/PSTTableItem.java
index fe5fd1a..f660413 100644
--- a/com/pff/PSTTableItem.java
+++ b/com/pff/parsing/tables/PSTTableItem.java
@@ -31,7 +31,7 @@
  * along with java-libpst.  If not, see .
  *
  */
-package com.pff;
+package com.pff.parsing.tables;
 
 import java.io.UnsupportedEncodingException;
 import java.nio.charset.Charset;
@@ -39,12 +39,15 @@
 import java.util.Date;
 import java.util.SimpleTimeZone;
 
+import com.pff.PSTUtils;
+import com.pff.source.PSTSource;
+
 /**
  * Generic table item.
  * Provides some basic string functions
  * @author Richard Johnson
  */
-class PSTTableItem {
+public class PSTTableItem {
 
 	public static final int VALUE_TYPE_PT_UNICODE = 0x1f;
 	public static final int VALUE_TYPE_PT_STRING8 = 0x1e;
@@ -59,7 +62,7 @@ class PSTTableItem {
 	
 	public long getLongValue() {
 		if ( this.data.length > 0 ) {
-			return PSTObject.convertLittleEndianBytesToLong(data);
+			return PSTUtils.convertLittleEndianBytesToLong(data);
 		}
 		return -1;
 	}
@@ -134,7 +137,7 @@ public String getStringValue(int stringType) {
 	}
 
 	public String toString() {
-		String ret = PSTFile.getPropertyDescription(entryType, entryValueType);
+		String ret = PSTSource.getPropertyDescription(entryType, entryValueType);
 
 		if ( entryValueType == 0x000B )
 		{
@@ -153,7 +156,7 @@ public String toString() {
 				return ret + "no data";
 			}
 			if ( data.length == 8 ) {
-				long l = PSTObject.convertLittleEndianBytesToLong(data, 0, 8);
+				long l = PSTUtils.convertLittleEndianBytesToLong(data, 0, 8);
 				return String.format("%s0x%016X (%d)", ret, l, l);
 			} else {
 				return String.format("%s invalid data length: %d", ret, data.length);
@@ -162,10 +165,10 @@ public String toString() {
 		
 		if ( entryValueType == 0x0040 ) {
 			// It's a date...
-			int high = (int)PSTObject.convertLittleEndianBytesToLong(data, 4, 8);
-			int low = (int)PSTObject.convertLittleEndianBytesToLong(data, 0, 4);
+			int high = (int)PSTUtils.convertLittleEndianBytesToLong(data, 4, 8);
+			int low = (int)PSTUtils.convertLittleEndianBytesToLong(data, 0, 4);
 			 
-			Date d = PSTObject.filetimeToDate(high, low);
+			Date d = PSTUtils.filetimeToDate(high, low);
 			dateFormatter.setTimeZone(utcTimeZone);
 			return ret + dateFormatter.format(d);
 		}
diff --git a/com/pff/source/PSTRandomAccessFile.java b/com/pff/source/PSTRandomAccessFile.java
new file mode 100644
index 0000000..fe4cc9f
--- /dev/null
+++ b/com/pff/source/PSTRandomAccessFile.java
@@ -0,0 +1,57 @@
+package com.pff.source;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+public class PSTRandomAccessFile implements _RandomAccessPSTSource {
+
+	RandomAccessFile raf;
+	
+	public PSTRandomAccessFile(File f) throws FileNotFoundException {
+		this.raf = new RandomAccessFile(f, "r");
+	}
+
+	
+	public PSTRandomAccessFile(String filename) throws FileNotFoundException {
+		this.raf = new RandomAccessFile(filename, "r");
+	}
+
+
+	
+	@Override
+	public void close() throws IOException {
+		this.raf.close();		
+	}
+
+
+	@Override
+	public void seek(long pos) throws IOException {
+		this.raf.seek(pos);
+	}
+
+
+	@Override
+	public int read() throws IOException {
+		return this.raf.read();
+	}
+
+
+	@Override
+	public int read(byte[] buffer, int offset, int length) throws IOException {
+		return this.raf.read(buffer, offset, length);
+	}
+
+
+	@Override
+	public int read(byte[] b) throws IOException {
+		return this.raf.read(b);
+	}
+
+
+	@Override
+	public long position() throws IOException {
+		return this.raf.getFilePointer();
+	}
+}
diff --git a/com/pff/source/PSTSource.java b/com/pff/source/PSTSource.java
new file mode 100644
index 0000000..94b28a1
--- /dev/null
+++ b/com/pff/source/PSTSource.java
@@ -0,0 +1,918 @@
+/**
+ * Copyright 2010 Richard Johnson & Orin Eman
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ---
+ *
+ * This file is part of java-libpst.
+ *
+ * java-libpst is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * java-libpst 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with java-libpst.  If not, see .
+ *
+ */
+package com.pff.source;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.Properties;
+import java.util.UUID;
+
+import com.pff.PSTUtils;
+import com.pff.exceptions.PSTException;
+import com.pff.objects.PSTFolder;
+import com.pff.objects.PSTMessageStore;
+import com.pff.parsing.DescriptorIndexNode;
+import com.pff.parsing.OffsetIndexItem;
+import com.pff.parsing.PSTDescriptorItem;
+import com.pff.parsing.PSTNodeInputStream;
+import com.pff.parsing.tables.PSTTableBC;
+import com.pff.parsing.tables.PSTTableBCItem;
+import com.pff.parsing.tables.PSTTableItem;
+
+/**
+ * PSTFile is the containing class that allows you to access items within a .pst file.
+ * Start here, get the root of the folders and work your way down through your items.
+ * @author Richard Johnson
+ */
+public class PSTSource {
+
+	public static final int ENCRYPTION_TYPE_NONE = 0;
+	public static final int ENCRYPTION_TYPE_COMPRESSIBLE = 1;
+
+	private static final int MESSAGE_STORE_DESCRIPTOR_IDENTIFIER = 33;
+	private static final int ROOT_FOLDER_DESCRIPTOR_IDENTIFIER = 290;
+
+	public static final int PST_TYPE_ANSI = 14;
+	protected static final int PST_TYPE_ANSI_2 = 15;
+	public static final int PST_TYPE_UNICODE = 23;
+	
+	// Known GUIDs
+	// Local IDs first
+	public static final int PS_PUBLIC_STRINGS = 0;
+	public static final int PSETID_Common = 1;
+	public static final int PSETID_Address = 2;
+	public static final int PS_INTERNET_HEADERS = 3;
+	public static final int PSETID_Appointment = 4;
+	public static final int PSETID_Meeting = 5;
+	public static final int PSETID_Log = 6;
+	public static final int PSETID_Messaging = 7;
+	public static final int PSETID_Note = 8;
+	public static final int PSETID_PostRss = 9;
+	public static final int PSETID_Task = 10;
+	public static final int PSETID_UnifiedMessaging = 11;
+	public static final int PS_MAPI = 12;
+	public static final int PSETID_AirSync = 13;
+	public static final int PSETID_Sharing = 14;
+
+	// Now the string guids
+	private static final String guidStrings[] =
+		{ "00020329-0000-0000-C000-000000000046",
+		  "00062008-0000-0000-C000-000000000046",
+		  "00062004-0000-0000-C000-000000000046",
+		  "00020386-0000-0000-C000-000000000046",
+		  "00062002-0000-0000-C000-000000000046",
+		  "6ED8DA90-450B-101B-98DA-00AA003F1305",
+		  "0006200A-0000-0000-C000-000000000046",
+		  "41F28F13-83F4-4114-A584-EEDB5A6B0BFF",
+		  "0006200E-0000-0000-C000-000000000046",
+		  "00062041-0000-0000-C000-000000000046",
+		  "00062003-0000-0000-C000-000000000046",
+		  "4442858E-A9E3-4E80-B900-317A210CC15B",
+		  "00020328-0000-0000-C000-000000000046",
+		  "71035549-0739-4DCB-9163-00F0580DBBDF",
+		  "00062040-0000-0000-C000-000000000046" };
+	
+	private HashMap guidMap = new HashMap();
+	
+	// the type of encryption the files uses.
+	private int encryptionType = 0;
+	
+	// our all important tree.
+	private LinkedHashMap> childrenDescriptorTree = null;
+	
+	private HashMap nameToId = new HashMap();
+	private HashMap stringToId = new HashMap();
+	private static HashMap idToName = new HashMap();
+	private HashMap idToString = new HashMap();
+	private byte[] guids = null;
+	
+	_RandomAccessPSTSource in;	
+
+	int itemCount=0;
+	
+	/**
+	 * constructor
+	 * @param fileName
+	 * @throws FileNotFoundException
+	 * @throws PSTException
+	 * @throws IOException
+	 */
+	public PSTSource(_RandomAccessPSTSource in)
+		throws FileNotFoundException, PSTException, IOException
+	{
+		// attempt to open the file.
+		this.in = in;
+		
+		// get the first 4 bytes, should be !BDN
+		try {
+			byte[] temp = new byte[4];
+			in.read(temp);
+			String strValue = new String(temp);
+			if (!strValue.equals("!BDN")) {
+				throw new PSTException("Invalid file header: "+strValue+", expected: !BDN"); 
+			}
+			
+			// make sure we are using a supported version of a PST...
+			byte[] fileTypeBytes = new byte[2];
+			in.seek(10); //seekStream(in, 10);	//
+			in.read(fileTypeBytes);
+			// ANSI file types can be 14 or 15:
+			if (fileTypeBytes[0] == PSTSource.PST_TYPE_ANSI_2) {
+				fileTypeBytes[0] = PSTSource.PST_TYPE_ANSI;
+			}
+			if (fileTypeBytes[0] != PSTSource.PST_TYPE_ANSI &&
+				fileTypeBytes[0] != PSTSource.PST_TYPE_UNICODE)
+			{
+				throw new PSTException("Unrecognised PST File version: "+fileTypeBytes[0]);
+			}
+			this.pstFileType = fileTypeBytes[0];
+			
+			// make sure encryption is turned off at this stage...
+			if (this.getPSTFileType() == PST_TYPE_ANSI) {
+				in.seek(461);
+			} else {
+				in.seek(513);
+			}
+			encryptionType = in.read();
+			if (encryptionType == 0x02) {
+				throw new PSTException("Only unencrypted and compressable PST files are supported at this time"); 
+			}
+			
+			// build out name to id map.
+			processNameToIdMap(in);
+			
+		}  catch (IOException err) {
+			throw new PSTException("Unable to read PST Sig", err);
+		}
+
+	}
+
+	private int pstFileType = 0;
+	public int getPSTFileType() {
+		return pstFileType;
+	}
+	
+	/**
+	 * read the name-to-id map from the file and load it in
+	 * @param in
+	 * @throws IOException
+	 * @throws PSTException
+	 */
+	private void processNameToIdMap(_RandomAccessPSTSource in)
+		throws IOException, PSTException
+	{
+
+		// Create our guid map
+		for ( int i = 0; i < guidStrings.length; ++i ) {
+			UUID uuid = UUID.fromString(guidStrings[i]);
+			guidMap.put(uuid, i);
+/*
+			System.out.printf("guidMap[{%s}] = %d\n", uuid.toString(), i);
+/**/
+		}
+		
+		// process the name to id map
+		DescriptorIndexNode nameToIdMapDescriptorNode = (getDescriptorIndexNode(97));
+		//nameToIdMapDescriptorNode.readData(this);
+
+		// get the descriptors if we have them
+		HashMap localDescriptorItems = null;
+		if (nameToIdMapDescriptorNode.localDescriptorsOffsetIndexIdentifier != 0) {
+			//PSTDescriptor descriptor = new PSTDescriptor(this, nameToIdMapDescriptorNode.localDescriptorsOffsetIndexIdentifier);
+			//localDescriptorItems = descriptor.getChildren();
+			localDescriptorItems = this.getPSTDescriptorItems(nameToIdMapDescriptorNode.localDescriptorsOffsetIndexIdentifier);
+		}
+
+		// process the map
+		//PSTTableBC bcTable = new PSTTableBC(nameToIdMapDescriptorNode.dataBlock.data, nameToIdMapDescriptorNode.dataBlock.blockOffsets);
+		OffsetIndexItem off = this.getOffsetIndexNode(nameToIdMapDescriptorNode.dataOffsetIndexIdentifier);
+		PSTNodeInputStream nodein = new PSTNodeInputStream(this, off);
+		byte[] tmp = new byte[1024];
+		nodein.read(tmp);
+		PSTTableBC bcTable = new PSTTableBC(nodein);
+
+		HashMap tableItems = (bcTable.getItems());
+		// Get the guids
+		PSTTableBCItem guidEntry = tableItems.get(2);	// PidTagNameidStreamGuid
+		guids = getData(guidEntry, localDescriptorItems);
+		int nGuids = guids.length / 16;
+		UUID[] uuidArray = new UUID[nGuids];
+		int[] uuidIndexes = new int[nGuids];
+		int offset = 0;
+		for ( int i = 0; i < nGuids; ++i ) {
+			long mostSigBits = (PSTUtils.convertLittleEndianBytesToLong(guids, offset, offset+4) << 32) |
+								(PSTUtils.convertLittleEndianBytesToLong(guids, offset+4, offset+6) << 16) |
+								PSTUtils.convertLittleEndianBytesToLong(guids, offset+6, offset+8);
+			long leastSigBits = PSTUtils.convertBigEndianBytesToLong(guids, offset+8, offset+16);
+			uuidArray[i] = new UUID(mostSigBits, leastSigBits);
+			if ( guidMap.containsKey(uuidArray[i]) ) {
+				uuidIndexes[i] = guidMap.get(uuidArray[i]);
+			} else {
+				uuidIndexes[i] = -1;	// We don't know this guid
+			}
+/*
+			System.out.printf("uuidArray[%d] = {%s},%d\n", i, uuidArray[i].toString(), uuidIndexes[i]);
+/**/
+			offset += 16;
+		}
+		
+		// if we have a reference to an internal descriptor
+		PSTTableBCItem mapEntries = tableItems.get(3);	//
+		byte[] nameToIdByte = getData(mapEntries, localDescriptorItems);
+
+		PSTTableBCItem stringMapEntries = tableItems.get(4);	//
+		byte[] stringNameToIdByte = getData(stringMapEntries, localDescriptorItems);
+
+		// process the entries
+		for (int x = 0; x+8 < nameToIdByte.length; x += 8) {
+			int dwPropertyId = (int)PSTUtils.convertLittleEndianBytesToLong(nameToIdByte, x, x+4);
+			int wGuid = (int)PSTUtils.convertLittleEndianBytesToLong(nameToIdByte, x+4, x+6);
+			int wPropIdx = ((int)PSTUtils.convertLittleEndianBytesToLong(nameToIdByte, x+6, x+8));
+
+			if ( (wGuid & 0x0001) == 0 ) {
+				wPropIdx += 0x8000;
+				wGuid >>= 1;
+				int guidIndex;
+				if ( wGuid == 1 ) {
+					guidIndex = PS_MAPI;
+				} else if ( wGuid == 2 ) {
+					guidIndex = PS_PUBLIC_STRINGS;
+				} else {
+					guidIndex = uuidIndexes[wGuid-3];
+				}
+				nameToId.put((long)dwPropertyId | ((long)guidIndex << 32), wPropIdx);
+				idToName.put(wPropIdx, (long)dwPropertyId);
+/*
+				System.out.printf("0x%08X:%04X, 0x%08X\n", dwPropertyId, guidIndex, wPropIdx);
+/**/
+			} else {
+				// else the identifier is a string
+				// dwPropertyId becomes thHke byte offset into the String stream in which the string name of the property is stored.
+				int len = (int)PSTUtils.convertLittleEndianBytesToLong(
+						stringNameToIdByte,
+						dwPropertyId,
+						dwPropertyId+4
+					);
+				byte[] keyByteValue = new byte[len];
+				System.arraycopy(stringNameToIdByte, dwPropertyId+4, keyByteValue, 0, keyByteValue.length);
+				wPropIdx += 0x8000;
+				String key = new String(keyByteValue, "UTF-16LE");
+				stringToId.put(key, wPropIdx);
+				idToString.put(wPropIdx, key);
+				/*
+				if (wPropIdx == 32784) {
+					System.out.println("here!" + dwPropertyId);
+					System.out.println(key);
+					//System.out.println(32784 - 0x8000);
+				}
+				*/
+			}
+		}
+	}
+
+
+	private byte [] getData(PSTTableItem item, HashMap localDescriptorItems)
+		throws IOException, PSTException
+	{
+		if ( item.data.length != 0 ) {
+			return item.data;
+		}
+
+		if ( localDescriptorItems == null ) {
+			throw new PSTException("External reference but no localDescriptorItems in PSTFile.getData()");
+		}
+		
+		if ( item.entryValueType != 0x0102 ) {
+			throw new PSTException("Attempting to get non-binary data in PSTFile.getData()");
+		}
+
+		PSTDescriptorItem mapDescriptorItem = localDescriptorItems.get(item.entryValueReference);
+		if (mapDescriptorItem == null) {
+			throw new PSTException ("not here "+item.entryValueReference + "\n"+localDescriptorItems.keySet());
+		}
+		return mapDescriptorItem.getData();
+	}
+	
+	public int getNameToIdMapItem(int key, int propertySetIndex)
+	{
+		long lKey = ((long)propertySetIndex << 32) | (long)key;
+		Integer i = nameToId.get(lKey);
+		if ( i == null )
+		{
+			return -1;
+		}
+		return i;
+	}
+	public int getPublicStringToIdMapItem(String key)
+	{
+		Integer i = this.stringToId.get(key);
+		if (i == null) {
+			return -1;
+		}
+		return i;
+	}
+
+
+	static long getNameToIdMapKey(int id)
+		//throws PSTException
+	{
+		Long i = idToName.get(id);
+		if ( i == null )
+		{
+			//throw new PSTException("Name to Id mapping not found");
+			return -1;
+		}
+		return i;
+	}
+
+
+
+	static private Properties propertyInternetCodePages = null;
+	static private boolean bCPFirstTime = true;
+	public static String getInternetCodePageCharset(int propertyId) {
+		if ( bCPFirstTime ) {
+			bCPFirstTime = false;
+			propertyInternetCodePages = new Properties();
+			try {
+				InputStream propertyStream = PSTSource.class.getResourceAsStream("/InternetCodepages.txt");
+				if ( propertyStream != null ) {
+					propertyInternetCodePages.load(propertyStream);
+				} else {
+					propertyInternetCodePages = null;
+				}
+			} catch (FileNotFoundException e) {
+				propertyInternetCodePages = null;
+				e.printStackTrace();
+			} catch (IOException e) {
+				propertyInternetCodePages = null;
+				e.printStackTrace();
+			}
+		}
+		if ( propertyInternetCodePages != null ) {
+			return propertyInternetCodePages.getProperty(propertyId+"");
+		}
+		return null;
+	}
+
+
+	static private Properties propertyNames = null;
+	static private boolean bFirstTime = true;
+	
+	static String getPropertyName(int propertyId, boolean bNamed) {
+		if ( bFirstTime ) {
+			bFirstTime = false;
+			propertyNames = new Properties();
+			try {
+				InputStream propertyStream = PSTSource.class.getResourceAsStream("/PropertyNames.txt");
+				if ( propertyStream != null ) {
+					propertyNames.load(propertyStream);
+				} else {
+					propertyNames = null;
+				}
+			} catch (FileNotFoundException e) {
+				propertyNames = null;
+				e.printStackTrace();
+			} catch (IOException e) {
+				propertyNames = null;
+				e.printStackTrace();
+			}
+		}
+
+		if ( propertyNames != null ) {
+			String key = String.format((bNamed ? "%08X" : "%04X"), propertyId);
+			return propertyNames.getProperty(key);
+		}
+
+		return null;
+	}
+
+	
+	
+	public static String getPropertyDescription(int entryType, int entryValueType) {
+		String ret = "";
+		if ( entryType < 0x8000 ) {
+			String name = PSTSource.getPropertyName(entryType, false);
+			if ( name != null ) {
+				ret = String.format("%s:%04X: ", name, entryValueType);
+			} else {
+				ret = String.format("0x%04X:%04X: ", entryType, entryValueType);
+			}
+		} else {
+			long type = PSTSource.getNameToIdMapKey(entryType);
+			if ( type == -1 ) {
+				ret = String.format("0xFFFF(%04X):%04X: ", entryType, entryValueType);
+			} else {
+				String name = PSTSource.getPropertyName((int)type, true);
+				if ( name != null ) {
+					ret = String.format("%s(%04X):%04X: ", name, entryType, entryValueType);
+				} else {
+					ret = String.format("0x%04X(%04X):%04X: ", type, entryType, entryValueType);
+				}
+			}
+		}
+		return ret;
+	}
+
+	/**
+	 * destructor just closes the file handle...
+	 */
+	@Override
+	protected void finalize() throws IOException {
+		in.close();
+	}
+	
+	/**
+	 * get the type of encryption the file uses
+	 * @return encryption type used in the PST File
+	 */
+	public int getEncryptionType() {
+		return this.encryptionType;
+	}
+	
+	/**
+	 * get the handle to the file we are currently accessing
+	 */
+	/*public RandomAccessFile getFileHandle() {
+		return this.in;
+	}*/
+	
+	
+	/**
+	 * get the message store of the PST file.
+	 * Note that this doesn't really have much information, better to look under the root folder
+	 * @throws PSTException
+	 * @throws IOException
+	 */
+	public PSTMessageStore getMessageStore() throws PSTException, IOException {
+		DescriptorIndexNode messageStoreDescriptor = getDescriptorIndexNode(MESSAGE_STORE_DESCRIPTOR_IDENTIFIER);
+		return new PSTMessageStore(this, messageStoreDescriptor);
+	}
+
+	/**
+	 * get the root folder for the PST file.
+	 * You should find all of your data under here...
+	 * @throws PSTException
+	 * @throws IOException
+	 */
+	public PSTFolder getRootFolder() throws PSTException, IOException {
+		DescriptorIndexNode rootFolderDescriptor = getDescriptorIndexNode(ROOT_FOLDER_DESCRIPTOR_IDENTIFIER);
+		PSTFolder output = new PSTFolder(this, rootFolderDescriptor);
+		return output;
+	}
+	
+
+	
+	public PSTNodeInputStream readLeaf(long bid) throws IOException, PSTException {
+		//PSTFileBlock ret = null;
+		//PSTNodeInputStream ret = null;
+
+		// get the index node for the descriptor index
+		OffsetIndexItem offsetItem = getOffsetIndexNode(bid);
+		return new PSTNodeInputStream(this, offsetItem);
+
+	}
+	
+	
+	public int getLeafSize(long bid) throws IOException, PSTException {
+		OffsetIndexItem offsetItem = getOffsetIndexNode(bid);
+
+		// Internal block?
+		if ( (offsetItem.getIndexIdentifier() & 0x02) == 0 ) {
+			// No, return the raw size
+			return offsetItem.getSize();
+		}
+	
+		// we only need the first 8 bytes
+		byte[] data = new byte[8];
+		in.seek(offsetItem.getFileOffset());
+		in.read(data);
+	
+		// we are an array, get the sum of the sizes...
+		return (int)PSTUtils.convertLittleEndianBytesToLong(data, 4, 8);
+	}
+
+	/**
+	 * Read a file offset from the file
+	 * PST Files have this tendency to store file offsets (pointers) in 8 little endian bytes.
+	 * Convert this to a long for seeking to.
+	 * @param in handle for PST file
+	 * @param startOffset where to read the 8 bytes from
+	 * @return long representing the read location
+	 * @throws IOException
+	 */
+	protected long extractLEFileOffset(long startOffset)
+		throws IOException
+	{
+		long offset = 0;
+		if (this.getPSTFileType() == PSTSource.PST_TYPE_ANSI) {
+			in.seek(startOffset);
+			byte[] temp = new byte[4];
+			in.read(temp);
+			offset |= temp[3] & 0xff;
+			offset <<= 8;
+			offset |= temp[2] & 0xff;
+			offset <<= 8;
+			offset |= temp[1] & 0xff;
+			offset <<= 8;
+			offset |= temp[0] & 0xff;
+		} else {
+			in.seek(startOffset);
+			byte[] temp = new byte[8];
+			in.read(temp);
+			offset = temp[7] & 0xff;
+			long tmpLongValue;
+			for (int x = 6; x >= 0; x--) {
+				offset = offset << 8;
+				tmpLongValue = (long)temp[x] & 0xff;
+				offset |= tmpLongValue;
+			}
+		}
+
+		return offset;
+	}
+
+	/**
+	 * Generic function used by getOffsetIndexNode and getDescriptorIndexNode for navigating the PST B-Trees
+	 * @param in
+	 * @param index
+	 * @param descTree
+	 * @return
+	 * @throws IOException
+	 * @throws PSTException
+	 */
+	private byte[] findBtreeItem(_RandomAccessPSTSource in, long index, boolean descTree)
+		throws IOException, PSTException
+	{
+
+		long btreeStartOffset;
+		// first find the starting point for the offset index
+		if (this.getPSTFileType() == PST_TYPE_ANSI) {
+			btreeStartOffset = this.extractLEFileOffset(196);
+			if (descTree) {
+				btreeStartOffset = this.extractLEFileOffset(188);
+			}
+		} else {
+			btreeStartOffset = this.extractLEFileOffset(240);
+			if (descTree) {
+				btreeStartOffset = this.extractLEFileOffset(224);
+			}
+		}
+
+		// okay, what we want to do is navigate the tree until you reach the bottom....
+		// try and read the index b-tree
+		byte[] temp = new byte[2];
+		if (this.getPSTFileType() == PST_TYPE_ANSI) {
+			in.seek(btreeStartOffset+500);
+		} else {
+			in.seek(btreeStartOffset+496);
+		}
+		in.read(temp);
+		while	((temp[0] == 0xffffff80 && temp[1] == 0xffffff80 && !descTree) ||
+				 (temp[0] == 0xffffff81 && temp[1] == 0xffffff81 && descTree))
+		{
+
+			// get the rest of the data....
+			byte[] branchNodeItems;
+			if (this.getPSTFileType() == PST_TYPE_ANSI) {
+				branchNodeItems = new byte[496];
+			} else {
+				branchNodeItems = new byte[488];
+			}
+			in.seek(btreeStartOffset);
+			in.read(branchNodeItems);
+
+			int numberOfItems = in.read();
+			in.read(); // maxNumberOfItems
+			in.read(); // itemSize
+			int levelsToLeaf = in.read();
+
+			if (levelsToLeaf > 0) {
+				boolean found = false;
+				for (int x = 0; x < numberOfItems; x++) {
+					if (this.getPSTFileType() == PST_TYPE_ANSI) {
+						long indexIdOfFirstChildNode = extractLEFileOffset(btreeStartOffset + (x * 12));
+						if (indexIdOfFirstChildNode > index) {
+							// get the address for the child first node in this group
+							btreeStartOffset = extractLEFileOffset(btreeStartOffset+((x-1) * 12)+8);
+							in.seek(btreeStartOffset+500);
+							in.read(temp);
+							found = true;
+							break;
+						}
+					} else {
+						long indexIdOfFirstChildNode = extractLEFileOffset(btreeStartOffset + (x * 24));
+						if (indexIdOfFirstChildNode > index) {
+							// get the address for the child first node in this group
+							btreeStartOffset = extractLEFileOffset(btreeStartOffset+((x-1) * 24)+16);
+							in.seek(btreeStartOffset+496);
+							in.read(temp);
+							found = true;
+							break;
+						}
+					}
+				}
+				if (!found) {
+					// it must be in the very last branch...
+					if (this.getPSTFileType() == PST_TYPE_ANSI) {
+						btreeStartOffset = extractLEFileOffset(btreeStartOffset+((numberOfItems-1) * 12)+8);
+						in.seek(btreeStartOffset+500);
+						in.read(temp);
+					} else {
+						btreeStartOffset = extractLEFileOffset(btreeStartOffset+((numberOfItems-1) * 24)+16);
+						in.seek(btreeStartOffset+496);
+						in.read(temp);
+					}
+				}
+			}
+			else
+			{
+				// we are at the bottom of the tree...
+				// we want to get our file offset!
+				for (int x = 0; x < numberOfItems; x++) {
+
+					if (this.getPSTFileType() == PSTSource.PST_TYPE_ANSI) {
+						if (descTree)
+						{
+							// The 32-bit descriptor index b-tree leaf node item
+							in.seek(btreeStartOffset + (x * 16));
+							temp = new byte[4];
+							in.read(temp);
+							if (PSTUtils.convertLittleEndianBytesToLong(temp) == index) {
+								// give me the offset index please!
+								in.seek(btreeStartOffset + (x * 16));
+								temp = new byte[16];
+								in.read(temp);
+								return temp;
+							}
+						}
+						else
+						{
+							// The 32-bit (file) offset index item
+							long indexIdOfFirstChildNode = extractLEFileOffset(btreeStartOffset + (x * 12));
+
+							if (indexIdOfFirstChildNode == index) {
+								// we found it!!!! OMG
+								//System.out.println("item found as item #"+x);
+								in.seek(btreeStartOffset + (x * 12));
+
+								temp = new byte[12];
+								in.read(temp);
+								return temp;
+							}
+						}
+					} else {
+						if (descTree)
+						{
+							// The 64-bit descriptor index b-tree leaf node item
+							in.seek(btreeStartOffset + (x * 32));
+
+							temp = new byte[4];
+							in.read(temp);
+							if (PSTUtils.convertLittleEndianBytesToLong(temp) == index) {
+								// give me the offset index please!
+								in.seek(btreeStartOffset + (x * 32));
+								temp = new byte[32];
+								in.read(temp);
+								return temp;
+							}
+						}
+						else
+						{
+							// The 64-bit (file) offset index item
+							long indexIdOfFirstChildNode = extractLEFileOffset(btreeStartOffset + (x * 24));
+
+							if (indexIdOfFirstChildNode == index) {
+								// we found it!!!! OMG
+								//System.out.println("item found as item #"+x);
+								in.seek(btreeStartOffset + (x * 24));
+
+								temp = new byte[24];
+								in.read(temp);
+								return temp;
+							}
+						}
+					}
+				}
+				throw new PSTException("Unable to find "+index);
+			}
+		}
+
+		throw new PSTException("Unable to find node: "+index);
+	}
+
+	/**
+	 * navigate the internal descriptor B-Tree and find a specific item
+	 * @param in
+	 * @param identifier
+	 * @return the descriptor node for the item
+	 * @throws IOException
+	 * @throws PSTException
+	 */
+	public DescriptorIndexNode getDescriptorIndexNode(long identifier)
+		throws IOException, PSTException
+	{
+		return new DescriptorIndexNode(findBtreeItem(in, identifier, true), this.getPSTFileType());
+	}
+
+	/**
+	 * navigate the internal index B-Tree and find a specific item
+	 * @param in
+	 * @param identifier
+	 * @return the offset index item
+	 * @throws IOException
+	 * @throws PSTException
+	 */
+	public OffsetIndexItem getOffsetIndexNode(long identifier) throws IOException, PSTException	{
+		return new OffsetIndexItem(findBtreeItem(in, identifier, false), this.getPSTFileType());
+	}
+
+
+	/**
+	 * parse a PSTDescriptor and get all of its items
+	 */
+	public HashMap getPSTDescriptorItems(long localDescriptorsOffsetIndexIdentifier)
+		throws PSTException, IOException
+	{
+		return this.getPSTDescriptorItems(this.readLeaf(localDescriptorsOffsetIndexIdentifier));
+	}
+	HashMap getPSTDescriptorItems(PSTNodeInputStream in)
+		throws PSTException, IOException
+	{
+		// make sure the signature is correct
+		in.seek(0);
+		int sig = in.read();
+		if (sig != 0x2) {
+			throw new PSTException("Unable to process descriptor node, bad signature: "+sig);
+		}
+
+		HashMap output = new HashMap();
+		int numberOfItems = (int)in.seekAndReadLong(2, 2);
+		int offset;
+		if (this.getPSTFileType() == PSTSource.PST_TYPE_ANSI) {
+			offset = 4;
+		} else {
+			offset = 8;
+		}
+
+		byte[] data = new byte[(int)in.length()];
+		in.seek(0);
+		in.read(data);
+
+		for (int x = 0; x < numberOfItems; x++) {
+			PSTDescriptorItem item = new PSTDescriptorItem(data, offset, this);
+			output.put(item.getDescriptorIdentifier(), item);
+			if (this.getPSTFileType() == PSTSource.PST_TYPE_ANSI) {
+				offset += 12;
+			} else {
+				offset += 24;
+			}
+		}
+
+		return output;
+	}
+
+	/**
+	 * Build the children descriptor tree
+	 * This goes through the entire descriptor B-Tree and adds every item to the childrenDescriptorTree.
+	 * This is used as fallback when the nodes that list file contents are broken.
+	 * @param in
+	 * @throws IOException
+	 * @throws PSTException
+	 */
+	public LinkedHashMap> getChildDescriptorTree()
+			throws IOException, PSTException
+	{
+		if (this.childrenDescriptorTree == null) {
+			long btreeStartOffset = 0;
+			if (this.getPSTFileType() == PST_TYPE_ANSI) {
+				btreeStartOffset = this.extractLEFileOffset(188);
+			} else {
+				btreeStartOffset = this.extractLEFileOffset(224);
+			}
+			this.childrenDescriptorTree = new LinkedHashMap>();
+			processDescriptorBTree(btreeStartOffset);
+		}
+		return this.childrenDescriptorTree;
+	}
+
+	/**
+	 * Recursive function for building the descriptor tree, used by buildDescriptorTree
+	 * @param in
+	 * @param btreeStartOffset
+	 * @throws IOException
+	 * @throws PSTException
+	 */
+	private void processDescriptorBTree(long btreeStartOffset)
+			throws IOException, PSTException
+	{
+		byte[] temp = new byte[2];
+		if (this.getPSTFileType() == PST_TYPE_ANSI) {
+			in.seek(btreeStartOffset+500);
+		} else {
+			in.seek(btreeStartOffset+496);
+		}
+		in.read(temp);
+
+		if ((temp[0] == 0xffffff81 && temp[1] == 0xffffff81)) {
+
+			if (this.getPSTFileType() == PST_TYPE_ANSI) {
+				in.seek(btreeStartOffset+496);
+			} else {
+				in.seek(btreeStartOffset+488);
+			}
+
+			int numberOfItems = in.read();
+			in.read(); // maxNumberOfItems
+			in.read(); // itemSize
+			int levelsToLeaf = in.read();
+
+			if (levelsToLeaf > 0) {
+				for (int x = 0; x < numberOfItems; x++) {
+					if (this.getPSTFileType() == PST_TYPE_ANSI) {
+						long branchNodeItemStartIndex = (btreeStartOffset + (12*x));
+						long nextLevelStartsAt =  this.extractLEFileOffset(branchNodeItemStartIndex+8);
+						processDescriptorBTree(nextLevelStartsAt);
+					} else {
+						long branchNodeItemStartIndex = (btreeStartOffset + (24*x));
+						long nextLevelStartsAt =  this.extractLEFileOffset(branchNodeItemStartIndex+16);
+						processDescriptorBTree(nextLevelStartsAt);
+					}
+				}
+			} else {
+				for (int x = 0; x < numberOfItems; x++) {
+					// The 64-bit descriptor index b-tree leaf node item give me the offset index please!
+					if (this.getPSTFileType() == PSTSource.PST_TYPE_ANSI) {
+						in.seek(btreeStartOffset + (x * 16));
+						temp = new byte[16];
+						in.read(temp);
+					} else {
+						in.seek(btreeStartOffset + (x * 32));
+						temp = new byte[32];
+						in.read(temp);
+					}
+
+					DescriptorIndexNode tempNode = new DescriptorIndexNode(temp, this.getPSTFileType());
+
+					// we don't want to be children of ourselves...
+					if (tempNode.parentDescriptorIndexIdentifier == tempNode.descriptorIdentifier) {
+						// skip!
+					} else if (childrenDescriptorTree.containsKey(tempNode.parentDescriptorIndexIdentifier)) {
+						// add this entry to the existing list of children
+						LinkedList children = childrenDescriptorTree.get(tempNode.parentDescriptorIndexIdentifier);
+						children.add(tempNode);
+					} else {
+						// create a new entry and add this one to that
+						LinkedList children = new LinkedList();
+						children.add(tempNode);
+						childrenDescriptorTree.put(tempNode.parentDescriptorIndexIdentifier, children);
+					}
+					this.itemCount++;
+				}
+			}
+		} else {
+			PSTUtils.printHexFormatted(temp, true);
+			throw new PSTException("Unable to read descriptor node, is not a descriptor");
+		}
+	}
+	
+	
+	public _RandomAccessPSTSource getRASource() {
+		return this.in;
+	}
+
+
+}
diff --git a/com/pff/source/_RandomAccessPSTSource.java b/com/pff/source/_RandomAccessPSTSource.java
new file mode 100644
index 0000000..8fa11d1
--- /dev/null
+++ b/com/pff/source/_RandomAccessPSTSource.java
@@ -0,0 +1,22 @@
+package com.pff.source;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+public interface _RandomAccessPSTSource extends Closeable {
+
+	
+	public void seek(long pos) throws IOException;
+	
+	public int read() throws IOException;
+	
+	public int read(byte[] buffer, int offset, int length) throws IOException;
+	
+	public int read( byte[] b ) throws IOException;
+	
+	public long position() throws IOException;
+
+	
+	
+	
+}
diff --git a/deprecated/LZFu.java b/deprecated/LZFu.java
new file mode 100644
index 0000000..48f387e
--- /dev/null
+++ b/deprecated/LZFu.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2010 Richard Johnson & Orin Eman
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ---
+ *
+ * This file is part of java-libpst.
+ *
+ * java-libpst is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * java-libpst 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with java-libpst.  If not, see .
+ *
+ */
+
+package com.pff;
+
+import java.io.UnsupportedEncodingException;
+
+import com.pff.exceptions.PSTException;
+
+/**
+ * An implementation of the LZFu algorithm to decompress RTF content
+ * @author Richard Johnson
+ */
+public class LZFu {
+
+	
+	
+}
diff --git a/com/pff/PSTFile.java b/deprecated/PSTFile.java
similarity index 93%
rename from com/pff/PSTFile.java
rename to deprecated/PSTFile.java
index 64e396e..596e5f6 100644
--- a/com/pff/PSTFile.java
+++ b/deprecated/PSTFile.java
@@ -135,7 +135,8 @@ public PSTFile(File fileName)
 			
 			// make sure we are using a supported version of a PST...
 			byte[] fileTypeBytes = new byte[2];
-			in.seek(10);
+			//in.seek(10);
+			seek(10);
 			in.read(fileTypeBytes);
 			// ANSI file types can be 14 or 15:
 			if (fileTypeBytes[0] == PSTFile.PST_TYPE_ANSI_2) {
@@ -150,9 +151,11 @@ public PSTFile(File fileName)
 			
 			// make sure encryption is turned off at this stage...
 			if (this.getPSTFileType() == PST_TYPE_ANSI) {
-				in.seek(461);
+				//in.seek(461);
+				seek(461);
 			} else {
-				in.seek(513);
+				//in.seek(513);
+				seek(513);
 			}
 			encryptionType = in.readByte();
 			if (encryptionType == 0x02) {
@@ -407,6 +410,7 @@ static String getPropertyName(int propertyId, boolean bNamed) {
 		return null;
 	}
 
+	
 	static String getPropertyDescription(int entryType, int entryValueType) {
 		String ret = "";
 		if ( entryType < 0x8000 ) {
@@ -492,7 +496,7 @@ PSTNodeInputStream readLeaf(long bid)
 		throws IOException, PSTException
 	{
 		//PSTFileBlock ret = null;
-		PSTNodeInputStream ret = null;
+		//PSTNodeInputStream ret = null;
 
 		// get the index node for the descriptor index
 		OffsetIndexItem offsetItem = getOffsetIndexNode(bid);
@@ -514,7 +518,8 @@ public int getLeafSize(long bid)
 	
 		// we only need the first 8 bytes
 		byte[] data = new byte[8];
-		in.seek(offsetItem.fileOffset);
+		//in.seek(offsetItem.fileOffset);
+		seek(offsetItem.fileOffset);
 		in.read(data);
 	
 		// we are an array, get the sum of the sizes...
@@ -535,7 +540,8 @@ protected long extractLEFileOffset(long startOffset)
 	{
 		long offset = 0;
 		if (this.getPSTFileType() == PSTFile.PST_TYPE_ANSI) {
-			in.seek(startOffset);
+			//in.seek(startOffset);
+			seek(startOffset);
 			byte[] temp = new byte[4];
 			in.read(temp);
 			offset |= temp[3] & 0xff;
@@ -546,7 +552,8 @@ protected long extractLEFileOffset(long startOffset)
 			offset <<= 8;
 			offset |= temp[0] & 0xff;
 		} else {
-			in.seek(startOffset);
+			//in.seek(startOffset);
+			seek(startOffset);
 			byte[] temp = new byte[8];
 			in.read(temp);
 			offset = temp[7] & 0xff;
@@ -570,7 +577,7 @@ protected long extractLEFileOffset(long startOffset)
 	 * @throws IOException
 	 * @throws PSTException
 	 */
-	private byte[] findBtreeItem(RandomAccessFile in, long index, boolean descTree)
+	private byte[] findBtreeItem(/*RandomAccessFile in, */long index, boolean descTree)
 		throws IOException, PSTException
 	{
 
@@ -592,9 +599,11 @@ private byte[] findBtreeItem(RandomAccessFile in, long index, boolean descTree)
 		// try and read the index b-tree
 		byte[] temp = new byte[2];
 		if (this.getPSTFileType() == PST_TYPE_ANSI) {
-			in.seek(btreeStartOffset+500);
+			//in.seek(btreeStartOffset+500);
+			seek(btreeStartOffset+500);
 		} else {
-			in.seek(btreeStartOffset+496);
+			//in.seek(btreeStartOffset+496);
+			seek(btreeStartOffset+496);
 		}
 		in.read(temp);
 		while	((temp[0] == 0xffffff80 && temp[1] == 0xffffff80 && !descTree) ||
@@ -608,7 +617,8 @@ private byte[] findBtreeItem(RandomAccessFile in, long index, boolean descTree)
 			} else {
 				branchNodeItems = new byte[488];
 			}
-			in.seek(btreeStartOffset);
+			//in.seek(btreeStartOffset);
+			seek(btreeStartOffset);
 			in.read(branchNodeItems);
 
 			int numberOfItems = in.read();
@@ -624,7 +634,8 @@ private byte[] findBtreeItem(RandomAccessFile in, long index, boolean descTree)
 						if (indexIdOfFirstChildNode > index) {
 							// get the address for the child first node in this group
 							btreeStartOffset = extractLEFileOffset(btreeStartOffset+((x-1) * 12)+8);
-							in.seek(btreeStartOffset+500);
+							//in.seek(btreeStartOffset+500);
+							seek(btreeStartOffset+500);
 							in.read(temp);
 							found = true;
 							break;
@@ -634,7 +645,8 @@ private byte[] findBtreeItem(RandomAccessFile in, long index, boolean descTree)
 						if (indexIdOfFirstChildNode > index) {
 							// get the address for the child first node in this group
 							btreeStartOffset = extractLEFileOffset(btreeStartOffset+((x-1) * 24)+16);
-							in.seek(btreeStartOffset+496);
+							//in.seek(btreeStartOffset+496);
+							seek(btreeStartOffset+496);
 							in.read(temp);
 							found = true;
 							break;
@@ -645,11 +657,13 @@ private byte[] findBtreeItem(RandomAccessFile in, long index, boolean descTree)
 					// it must be in the very last branch...
 					if (this.getPSTFileType() == PST_TYPE_ANSI) {
 						btreeStartOffset = extractLEFileOffset(btreeStartOffset+((numberOfItems-1) * 12)+8);
-						in.seek(btreeStartOffset+500);
+						//in.seek(btreeStartOffset+500);
+						seek(btreeStartOffset+500);
 						in.read(temp);
 					} else {
 						btreeStartOffset = extractLEFileOffset(btreeStartOffset+((numberOfItems-1) * 24)+16);
-						in.seek(btreeStartOffset+496);
+						//in.seek(btreeStartOffset+496);
+						seek(btreeStartOffset+496);
 						in.read(temp);
 					}
 				}
@@ -664,12 +678,14 @@ private byte[] findBtreeItem(RandomAccessFile in, long index, boolean descTree)
 						if (descTree)
 						{
 							// The 32-bit descriptor index b-tree leaf node item
-							in.seek(btreeStartOffset + (x * 16));
+							//in.seek(btreeStartOffset + (x * 16));
+							seek(btreeStartOffset + (x * 16));
 							temp = new byte[4];
 							in.read(temp);
 							if (PSTObject.convertLittleEndianBytesToLong(temp) == index) {
 								// give me the offset index please!
-								in.seek(btreeStartOffset + (x * 16));
+								//in.seek(btreeStartOffset + (x * 16));
+								seek(btreeStartOffset + (x * 16));
 								temp = new byte[16];
 								in.read(temp);
 								return temp;
@@ -683,7 +699,8 @@ private byte[] findBtreeItem(RandomAccessFile in, long index, boolean descTree)
 							if (indexIdOfFirstChildNode == index) {
 								// we found it!!!! OMG
 								//System.out.println("item found as item #"+x);
-								in.seek(btreeStartOffset + (x * 12));
+								//in.seek(btreeStartOffset + (x * 12));
+								seek(btreeStartOffset + (x * 12));
 
 								temp = new byte[12];
 								in.read(temp);
@@ -694,13 +711,15 @@ private byte[] findBtreeItem(RandomAccessFile in, long index, boolean descTree)
 						if (descTree)
 						{
 							// The 64-bit descriptor index b-tree leaf node item
-							in.seek(btreeStartOffset + (x * 32));
+							//in.seek(btreeStartOffset + (x * 32));
+							seek(btreeStartOffset + (x * 32));
 
 							temp = new byte[4];
 							in.read(temp);
 							if (PSTObject.convertLittleEndianBytesToLong(temp) == index) {
 								// give me the offset index please!
-								in.seek(btreeStartOffset + (x * 32));
+								//in.seek(btreeStartOffset + (x * 32));
+								seek(btreeStartOffset + (x * 32));
 								temp = new byte[32];
 								in.read(temp);
 								return temp;
@@ -714,7 +733,8 @@ private byte[] findBtreeItem(RandomAccessFile in, long index, boolean descTree)
 							if (indexIdOfFirstChildNode == index) {
 								// we found it!!!! OMG
 								//System.out.println("item found as item #"+x);
-								in.seek(btreeStartOffset + (x * 24));
+								//in.seek(btreeStartOffset + (x * 24));
+								seek(btreeStartOffset + (x * 24));
 
 								temp = new byte[24];
 								in.read(temp);
@@ -741,7 +761,7 @@ private byte[] findBtreeItem(RandomAccessFile in, long index, boolean descTree)
 	DescriptorIndexNode getDescriptorIndexNode(long identifier)
 		throws IOException, PSTException
 	{
-		return new DescriptorIndexNode(findBtreeItem(in, identifier, true), this.getPSTFileType());
+		return new DescriptorIndexNode(findBtreeItem(/*in,*/ identifier, true), this.getPSTFileType());
 	}
 
 	/**
@@ -755,7 +775,7 @@ DescriptorIndexNode getDescriptorIndexNode(long identifier)
 	OffsetIndexItem getOffsetIndexNode(long identifier)
 		throws IOException, PSTException
 	{
-		return new OffsetIndexItem(findBtreeItem(in, identifier, false), this.getPSTFileType());
+		return new OffsetIndexItem(findBtreeItem(/*in,*/ identifier, false), this.getPSTFileType());
 	}
 
 
@@ -909,4 +929,10 @@ private void processDescriptorBTree(long btreeStartOffset)
 	}
 
 
+	
+	
+	private void seek(long pos) throws IOException {
+		System.out.println("seeking file => "+pos);
+		this.in.seek(pos);
+	}
 }
diff --git a/example/Test.java b/example/Test.java
index b95d13b..112540d 100644
--- a/example/Test.java
+++ b/example/Test.java
@@ -1,16 +1,23 @@
 package example;
-import com.pff.*;
-import java.util.*;
+import java.util.ArrayList;
+
+import com.pff.exceptions.PSTException;
+import com.pff.objects.PSTFolder;
+import com.pff.objects.PSTMessage;
+import com.pff.source.PSTRandomAccessFile;
+import com.pff.source.PSTSource;
+import com.pff.source._RandomAccessPSTSource;
 
 public class Test {
-	public static void main(String[] args)
-	{
+	
+	public static void main(String[] args) {
 		new Test(args[0]);
 	}
 
 	public Test(String filename) {
 		try {
-			PSTFile pstFile = new PSTFile(filename);
+			_RandomAccessPSTSource raSrc = new PSTRandomAccessFile(filename);
+			PSTSource pstFile = new PSTSource(raSrc);
 			System.out.println(pstFile.getMessageStore().getDisplayName());
 			processFolder(pstFile.getRootFolder());
 		} catch (Exception err) {
@@ -19,9 +26,7 @@ public Test(String filename) {
 	}
 
 	int depth = -1;
-	public void processFolder(PSTFolder folder)
-			throws PSTException, java.io.IOException
-	{
+	public void processFolder(PSTFolder folder) throws PSTException, java.io.IOException {
 		depth++;
 		// the root folder doesn't have a display name
 		if (depth > 0) {
@@ -31,7 +36,7 @@ public void processFolder(PSTFolder folder)
 
 		// go through the folders...
 		if (folder.hasSubfolders()) {
-			Vector childFolders = folder.getSubFolders();
+			ArrayList childFolders = folder.getSubFolders();
 			for (PSTFolder childFolder : childFolders) {
 				processFolder(childFolder);
 			}
diff --git a/example/TestGui.java b/example/TestGui.java
index 6672855..a945dbb 100644
--- a/example/TestGui.java
+++ b/example/TestGui.java
@@ -4,26 +4,62 @@
 
 package example;
 
-import java.awt.*;
-import java.awt.event.*;
-
-import javax.swing.*;
-import javax.swing.event.*;
-import javax.swing.table.AbstractTableModel;
+import java.awt.BorderLayout;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
 import javax.swing.JMenuItem;
-import java.io.*;
-import javax.swing.tree.*;
-
-import com.pff.*;
-
-import java.util.*;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.JTextPane;
+import javax.swing.JTree;
+import javax.swing.ListSelectionModel;
+import javax.swing.WindowConstants;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
+
+import com.pff.exceptions.PSTException;
+import com.pff.objects.PSTActivity;
+import com.pff.objects.PSTAttachment;
+import com.pff.objects.PSTContact;
+import com.pff.objects.PSTFolder;
+import com.pff.objects.PSTMessage;
+import com.pff.objects.PSTMessageStore;
+import com.pff.objects.PSTObject;
+import com.pff.objects.PSTRss;
+import com.pff.objects.PSTTask;
+import com.pff.source.PSTRandomAccessFile;
+import com.pff.source.PSTSource;
 
 /**
  * @author toweruser
  *
  */
 public class TestGui implements ActionListener {
-	private PSTFile pstFile;
+	private PSTSource pstFile;
 	private EmailTableModel emailTableModel;
 	private JTextPane emailText;
 	private JPanel emailPanel;
@@ -40,7 +76,7 @@ public TestGui() throws PSTException, IOException {
 		
 		// attempt to open the pst file
 		try {
-			/*
+			
 			JFileChooser chooser = new JFileChooser();
 			if (chooser.showOpenDialog(f) == JFileChooser.APPROVE_OPTION) {
 			} else {
@@ -48,13 +84,12 @@ public TestGui() throws PSTException, IOException {
 			}
 
 			String filename = chooser.getSelectedFile().getCanonicalPath();
-			 */
-			String filename = "Outlook-new.pst";
-			filename = "G:\\From old Tower\\pff\\java\\Old Email.pst";
+			 
+			/*String filename = "Outlook-new.pst";
+			filename = "G:\\From old Tower\\pff\\java\\Old Email.pst";*/
 			//filename = "RichardJohnson@sumac.uk.com - exchange.ost";
 			//String filename = "Outlook 32bit.pst";
-			//String russian = "Узеи́р Абду́л-Гусе́йн оглы́ Гаджибе́ков (азерб. Üzeyir bəy Əbdülhüseyn oğlu Hacıbəyov; 18 сентября 1885, Агджабеди, Шушинский уезд, Елизаветпольская губерния, Российская империя — 23 ноября 1948, Баку, Азербайджанская ССР, СССР) — азербайджанский композитор, дирижёр, публицист, фельетонист, драматург и педагог, народный артист СССР (1938), дважды лауреат Сталинских премий (1941, 1946). Действительный член АН Азербайджана (1945), профессор (1940), ректор Азербайджанской государственной ";
-
+			
 			//System.out.println(java.nio.charset.Charset.availableCharsets());
 
 			//byte[] russianBytes = russian.getBytes("UTF-8");
@@ -62,7 +97,8 @@ public TestGui() throws PSTException, IOException {
 
 			//String filename = "Outlook 32bit.pst";
 			//filename = "RichardJohnson@sumac.uk.com - exchange.ost";
-			pstFile = new PSTFile(filename);
+			PSTRandomAccessFile pstRaF = new PSTRandomAccessFile(filename);
+			pstFile = new PSTSource(pstRaF);
 			//pstFile = new PSTFile("RichardJohnson@sumac.uk.com - exchange.ost");
 
 
@@ -175,7 +211,9 @@ public TestGui() throws PSTException, IOException {
         }
         
         final JTree folderTree = new JTree(top){
-        	public String convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
+        	private static final long serialVersionUID = 1L;
+
+			public String convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
         		DefaultMutableTreeNode nodeValue = (DefaultMutableTreeNode)value;
         		if (nodeValue.getUserObject() instanceof PSTFolder) {
         			PSTFolder folderValue = (PSTFolder)nodeValue.getUserObject();
@@ -293,14 +331,14 @@ public void valueChanged(ListSelectionEvent e) {
         // Set the visibility as true, thereby displaying it
         f.setVisible(true);
 //        f.setSize(800, 600);
-        f.setExtendedState(f.getExtendedState() | f.MAXIMIZED_BOTH);
+        f.setExtendedState(f.getExtendedState() | JFrame.MAXIMIZED_BOTH);
 	}
 	
 	private void buildTree(DefaultMutableTreeNode top, PSTFolder theFolder) {
 		// this is recursive, try and keep up.
 		try {
-			Vector children = theFolder.getSubFolders();
-			Iterator childrenIterator = children.iterator();
+			ArrayList children = theFolder.getSubFolders();
+			Iterator childrenIterator = children.iterator();
 			while (childrenIterator.hasNext()) {
 				PSTFolder folder = (PSTFolder)childrenIterator.next();
 
@@ -429,13 +467,14 @@ public static void main(String[] args) throws PSTException, IOException {
 }
 
 class EmailTableModel extends AbstractTableModel {
+	private static final long serialVersionUID = 6414607674310021030L;
 	
 	PSTFolder theFolder = null;
-	PSTFile theFile = null;
+	PSTSource theFile = null;
 	
-	HashMap cache = new HashMap();
+	HashMap cache = new HashMap();
 	
-	public EmailTableModel(PSTFolder theFolder, PSTFile theFile) {
+	public EmailTableModel(PSTFolder theFolder, PSTSource theFile) {
 		super();
 		
 		this.theFolder = theFolder;
@@ -527,7 +566,7 @@ public void setFolder(PSTFolder theFolder)
 	{
     	theFolder.moveChildCursorTo(0);
     	this.theFolder = theFolder;
-    	cache = new HashMap();
+    	cache = new HashMap();
     	this.fireTableDataChanged();
     }
 
diff --git a/example/TestRandomAccess.java b/example/TestRandomAccess.java
new file mode 100644
index 0000000..d0fdc97
--- /dev/null
+++ b/example/TestRandomAccess.java
@@ -0,0 +1,15 @@
+package example;
+
+public class TestRandomAccess {
+
+	public TestRandomAccess() {
+		
+	}
+
+	
+	public static void main(String[] args) {
+		
+
+	}
+
+}