diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp
index cf337be1615fc7860c478d1772ebc9c6b9f14f3b..c341b15539967be01fd3f3a810d8a74a5cf3a5de 100644
--- a/indra/llcommon/llsdserialize.cpp
+++ b/indra/llcommon/llsdserialize.cpp
@@ -39,6 +39,7 @@
 
 #include <iostream>
 #include "apr_base64.h"
+#include "zlib/zlib.h"  // for davep's dirty little zip functions
 
 #if !LL_WINDOWS
 #include <netinet/in.h> // htonl & ntohl
@@ -1989,3 +1990,139 @@ std::ostream& operator<<(std::ostream& s, const LLSD& llsd)
 	return s;
 }
 
+
+//dirty little zippers -- yell at davep if these are horrid
+
+//return a string containing gzipped bytes of binary serialized LLSD
+// VERY inefficient -- creates several copies of LLSD block in memory
+std::string zip_llsd(LLSD& data)
+{ 
+	std::stringstream llsd_strm;
+
+	LLSDSerialize::serialize(data, llsd_strm, LLSDSerialize::LLSD_BINARY);
+
+	z_stream strm;
+	strm.zalloc = Z_NULL;
+	strm.zfree = Z_NULL;
+	strm.opaque = Z_NULL;
+
+	S32 ret = deflateInit(&strm, Z_BEST_COMPRESSION);
+	if (ret != Z_OK)
+	{
+		llwarns << "Failed to compress LLSD block." << llendl;
+		return std::string();
+	}
+
+	std::string source = llsd_strm.str();
+
+	strm.avail_in = source.size();
+	strm.next_in = (U8*) source.data();
+	U8* output = new U8[strm.avail_in];
+	strm.avail_out = strm.avail_in;
+	strm.next_out = output;
+	ret = deflate(&strm, Z_FINISH);
+	if (ret != Z_STREAM_END)
+	{
+		delete [] output;
+		llwarns << "Failed to compress LLSD block." << llendl;
+	}
+
+	std::string::size_type size = source.size()-strm.avail_out;
+
+	std::string result((char*) output, size);
+	deflateEnd(&strm);
+	delete [] output;
+
+	return result;
+}
+
+//decompress a block of LLSD from provided istream
+// not very efficient -- creats a copy of decompressed LLSD block in memory
+// and deserializes from that copy using LLSDSerialize
+bool unzip_llsd(LLSD& data, std::istream& is, S32 size)
+{
+	U8* result = NULL;
+	U32 cur_size = 0;
+	z_stream strm;
+		
+	const U32 CHUNK = 65536;
+
+	U8 *in = new U8[size];
+	is.read((char*) in, size); 
+
+	U8 out[CHUNK];
+		
+	strm.zalloc = Z_NULL;
+	strm.zfree = Z_NULL;
+	strm.opaque = Z_NULL;
+	strm.avail_in = size;
+	strm.next_in = in;
+
+	S32 ret = inflateInit(&strm);
+
+	if (ret != Z_OK)
+	{
+		llerrs << "WTF?" << llendl;
+	}
+	
+	do
+	{
+		strm.avail_out = CHUNK;
+		strm.next_out = out;
+		ret = inflate(&strm, Z_NO_FLUSH);
+		if (ret == Z_STREAM_ERROR)
+		{
+			inflateEnd(&strm);
+			free(result);
+			delete [] in;
+			return false;
+		}
+		
+		switch (ret)
+		{
+		case Z_NEED_DICT:
+			ret = Z_DATA_ERROR;
+		case Z_DATA_ERROR:
+		case Z_MEM_ERROR:
+			inflateEnd(&strm);
+			free(result);
+			delete [] in;
+			return false;
+			break;
+		}
+
+		U32 have = CHUNK-strm.avail_out;
+
+		result = (U8*) realloc(result, cur_size + have);
+		memcpy(result+cur_size, out, have);
+		cur_size += have;
+
+	} while (strm.avail_out == 0);
+
+	inflateEnd(&strm);
+	delete [] in;
+
+	if (ret != Z_STREAM_END)
+	{
+		free(result);
+		return false;
+	}
+
+	//result now points to the decompressed LLSD block
+	{
+		std::string res_str((char*) result, cur_size);
+		std::istringstream istr(res_str);
+
+		if (!LLSDSerialize::deserialize(data, istr, cur_size))
+		{
+			llwarns << "Failed to unzip LLSD block" << llendl;
+			return false;
+		}
+	}
+
+	free(result);
+	return true;
+}
+
+
+
diff --git a/indra/llcommon/llsdserialize.h b/indra/llcommon/llsdserialize.h
index 2f2b292189bf099373899d3eb48d0ddddc16d74c..390eaca783828ae5b3c21159b89aeff2f1988e81 100644
--- a/indra/llcommon/llsdserialize.h
+++ b/indra/llcommon/llsdserialize.h
@@ -796,4 +796,8 @@ class LL_COMMON_API LLSDSerialize
 	}
 };
 
+//dirty little zip functions -- yell at davep
+LL_COMMON_API std::string zip_llsd(LLSD& data);
+LL_COMMON_API bool unzip_llsd(LLSD& data, std::istream& is, S32 size);
+
 #endif // LL_LLSDSERIALIZE_H
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 52a3fb21955b8ff7d142c37416d25847328fe9ff..c563af592f193a5f6606848030eb3d31df4e6cbd 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -47,8 +47,6 @@
 #include "llvolume.h"
 #include "llstl.h"
 #include "llsdserialize.h"
-#include "zlib/zlib.h"
-
 
 #define DEBUG_SILHOUETTE_BINORMALS 0
 #define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette
@@ -1964,97 +1962,15 @@ BOOL LLVolume::createVolumeFacesFromStream(std::istream& is)
 
 bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
 {
-	U8* result = NULL;
-	U32 cur_size = 0;
-
-	{
-		//input stream is now pointing at a zlib compressed block of LLSD
-		//decompress block
-		z_stream strm;
-		
-		const U32 CHUNK = 65536;
-
-		U8 *in = new U8[size];
-		is.read((char*) in, size); 
-
-		U8 out[CHUNK];
-			
-		strm.zalloc = Z_NULL;
-		strm.zfree = Z_NULL;
-		strm.opaque = Z_NULL;
-		strm.avail_in = size;
-		strm.next_in = in;
-
-		S32 ret = inflateInit(&strm);
-
-		if (ret != Z_OK)
-		{
-			llerrs << "WTF?" << llendl;
-		}
-		
-		do
-		{
-			strm.avail_out = CHUNK;
-			strm.next_out = out;
-			ret = inflate(&strm, Z_NO_FLUSH);
-			if (ret == Z_STREAM_ERROR)
-			{
-				inflateEnd(&strm);
-				free(result);
-				delete [] in;
-				return false;
-			}
-			
-			switch (ret)
-			{
-			case Z_NEED_DICT:
-				ret = Z_DATA_ERROR;
-			case Z_DATA_ERROR:
-			case Z_MEM_ERROR:
-				inflateEnd(&strm);
-				free(result);
-				delete [] in;
-				return false;
-				break;
-			}
-
-			U32 have = CHUNK-strm.avail_out;
-
-			result = (U8*) realloc(result, cur_size + have);
-			memcpy(result+cur_size, out, have);
-			cur_size += have;
-
-		} while (strm.avail_out == 0);
-
-		inflateEnd(&strm);
-		delete [] in;
-
-		if (ret != Z_STREAM_END)
-		{
-			free(result);
-			return false;
-		}
-	}
-
-	//result now points to the decompressed LLSD block
-
+	//input stream is now pointing at a zlib compressed block of LLSD
+	//decompress block
 	LLSD mdl;
-
+	if (!unzip_llsd(mdl, is, size))
 	{
-		std::string res_str((char*) result, cur_size);
-		std::istringstream istr(res_str);
-
-		if (!LLSDSerialize::deserialize(mdl, istr, cur_size))
-		{
-			llwarns << "not a valid mesh asset!" << llendl;
-			return false;
-		}
+		llwarns << "not a valid mesh asset!" << llendl;
+		return false;
 	}
-
-
-	free(result);
-
-
+	
 	{
 		U32 face_count = mdl.size();
 
@@ -2094,11 +2010,50 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
 			U32 num_verts = pos.size()/(3*2);
 			face.mVertices.resize(num_verts);
 
+			if (mdl[i].has("Weights"))
+			{
+				face.mWeights.resize(num_verts);
+				LLSD::Binary weights = mdl[i]["Weights"];
+
+				LLSD::Binary::iterator iter = weights.begin();
+
+				U32 cur_vertex = 0;
+				while (iter != weights.end())
+				{
+					const S32 END_INFLUENCES = 0xFF;
+					U8 joint = *(iter++);
+
+					U32 cur_influence = 0;
+					while (joint != END_INFLUENCES)
+					{
+						U16 influence = *(iter++);
+						influence = influence << 8;
+						influence |= *(iter++);
+
+						F32 w = llmin((F32) influence / 65535.f, 0.99999f);
+						face.mWeights[cur_vertex].mV[cur_influence++] = (F32) joint + w;
+
+						if (cur_influence >= 4)
+						{
+							joint = END_INFLUENCES;
+						}
+						else
+						{
+							joint = *(iter++);
+						}
+					}
+
+					cur_vertex++;
+					iter++;
+				}
+			}
+
 			LLVector3 min_pos;
 			LLVector3 max_pos;
 			LLVector2 min_tc; 
 			LLVector2 max_tc; 
 
+		
 			min_pos.setValue(mdl[i]["PositionDomain"]["Min"]);
 			max_pos.setValue(mdl[i]["PositionDomain"]["Max"]);
 			min_tc.setValue(mdl[i]["TexCoord0Domain"]["Min"]);
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index 60c1569e550f6a98c31085c9ba929623d7c57b79..c6a156ae3791e3e77d787e1086b7468091a48e59 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -49,6 +49,7 @@ class LLVolume;
 //#include "vmath.h"
 #include "v2math.h"
 #include "v3math.h"
+#include "v4math.h"
 #include "llquaternion.h"
 #include "llstrider.h"
 #include "v4coloru.h"
@@ -887,6 +888,11 @@ class LLVolumeFace
 	std::vector<U16>	mTriStrip;
 	std::vector<S32>	mEdge;
 
+	//list of skin weights for rigged volumes
+	// format is mWeights[vertex_index].mV[influence] = <joint_index>.<weight>
+	// mWeights.size() should be empty or match mVertices.size()  
+	std::vector<LLVector4> mWeights;
+
 private:
 	BOOL createUnCutCubeCap(LLVolume* volume, BOOL partial_build = FALSE);
 	BOOL createCap(LLVolume* volume, BOOL partial_build = FALSE);