diff --git a/src/animation/AnimBlendAssociation.cpp b/src/animation/AnimBlendAssociation.cpp
index 2f4739a6..b91b35ee 100644
--- a/src/animation/AnimBlendAssociation.cpp
+++ b/src/animation/AnimBlendAssociation.cpp
@@ -139,11 +139,15 @@ CAnimBlendAssociation::SetCurrentTime(float time)
 		}
 
 	CAnimManager::UncompressAnimation(hierarchy);
-	if(hierarchy->compressed2){
+#ifdef ANIM_COMPRESSION
+	// strangely PC has this but android doesn't
+	if(hierarchy->keepCompressed){
 		for(i = 0; i < numNodes; i++)
 			if(nodes[i].sequence)
 				nodes[i].SetupKeyFrameCompressed();
-	}else{
+	}else
+#endif
+	{
 		for(i = 0; i < numNodes; i++)
 			if(nodes[i].sequence)
 				nodes[i].FindKeyFrame(currentTime);
diff --git a/src/animation/AnimBlendClumpData.h b/src/animation/AnimBlendClumpData.h
index 315cbd8a..633fc7b9 100644
--- a/src/animation/AnimBlendClumpData.h
+++ b/src/animation/AnimBlendClumpData.h
@@ -12,7 +12,7 @@ struct AnimBlendFrameData
 		VELOCITY_EXTRACTION = 8,
 		VELOCITY_EXTRACTION_3D = 0x10,
 		UPDATE_KEYFRAMES = 0x20,
-		UNK_COMPRESSED = 0x40,
+		COMPRESSED = 0x40
 	};
 
 	uint8 flag;
diff --git a/src/animation/AnimBlendHierarchy.cpp b/src/animation/AnimBlendHierarchy.cpp
index f3e90818..cc7c7de8 100644
--- a/src/animation/AnimBlendHierarchy.cpp
+++ b/src/animation/AnimBlendHierarchy.cpp
@@ -65,10 +65,10 @@ CAnimBlendHierarchy::CalcTotalTimeCompressed(void)
 			continue;
 #endif
 
-		totalLength = Max(totalLength, sequences[i].GetKeyFrameCompressed(sequences[i].numFrames-1)->deltaTime/60.0f);
+		totalLength = Max(totalLength, sequences[i].GetKeyFrameCompressed(sequences[i].numFrames-1)->GetDeltaTime());
 		for(j = sequences[i].numFrames-1; j >= 1; j--){
-			KeyFrame *kf1 = sequences[i].GetKeyFrameCompressed(j);
-			KeyFrame *kf2 = sequences[i].GetKeyFrameCompressed(j-1);
+			KeyFrameCompressed *kf1 = sequences[i].GetKeyFrameCompressed(j);
+			KeyFrameCompressed *kf2 = sequences[i].GetKeyFrameCompressed(j-1);
 			kf1->deltaTime -= kf2->deltaTime;
 		}
 	}
@@ -94,6 +94,12 @@ CAnimBlendHierarchy::RemoveAnimSequences(void)
 void
 CAnimBlendHierarchy::Uncompress(void)
 {
+#ifdef ANIM_COMPRESSION
+	int i;
+	assert(compressed);
+	for(i = 0; i < numSequences; i++)
+		sequences[i].Uncompress();
+#endif
 	compressed = 0;
 	if(totalLength == 0.0f){
 		RemoveQuaternionFlips();
@@ -104,7 +110,12 @@ CAnimBlendHierarchy::Uncompress(void)
 void
 CAnimBlendHierarchy::RemoveUncompressedData(void)
 {
-	// useless
+#ifdef ANIM_COMPRESSION
+	int i;
+	assert(!compressed);
+	for(i = 0; i < numSequences; i++)
+		sequences[i].RemoveUncompressedData();
+#endif
 	compressed = 1;
 }
 
diff --git a/src/animation/AnimBlendHierarchy.h b/src/animation/AnimBlendHierarchy.h
index 424a925d..4838c4f8 100644
--- a/src/animation/AnimBlendHierarchy.h
+++ b/src/animation/AnimBlendHierarchy.h
@@ -15,8 +15,8 @@ public:
 	char name[24];
 	CAnimBlendSequence *sequences;
 	int16 numSequences;
-	bool compressed;	// not really used
-	bool compressed2;	// not really used
+	bool compressed;
+	bool keepCompressed;
 	float totalLength;
 	CLink<CAnimBlendHierarchy*> *linkPtr;
 
diff --git a/src/animation/AnimBlendNode.cpp b/src/animation/AnimBlendNode.cpp
index ac1328eb..62423004 100644
--- a/src/animation/AnimBlendNode.cpp
+++ b/src/animation/AnimBlendNode.cpp
@@ -47,6 +47,44 @@ CAnimBlendNode::Update(CVector &trans, CQuaternion &rot, float weight)
 	return looped;
 }
 
+bool
+CAnimBlendNode::UpdateCompressed(CVector &trans, CQuaternion &rot, float weight)
+{
+	bool looped = false;
+
+	trans = CVector(0.0f, 0.0f, 0.0f);
+	rot = CQuaternion(0.0f, 0.0f, 0.0f, 0.0f);
+
+	if(association->IsRunning()){
+		remainingTime -= association->timeStep;
+		if(remainingTime <= 0.0f)
+			looped = NextKeyFrameCompressed();
+	}
+
+	float blend = association->GetBlendAmount(weight);
+	if(blend > 0.0f){
+		KeyFrameTransCompressed *kfA = (KeyFrameTransCompressed*)sequence->GetKeyFrameCompressed(frameA);
+		KeyFrameTransCompressed *kfB = (KeyFrameTransCompressed*)sequence->GetKeyFrameCompressed(frameB);
+		float t = kfA->deltaTime == 0 ? 0.0f : (kfA->GetDeltaTime() - remainingTime)/kfA->GetDeltaTime();
+		if(sequence->type & CAnimBlendSequence::KF_TRANS){
+			CVector transA, transB;
+			kfA->GetTranslation(&transA);
+			kfB->GetTranslation(&transB);
+			trans = transB + t*(transA - transB);
+			trans *= blend;
+		}
+		if(sequence->type & CAnimBlendSequence::KF_ROT){
+			CQuaternion rotA, rotB;
+			kfA->GetRotation(&rotA);
+			kfB->GetRotation(&rotB);
+			rot.Slerp(rotB, rotA, theta, invSin, t);
+			rot *= blend;
+		}
+	}
+
+	return looped;
+}
+
 bool
 CAnimBlendNode::NextKeyFrame(void)
 {
@@ -84,6 +122,43 @@ CAnimBlendNode::NextKeyFrame(void)
 	return looped;
 }
 
+bool
+CAnimBlendNode::NextKeyFrameCompressed(void)
+{
+	bool looped;
+
+	if(sequence->numFrames <= 1)
+		return false;
+
+	looped = false;
+	frameB = frameA;
+
+	// Advance as long as we have to
+	while(remainingTime <= 0.0f){
+		frameA++;
+
+		if(frameA >= sequence->numFrames){
+			// reached end of animation
+			if(!association->IsRepeating()){
+				frameA--;
+				remainingTime = 0.0f;
+				return false;
+			}
+			looped = true;
+			frameA = 0;
+		}
+
+		remainingTime += sequence->GetKeyFrameCompressed(frameA)->GetDeltaTime();
+	}
+
+	frameB = frameA - 1;
+	if(frameB < 0)
+		frameB += sequence->numFrames;
+
+	CalcDeltasCompressed();
+	return looped;
+}
+
 // Set animation to time t
 bool
 CAnimBlendNode::FindKeyFrame(float t)
@@ -132,7 +207,7 @@ CAnimBlendNode::SetupKeyFrameCompressed(void)
 		frameA = 0;
 		remainingTime = 0.0f;
 	}else
-		remainingTime = sequence->GetKeyFrameCompressed(frameA)->deltaTime/60.0f;
+		remainingTime = sequence->GetKeyFrameCompressed(frameA)->GetDeltaTime();
 
 	CalcDeltasCompressed();
 	return true;
@@ -157,9 +232,17 @@ CAnimBlendNode::CalcDeltasCompressed(void)
 {
 	if((sequence->type & CAnimBlendSequence::KF_ROT) == 0)
 		return;
-	KeyFrame *kfA = sequence->GetKeyFrameCompressed(frameA);
-	KeyFrame *kfB = sequence->GetKeyFrameCompressed(frameB);
-	float cos = DotProduct(kfA->rotation, kfB->rotation);
+	KeyFrameCompressed *kfA = sequence->GetKeyFrameCompressed(frameA);
+	KeyFrameCompressed *kfB = sequence->GetKeyFrameCompressed(frameB);
+	CQuaternion rotA, rotB;
+	kfA->GetRotation(&rotA);
+	kfB->GetRotation(&rotB);
+	float cos = DotProduct(rotA, rotB);
+	if(cos < 0.0f){
+		rotB *= -1.0f;
+		kfB->SetRotation(rotB);
+	}
+	cos = DotProduct(rotA, rotB);
 	if(cos > 1.0f)
 		cos = 1.0f;
 	theta = Acos(cos);
@@ -183,6 +266,26 @@ CAnimBlendNode::GetCurrentTranslation(CVector &trans, float weight)
 	}
 }
 
+void
+CAnimBlendNode::GetCurrentTranslationCompressed(CVector &trans, float weight)
+{
+	trans = CVector(0.0f, 0.0f, 0.0f);
+
+	float blend = association->GetBlendAmount(weight);
+	if(blend > 0.0f){
+		KeyFrameTransCompressed *kfA = (KeyFrameTransCompressed*)sequence->GetKeyFrameCompressed(frameA);
+		KeyFrameTransCompressed *kfB = (KeyFrameTransCompressed*)sequence->GetKeyFrameCompressed(frameB);
+		float t = kfA->deltaTime == 0 ? 0.0f : (kfA->GetDeltaTime() - remainingTime)/kfA->GetDeltaTime();
+		if(sequence->type & CAnimBlendSequence::KF_TRANS){
+			CVector transA, transB;
+			kfA->GetTranslation(&transA);
+			kfB->GetTranslation(&transB);
+			trans = transB + t*(transA - transB);
+			trans *= blend;
+		}
+	}
+}
+
 void
 CAnimBlendNode::GetEndTranslation(CVector &trans, float weight)
 {
@@ -195,3 +298,19 @@ CAnimBlendNode::GetEndTranslation(CVector &trans, float weight)
 			trans = kf->translation * blend;
 	}
 }
+
+void
+CAnimBlendNode::GetEndTranslationCompressed(CVector &trans, float weight)
+{
+	trans = CVector(0.0f, 0.0f, 0.0f);
+
+	float blend = association->GetBlendAmount(weight);
+	if(blend > 0.0f){
+		KeyFrameTransCompressed *kf = (KeyFrameTransCompressed*)sequence->GetKeyFrameCompressed(sequence->numFrames-1);
+		if(sequence->type & CAnimBlendSequence::KF_TRANS){
+			CVector trans;
+			kf->GetTranslation(&trans);
+			trans = trans * blend;
+		}
+	}
+}
diff --git a/src/animation/AnimBlendNode.h b/src/animation/AnimBlendNode.h
index 9446e1ae..99a657ac 100644
--- a/src/animation/AnimBlendNode.h
+++ b/src/animation/AnimBlendNode.h
@@ -20,13 +20,17 @@ public:
 
 	void Init(void);
 	bool Update(CVector &trans, CQuaternion &rot, float weight);
+	bool UpdateCompressed(CVector &trans, CQuaternion &rot, float weight);
 	bool NextKeyFrame(void);
+	bool NextKeyFrameCompressed(void);
 	bool FindKeyFrame(float t);
 	bool SetupKeyFrameCompressed(void);
 	void CalcDeltas(void);
 	void CalcDeltasCompressed(void);
 	void GetCurrentTranslation(CVector &trans, float weight);
+	void GetCurrentTranslationCompressed(CVector &trans, float weight);
 	void GetEndTranslation(CVector &trans, float weight);
+	void GetEndTranslationCompressed(CVector &trans, float weight);
 };
 
 
diff --git a/src/animation/AnimBlendSequence.cpp b/src/animation/AnimBlendSequence.cpp
index 3c675d4e..93cce91d 100644
--- a/src/animation/AnimBlendSequence.cpp
+++ b/src/animation/AnimBlendSequence.cpp
@@ -67,6 +67,119 @@ CAnimBlendSequence::RemoveQuaternionFlips(void)
 	}
 }
 
+void
+CAnimBlendSequence::Uncompress(void)
+{
+	int i;
+
+	if(numFrames == 0)
+		return;
+
+	PUSH_MEMID(MEMID_ANIMATION);
+
+	float rotScale = 1.0f/4096.0f;
+	float timeScale = 1.0f/60.0f;
+	float transScale = 1.0f/1024.0f;
+	if(type & KF_TRANS){
+		void *newKfs = RwMalloc(numFrames * sizeof(KeyFrameTrans));
+		KeyFrameTransCompressed *ckf = (KeyFrameTransCompressed*)keyFramesCompressed;
+		KeyFrameTrans *kf = (KeyFrameTrans*)newKfs;
+		for(i = 0; i < numFrames; i++){
+			kf->rotation.x = ckf->rot[0]*rotScale;
+			kf->rotation.y = ckf->rot[1]*rotScale;
+			kf->rotation.z = ckf->rot[2]*rotScale;
+			kf->rotation.w = ckf->rot[3]*rotScale;
+			kf->deltaTime = ckf->deltaTime*timeScale;
+			kf->translation.x = ckf->trans[0]*transScale;
+			kf->translation.y = ckf->trans[1]*transScale;
+			kf->translation.z = ckf->trans[2]*transScale;
+			kf++;
+			ckf++;
+		}
+		keyFrames = newKfs;
+	}else{
+		void *newKfs = RwMalloc(numFrames * sizeof(KeyFrame));
+		KeyFrameCompressed *ckf = (KeyFrameCompressed*)keyFramesCompressed;
+		KeyFrame *kf = (KeyFrame*)newKfs;
+		for(i = 0; i < numFrames; i++){
+			kf->rotation.x = ckf->rot[0]*rotScale;
+			kf->rotation.y = ckf->rot[1]*rotScale;
+			kf->rotation.z = ckf->rot[2]*rotScale;
+			kf->rotation.w = ckf->rot[3]*rotScale;
+			kf->deltaTime = ckf->deltaTime*timeScale;
+			kf++;
+			ckf++;
+		}
+		keyFrames = newKfs;
+	}
+	REGISTER_MEMPTR(&keyFrames);
+
+	RwFree(keyFramesCompressed);
+	keyFramesCompressed = nil;
+
+	POP_MEMID();
+}
+
+void
+CAnimBlendSequence::CompressKeyframes(void)
+{
+	int i;
+
+	if(numFrames == 0)
+		return;
+
+	PUSH_MEMID(MEMID_ANIMATION);
+
+	float rotScale = 4096.0f;
+	float timeScale = 60.0f;
+	float transScale = 1024.0f;
+	if(type & KF_TRANS){
+		void *newKfs = RwMalloc(numFrames * sizeof(KeyFrameTransCompressed));
+		KeyFrameTransCompressed *ckf = (KeyFrameTransCompressed*)newKfs;
+		KeyFrameTrans *kf = (KeyFrameTrans*)keyFrames;
+		for(i = 0; i < numFrames; i++){
+			ckf->rot[0] = kf->rotation.x*rotScale;
+			ckf->rot[1] = kf->rotation.y*rotScale;
+			ckf->rot[2] = kf->rotation.z*rotScale;
+			ckf->rot[3] = kf->rotation.w*rotScale;
+			ckf->deltaTime = kf->deltaTime*timeScale + 0.5f;
+			ckf->trans[0] = kf->translation.x*transScale;
+			ckf->trans[1] = kf->translation.y*transScale;
+			ckf->trans[2] = kf->translation.z*transScale;
+			kf++;
+			ckf++;
+		}
+		keyFramesCompressed = newKfs;
+	}else{
+		void *newKfs = RwMalloc(numFrames * sizeof(KeyFrameCompressed));
+		KeyFrameCompressed *ckf = (KeyFrameCompressed*)newKfs;
+		KeyFrame *kf = (KeyFrame*)keyFrames;
+		for(i = 0; i < numFrames; i++){
+			ckf->rot[0] = kf->rotation.x*rotScale;
+			ckf->rot[1] = kf->rotation.y*rotScale;
+			ckf->rot[2] = kf->rotation.z*rotScale;
+			ckf->rot[3] = kf->rotation.w*rotScale;
+			ckf->deltaTime = kf->deltaTime*timeScale + 0.5f;
+			kf++;
+			ckf++;
+		}
+		keyFramesCompressed = newKfs;
+	}
+	REGISTER_MEMPTR(&keyFramesCompressed);
+
+	POP_MEMID();
+}
+
+void
+CAnimBlendSequence::RemoveUncompressedData(void)
+{
+	if(numFrames == 0)
+		return;
+	CompressKeyframes();
+	RwFree(keyFrames);
+	keyFrames = nil;
+}
+
 #ifdef USE_CUSTOM_ALLOCATOR
 bool
 CAnimBlendSequence::MoveMemory(void)
@@ -87,4 +200,3 @@ CAnimBlendSequence::MoveMemory(void)
 	return false;
 }
 #endif
-
diff --git a/src/animation/AnimBlendSequence.h b/src/animation/AnimBlendSequence.h
index 1c2531ce..67118b2f 100644
--- a/src/animation/AnimBlendSequence.h
+++ b/src/animation/AnimBlendSequence.h
@@ -16,6 +16,43 @@ struct KeyFrameTrans : KeyFrame {
 	CVector translation;
 };
 
+struct KeyFrameCompressed {
+	int16 rot[4];		// 4096
+	int16 deltaTime;	// 60
+
+	void GetRotation(CQuaternion *quat){
+		float scale = 1.0f/4096.0f;
+		quat->x = rot[0]*scale;
+		quat->y = rot[1]*scale;
+		quat->z = rot[2]*scale;
+		quat->w = rot[3]*scale;
+	}
+	void SetRotation(const CQuaternion &quat){
+		rot[0] = quat.x * 4096.0f;
+		rot[1] = quat.y * 4096.0f;
+		rot[2] = quat.z * 4096.0f;
+		rot[3] = quat.w * 4096.0f;
+	}
+	float GetDeltaTime(void) { return deltaTime/60.0f; }
+	void SetTime(float t) { deltaTime = t*60.0f + 0.5f; }
+};
+
+struct KeyFrameTransCompressed : KeyFrameCompressed {
+	int16 trans[3];		// 1024
+
+	void GetTranslation(CVector *vec) {
+		float scale = 1.0f/1024.0f;
+		vec->x = trans[0]*scale;
+		vec->y = trans[1]*scale;
+		vec->z = trans[2]*scale;
+	}
+	void SetTranslation(const CVector &vec){
+		trans[0] = vec.x*1024.0f;
+		trans[1] = vec.y*1024.0f;
+		trans[2] = vec.z*1024.0f;
+	}
+};
+
 
 // The sequence of key frames of one animated node
 class CAnimBlendSequence
@@ -42,12 +79,15 @@ public:
 			&((KeyFrameTrans*)keyFrames)[n] :
 			&((KeyFrame*)keyFrames)[n];
 	}
-	KeyFrame *GetKeyFrameCompressed(int n) {
+	KeyFrameCompressed *GetKeyFrameCompressed(int n) {
 		return type & KF_TRANS ?
-			&((KeyFrameTrans*)keyFramesCompressed)[n] :
-			&((KeyFrame*)keyFramesCompressed)[n];
+			&((KeyFrameTransCompressed*)keyFramesCompressed)[n] :
+			&((KeyFrameCompressed*)keyFramesCompressed)[n];
 	}
 	bool HasTranslation(void) { return !!(type & KF_TRANS); }
+	void Uncompress(void);
+	void CompressKeyframes(void);
+	void RemoveUncompressedData(void);
 	bool MoveMemory(void);
 
 	void SetBoneTag(int tag) { boneTag = tag; }
diff --git a/src/animation/AnimManager.cpp b/src/animation/AnimManager.cpp
index 0aa6486b..edb7f877 100644
--- a/src/animation/AnimManager.cpp
+++ b/src/animation/AnimManager.cpp
@@ -992,7 +992,7 @@ CAnimManager::Shutdown(void)
 void
 CAnimManager::UncompressAnimation(CAnimBlendHierarchy *hier)
 {
-	if(hier->compressed2){
+	if(hier->keepCompressed){
 		if(hier->totalLength == 0.0f)
 			hier->CalcTotalTimeCompressed();
 	}else{
@@ -1275,7 +1275,7 @@ CAnimManager::LoadAnimFile(const char *filename)
 
 //--MIAMI: done
 void
-CAnimManager::LoadAnimFile(RwStream *stream, bool compress, char (*somename)[32])
+CAnimManager::LoadAnimFile(RwStream *stream, bool compress, char (*uncompressedAnims)[32])
 {
 	#define ROUNDSIZE(x) if((x) & 3) (x) += 4 - ((x)&3)
 	struct IfpHeader {
@@ -1320,16 +1320,22 @@ CAnimManager::LoadAnimFile(RwStream *stream, bool compress, char (*somename)[32]
 		RwStreamRead(stream, buf, name.size);
 		hier->SetName(buf);
 
-		// Unimplemented uncompressed anim thing
-		if (somename) {
-			for (int i = 0; somename[i][0]; i++) {
-				if (!CGeneral::faststricmp(somename[i], hier->name))
+#ifdef ANIM_COMPRESSION
+		bool compressHier = compress;
+#else
+		bool compressHier = false;
+#endif
+		if (uncompressedAnims) {
+			for (int i = 0; uncompressedAnims[i][0]; i++) {
+				if (!CGeneral::faststricmp(uncompressedAnims[i], hier->name)){
 					debug("Loading %s uncompressed\n", hier->name);
+					compressHier = false;
+				}
 			}
 		}
 
-		hier->compressed = false;
-		hier->compressed2 = false;
+		hier->compressed = compressHier;
+		hier->keepCompressed = false;
 
 		// DG info has number of nodes/sequences
 		RwStreamRead(stream, (char*)&dgan, sizeof(IfpHeader));
@@ -1349,67 +1355,86 @@ CAnimManager::LoadAnimFile(RwStream *stream, bool compress, char (*somename)[32]
 			ROUNDSIZE(anim.size);
 			RwStreamRead(stream, buf, anim.size);
 			int numFrames = *(int*)(buf+28);
+			seq->SetName(buf);
 			if(anim.size == 44)
 				seq->SetBoneTag(*(int*)(buf+40));
-			seq->SetName(buf);
 			if(numFrames == 0)
 				continue;
 
+			bool hasScale = false;
+			bool hasTranslation = false;
 			RwStreamRead(stream, &info, sizeof(info));
-			if(strncmp(info.ident, "KR00", 4) == 0){
-				seq->SetNumFrames(numFrames, false, false);
-				KeyFrame *kf = seq->GetKeyFrame(0);
-				if (strstr(seq->name, "L Toe"))
-					debug("anim %s has toe keyframes\n", hier->name); // , seq->name);
-
-				for(l = 0; l < numFrames; l++, kf++){
-					RwStreamRead(stream, buf, 0x14);
-					kf->rotation.x = -fbuf[0];
-					kf->rotation.y = -fbuf[1];
-					kf->rotation.z = -fbuf[2];
-					kf->rotation.w = fbuf[3];
-					kf->deltaTime = fbuf[4];	// absolute time here
-				}
+			if(strncmp(info.ident, "KRTS", 4) == 0){
+				hasScale = true;
+				seq->SetNumFrames(numFrames, true, compressHier);
 			}else if(strncmp(info.ident, "KRT0", 4) == 0){
-				seq->SetNumFrames(numFrames, true, false);
-				KeyFrameTrans *kf = (KeyFrameTrans*)seq->GetKeyFrame(0);
-				if (strstr(seq->name, "L Toe"))
-					debug("anim %s has toe keyframes\n", hier->name); // , seq->name);
+				hasTranslation = true;
+				seq->SetNumFrames(numFrames, true, compressHier);
+			}else if(strncmp(info.ident, "KR00", 4) == 0){
+				seq->SetNumFrames(numFrames, false, compressHier);
+			}
+			if(strstr(seq->name, "L Toe"))
+				debug("anim %s has toe keyframes\n", hier->name); // BUG: seq->name
 
-				for(l = 0; l < numFrames; l++, kf++){
-					RwStreamRead(stream, buf, 0x20);
-					kf->rotation.x = -fbuf[0];
-					kf->rotation.y = -fbuf[1];
-					kf->rotation.z = -fbuf[2];
-					kf->rotation.w = fbuf[3];
-					kf->translation.x = fbuf[4];
-					kf->translation.y = fbuf[5];
-					kf->translation.z = fbuf[6];
-					kf->deltaTime = fbuf[7];	// absolute time here
-				}
-			}else if(strncmp(info.ident, "KRTS", 4) == 0){
-				seq->SetNumFrames(numFrames, true, false);
-				KeyFrameTrans *kf = (KeyFrameTrans*)seq->GetKeyFrame(0);
-				if (strstr(seq->name, "L Toe"))
-					debug("anim %s has toe keyframes\n", hier->name); // , seq->name);
-
-				for(l = 0; l < numFrames; l++, kf++){
+			for(l = 0; l < numFrames; l++){
+				if(hasScale){
 					RwStreamRead(stream, buf, 0x2C);
-					kf->rotation.x = -fbuf[0];
-					kf->rotation.y = -fbuf[1];
-					kf->rotation.z = -fbuf[2];
-					kf->rotation.w = fbuf[3];
-					kf->translation.x = fbuf[4];
-					kf->translation.y = fbuf[5];
-					kf->translation.z = fbuf[6];
-					// scaling ignored
-					kf->deltaTime = fbuf[10];	// absolute time here
+					CQuaternion rot(fbuf[0], fbuf[1], fbuf[2], fbuf[3]);
+					rot.Invert();
+					CVector trans(fbuf[4], fbuf[5], fbuf[6]);
+
+					if(compressHier){
+						KeyFrameTransCompressed *kf = (KeyFrameTransCompressed*)seq->GetKeyFrameCompressed(l);
+						kf->SetRotation(rot);
+						kf->SetTranslation(trans);
+						// scaling ignored
+						kf->SetTime(fbuf[10]);	// absolute time here
+					}else{
+						KeyFrameTrans *kf = (KeyFrameTrans*)seq->GetKeyFrame(l);
+						kf->rotation = rot;
+						kf->translation = trans;
+						// scaling ignored
+						kf->deltaTime = fbuf[10];	// absolute time here
+					}
+				}else if(hasTranslation){
+					RwStreamRead(stream, buf, 0x20);
+					CQuaternion rot(fbuf[0], fbuf[1], fbuf[2], fbuf[3]);
+					rot.Invert();
+					CVector trans(fbuf[4], fbuf[5], fbuf[6]);
+
+					if(compressHier){
+						KeyFrameTransCompressed *kf = (KeyFrameTransCompressed*)seq->GetKeyFrameCompressed(l);
+						kf->SetRotation(rot);
+						kf->SetTranslation(trans);
+						kf->SetTime(fbuf[7]);	// absolute time here
+					}else{
+						KeyFrameTrans *kf = (KeyFrameTrans*)seq->GetKeyFrame(l);
+						kf->rotation = rot;
+						kf->translation = trans;
+						kf->deltaTime = fbuf[7];	// absolute time here
+					}
+				}else{
+					RwStreamRead(stream, buf, 0x14);
+					CQuaternion rot(fbuf[0], fbuf[1], fbuf[2], fbuf[3]);
+					rot.Invert();
+
+					if(compressHier){
+						KeyFrameCompressed *kf = (KeyFrameCompressed*)seq->GetKeyFrameCompressed(l);
+						kf->SetRotation(rot);
+						kf->SetTime(fbuf[4]);	// absolute time here
+					}else{
+						KeyFrame *kf = (KeyFrame*)seq->GetKeyFrame(l);
+						kf->rotation = rot;
+						kf->deltaTime = fbuf[4];	// absolute time here
+					}
 				}
 			}
 		}
 
-		hier->RemoveQuaternionFlips();
-		hier->CalcTotalTime();
+		if(!compressHier){
+			hier->RemoveQuaternionFlips();
+			hier->CalcTotalTime();
+		}
 	}
 	if(animIndex > ms_numAnimations)
 		ms_numAnimations = animIndex;
diff --git a/src/animation/AnimManager.h b/src/animation/AnimManager.h
index a55577b1..8333b865 100644
--- a/src/animation/AnimManager.h
+++ b/src/animation/AnimManager.h
@@ -136,7 +136,7 @@ public:
 	static CAnimBlendAssociation *BlendAnimation(RpClump *clump, AssocGroupId groupId, AnimationId animId, float delta);
 	static void LoadAnimFiles(void);
 	static void LoadAnimFile(const char *filename);
-	static void LoadAnimFile(RwStream *stream, bool compress, char (*somename)[32] = nil);
+	static void LoadAnimFile(RwStream *stream, bool compress, char (*uncompressedAnims)[32] = nil);
 	static void CreateAnimAssocGroups(void);
 	static void RemoveLastAnimFile(void);
 	static CAnimBlendAssocGroup* GetAnimAssocGroups(void) { return ms_aAnimAssocGroups; }
diff --git a/src/animation/CutsceneMgr.cpp b/src/animation/CutsceneMgr.cpp
index 64951a87..b312236b 100644
--- a/src/animation/CutsceneMgr.cpp
+++ b/src/animation/CutsceneMgr.cpp
@@ -291,12 +291,15 @@ CCutsceneMgr::SetupCutsceneToStart(void)
 			if (ms_pCutsceneObjects[i]->m_pAttachTo != nil) {
 				pAnimBlendAssoc->flags &= (~ASSOC_HAS_TRANSLATION);
 			} else {
-				KeyFrameTrans* keyFrames;
-				if (pAnimBlendAssoc->hierarchy->IsCompressed())
-					keyFrames = ((KeyFrameTrans*)pAnimBlendAssoc->hierarchy->sequences[0].GetKeyFrameCompressed(0));
-				else
-					keyFrames = ((KeyFrameTrans*)pAnimBlendAssoc->hierarchy->sequences[0].GetKeyFrame(0));
-				ms_pCutsceneObjects[i]->SetPosition(ms_cutsceneOffset + keyFrames->translation);
+				if (pAnimBlendAssoc->hierarchy->IsCompressed()){
+					KeyFrameTransCompressed *keyFrames = ((KeyFrameTransCompressed*)pAnimBlendAssoc->hierarchy->sequences[0].GetKeyFrameCompressed(0));
+					CVector trans;
+					keyFrames->GetTranslation(&trans);
+					ms_pCutsceneObjects[i]->SetPosition(ms_cutsceneOffset + trans);
+				}else{
+					KeyFrameTrans *keyFrames = ((KeyFrameTrans*)pAnimBlendAssoc->hierarchy->sequences[0].GetKeyFrame(0));
+					ms_pCutsceneObjects[i]->SetPosition(ms_cutsceneOffset + keyFrames->translation);
+				}
 			}
 			pAnimBlendAssoc->SetRun();
 		} else {
@@ -331,7 +334,7 @@ CCutsceneMgr::SetCutsceneAnim(const char *animName, CObject *pObject)
 	}
 
 	if (pNewAnim->hierarchy->IsCompressed())
-		pNewAnim->hierarchy->compressed2 = true;
+		pNewAnim->hierarchy->keepCompressed = true;
 
 	CStreaming::ImGonnaUseStreamingMemory();
 	pNewAnim = ms_cutsceneAssociations.CopyAnimation(animName);
@@ -344,8 +347,8 @@ CCutsceneMgr::SetCutsceneAnim(const char *animName, CObject *pObject)
 	pAnimBlendClumpData = *RPANIMBLENDCLUMPDATA(pObject->m_rwObject);
 	pAnimBlendClumpData->link.Prepend(&pNewAnim->link);
 
-	if (pNewAnim->hierarchy->compressed2)
-		pAnimBlendClumpData->frames->flag |= AnimBlendFrameData::UNK_COMPRESSED;
+	if (pNewAnim->hierarchy->keepCompressed)
+		pAnimBlendClumpData->frames->flag |= AnimBlendFrameData::COMPRESSED;
 }
 
 void
diff --git a/src/animation/FrameUpdate.cpp b/src/animation/FrameUpdate.cpp
index faeea709..a085fd4b 100644
--- a/src/animation/FrameUpdate.cpp
+++ b/src/animation/FrameUpdate.cpp
@@ -19,6 +19,9 @@ void FrameUpdateCallBackSkinned(AnimBlendFrameData *frame, void *arg);
 void FrameUpdateCallBackWithVelocityExtractionSkinned(AnimBlendFrameData *frame, void *arg);
 void FrameUpdateCallBackWith3dVelocityExtractionSkinned(AnimBlendFrameData *frame, void *arg);
 
+void FrameUpdateCallBackNonSkinnedCompressed(AnimBlendFrameData *frame, void *arg);
+void FrameUpdateCallBackSkinnedCompressed(AnimBlendFrameData *frame, void *arg);
+
 void
 FrameUpdateCallBackNonSkinned(AnimBlendFrameData *frame, void *arg)
 {
@@ -446,3 +449,222 @@ FrameUpdateCallBackOffscreen(AnimBlendFrameData *frame, void *arg)
 	if(frame->flag & AnimBlendFrameData::VELOCITY_EXTRACTION && gpAnimBlendClump->velocity)
 		FrameUpdateCallBackWithVelocityExtractionSkinned(frame, arg);
 }
+
+
+void
+FrameUpdateCallBackNonSkinnedCompressed(AnimBlendFrameData *frame, void *arg)
+{
+	CVector vec, pos(0.0f, 0.0f, 0.0f);
+	CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f);
+	float totalBlendAmount = 0.0f;
+	CVector trans(0.0f, 0.0f, 0.0f);
+	CVector cur(0.0f, 0.0f, 0.0f);
+	CVector end(0.0f, 0.0f, 0.0f);
+	bool looped = false;
+	RwMatrix *mat = RwFrameGetMatrix(frame->frame);
+	CAnimBlendNode **node;
+	AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg;
+
+	if(frame->flag & AnimBlendFrameData::VELOCITY_EXTRACTION &&
+	   gpAnimBlendClump->velocity){
+		if(updateData->foobar)
+			for(node = updateData->nodes; *node; node++)
+				if((*node)->sequence && (*node)->association->IsPartial())
+					totalBlendAmount += (*node)->association->blendAmount;
+
+		for(node = updateData->nodes; *node; node++)
+			if((*node)->sequence && (*node)->sequence->HasTranslation()){
+				if((*node)->association->HasTranslation()){
+					(*node)->GetCurrentTranslationCompressed(vec, 1.0f-totalBlendAmount);
+					cur += vec;
+				}
+			}
+
+		for(node = updateData->nodes; *node; node++){
+			if((*node)->sequence){
+				bool nodelooped = (*node)->UpdateCompressed(vec, q, 1.0f-totalBlendAmount);
+#ifdef FIX_BUGS
+				if(DotProduct(rot, q) < 0.0f)
+					rot -= q;
+				else
+#endif
+					rot += q;
+				if((*node)->sequence->HasTranslation()){
+					pos += vec;
+					if((*node)->association->HasTranslation()){
+						trans += vec;
+						looped |= nodelooped;
+						if(nodelooped){
+							(*node)->GetEndTranslationCompressed(vec, 1.0f-totalBlendAmount);
+							end += vec;
+						}
+					}
+				}
+			}
+			++*node;
+		}
+
+		if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){
+			RwMatrixSetIdentity(mat);
+			rot.Normalise();
+			rot.Get(mat);
+		}
+
+		if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){
+			*gpAnimBlendClump->velocity = trans - cur;
+			if(looped)
+				*gpAnimBlendClump->velocity += end;
+			mat->pos.x = (pos - trans).x + frame->resetPos.x;
+			mat->pos.y = (pos - trans).y + frame->resetPos.y;
+			mat->pos.z = (pos - trans).z + frame->resetPos.z;
+		}
+		RwMatrixUpdate(mat);
+	}else{
+		if(updateData->foobar)
+			for(node = updateData->nodes; *node; node++)
+				if((*node)->sequence && (*node)->association->IsPartial())
+					totalBlendAmount += (*node)->association->blendAmount;
+
+		for(node = updateData->nodes; *node; node++){
+			if((*node)->sequence){
+				(*node)->UpdateCompressed(vec, q, 1.0f-totalBlendAmount);
+				if((*node)->sequence->HasTranslation())
+					pos += vec;
+#ifdef FIX_BUGS
+				if(DotProduct(rot, q) < 0.0f)
+					rot -= q;
+				else
+#endif
+					rot += q;
+			}
+			++*node;
+		}
+
+		if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){
+			RwMatrixSetIdentity(mat);
+			rot.Normalise();
+			rot.Get(mat);
+		}
+
+		if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){
+			mat->pos.x = pos.x;
+			mat->pos.y = pos.y;
+			mat->pos.z = pos.z;
+			mat->pos.x += frame->resetPos.x;
+			mat->pos.y += frame->resetPos.y;
+			mat->pos.z += frame->resetPos.z;
+		}
+		RwMatrixUpdate(mat);
+	}
+}
+
+void
+FrameUpdateCallBackSkinnedCompressed(AnimBlendFrameData *frame, void *arg)
+{
+	CVector vec, pos(0.0f, 0.0f, 0.0f);
+	CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f);
+	float totalBlendAmount = 0.0f;
+	CVector trans(0.0f, 0.0f, 0.0f);
+	CVector cur(0.0f, 0.0f, 0.0f);
+	CVector end(0.0f, 0.0f, 0.0f);
+	bool looped = false;
+	RpHAnimStdInterpFrame *xform = frame->hanimFrame;
+	CAnimBlendNode **node;
+	AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg;
+
+	if(frame->flag & AnimBlendFrameData::VELOCITY_EXTRACTION &&
+	   gpAnimBlendClump->velocity){
+		if(updateData->foobar)
+			for(node = updateData->nodes; *node; node++)
+				if((*node)->sequence && (*node)->association->IsPartial())
+					totalBlendAmount += (*node)->association->blendAmount;
+
+		for(node = updateData->nodes; *node; node++)
+			if((*node)->sequence && (*node)->sequence->HasTranslation()){
+				if((*node)->association->HasTranslation()){
+					(*node)->GetCurrentTranslationCompressed(vec, 1.0f-totalBlendAmount);
+					cur += vec;
+				}
+			}
+
+		for(node = updateData->nodes; *node; node++){
+			if((*node)->sequence){
+				bool nodelooped = (*node)->UpdateCompressed(vec, q, 1.0f-totalBlendAmount);
+#ifdef FIX_BUGS
+				if(DotProduct(rot, q) < 0.0f)
+					rot -= q;
+				else
+#endif
+					rot += q;
+				if((*node)->sequence->HasTranslation()){
+					pos += vec;
+					if((*node)->association->HasTranslation()){
+						trans += vec;
+						looped |= nodelooped;
+						if(nodelooped){
+							(*node)->GetEndTranslationCompressed(vec, 1.0f-totalBlendAmount);
+							end += vec;
+						}
+					}
+				}
+			}
+			++*node;
+		}
+
+		if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){
+			rot.Normalise();
+			xform->q.imag.x = rot.x;
+			xform->q.imag.y = rot.y;
+			xform->q.imag.z = rot.z;
+			xform->q.real = rot.w;
+		}
+
+		if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){
+			*gpAnimBlendClump->velocity = trans - cur;
+			if(looped)
+				*gpAnimBlendClump->velocity += end;
+			xform->t.x = (pos - trans).x + frame->resetPos.x;
+			xform->t.y = (pos - trans).y + frame->resetPos.y;
+			xform->t.z = (pos - trans).z + frame->resetPos.z;
+		}
+	}else{
+		float transBlendAmount = 0.0f;
+
+		if(updateData->foobar)
+			for(node = updateData->nodes; *node; node++)
+				if((*node)->sequence && (*node)->association->IsPartial())
+					totalBlendAmount += (*node)->association->blendAmount;
+
+		for(node = updateData->nodes; *node; node++){
+			if((*node)->sequence){
+				(*node)->UpdateCompressed(vec, q, 1.0f-totalBlendAmount);
+				if((*node)->sequence->HasTranslation()){
+					pos += vec;
+					transBlendAmount += (*node)->association->blendAmount;
+				}
+				if(DotProduct(rot, q) < 0.0f)
+					rot -= q;
+				else
+					rot += q;
+			}
+			++*node;
+		}
+
+		if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){
+			rot.Normalise();
+			xform->q.imag.x = rot.x;
+			xform->q.imag.y = rot.y;
+			xform->q.imag.z = rot.z;
+			xform->q.real = rot.w;
+		}
+
+		if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){
+			xform->t.x = transBlendAmount*pos.x;
+			xform->t.y = transBlendAmount*pos.y;
+			xform->t.z = transBlendAmount*pos.z;
+			xform->t.x += (1.0f-transBlendAmount)*frame->resetPos.x;
+			xform->t.y += (1.0f-transBlendAmount)*frame->resetPos.y;
+			xform->t.z += (1.0f-transBlendAmount)*frame->resetPos.z;
+		}
+	}
+}
diff --git a/src/animation/RpAnimBlend.cpp b/src/animation/RpAnimBlend.cpp
index e93615b7..c4103dbf 100644
--- a/src/animation/RpAnimBlend.cpp
+++ b/src/animation/RpAnimBlend.cpp
@@ -8,6 +8,7 @@
 #include "AnimBlendClumpData.h"
 #include "AnimBlendHierarchy.h"
 #include "AnimBlendAssociation.h"
+#include "AnimManager.h"
 #include "RpAnimBlend.h"
 #include "PedModelInfo.h"
 
@@ -443,6 +444,9 @@ RpAnimBlendNodeUpdateKeyframes(AnimBlendFrameData *frames, AnimBlendFrameUpdateD
 	}
 }
 
+// TODO:
+// CAnimBlendClumpData::LoadFramesIntoSPR
+// CAnimBlendClumpData::ForAllFramesInSPR
 void
 RpAnimBlendClumpUpdateAnimations(RpClump *clump, float timeDelta, bool doRender)
 {
@@ -466,7 +470,7 @@ RpAnimBlendClumpUpdateAnimations(RpClump *clump, float timeDelta, bool doRender)
 		assoc = CAnimBlendAssociation::FromLink(link);
 		if(assoc->UpdateBlend(timeDelta)){
 			if(assoc->hierarchy->sequences){
-				//CAnimManager::UncompressAnimation(v6->hierarchy)
+				CAnimManager::UncompressAnimation(assoc->hierarchy);
 				if(i < 11)
 					updateData.nodes[i++] = assoc->GetNode(0);
 				if(assoc->flags & ASSOC_MOVEMENT){
@@ -486,6 +490,14 @@ RpAnimBlendClumpUpdateAnimations(RpClump *clump, float timeDelta, bool doRender)
 
 	updateData.nodes[i] = nil;
 
+#ifdef ANIM_COMPRESSION
+	if(clumpData->frames[0].flag & AnimBlendFrameData::COMPRESSED){
+		if(IsClumpSkinned(clump))
+			clumpData->ForAllFrames(FrameUpdateCallBackSkinnedCompressed, &updateData);
+		else
+			clumpData->ForAllFrames(FrameUpdateCallBackNonSkinnedCompressed, &updateData);
+	}else
+#endif
 	if(doRender){
 		if(clumpData->frames[0].flag & AnimBlendFrameData::UPDATE_KEYFRAMES)
 			RpAnimBlendNodeUpdateKeyframes(clumpData->frames, &updateData, clumpData->numFrames);
diff --git a/src/animation/RpAnimBlend.h b/src/animation/RpAnimBlend.h
index d0f7a114..6e9e0f0f 100644
--- a/src/animation/RpAnimBlend.h
+++ b/src/animation/RpAnimBlend.h
@@ -43,3 +43,6 @@ extern CAnimBlendClumpData *gpAnimBlendClump;
 void FrameUpdateCallBackNonSkinned(AnimBlendFrameData *frame, void *arg);
 void FrameUpdateCallBackSkinned(AnimBlendFrameData *frame, void *arg);
 void FrameUpdateCallBackOffscreen(AnimBlendFrameData *frame, void *arg);
+
+void FrameUpdateCallBackNonSkinnedCompressed(AnimBlendFrameData *frame, void *arg);
+void FrameUpdateCallBackSkinnedCompressed(AnimBlendFrameData *frame, void *arg);
diff --git a/src/core/config.h b/src/core/config.h
index c46adcd8..7e7292a3 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -192,6 +192,11 @@ enum Config {
 // those infamous texts
 #define DRAW_GAME_VERSION_TEXT
 
+// Memory allocation and compression
+// #define USE_CUSTOM_ALLOCATOR		// use CMemoryHeap for allocation. use with care, not finished yet
+//#define COMPRESSED_COL_VECTORS	// use compressed vectors for collision vertices
+//#define ANIM_COMPRESSION	// only keep most recently used anims uncompressed
+
 #if defined GTA_PS2
 #	define GTA_PS2_STUFF
 #	define RANDOMSPLASH
diff --git a/src/math/Quaternion.h b/src/math/Quaternion.h
index a5a34626..47c94f7c 100644
--- a/src/math/Quaternion.h
+++ b/src/math/Quaternion.h
@@ -12,6 +12,11 @@ public:
 	float MagnitudeSqr(void) const { return x*x + y*y + z*z + w*w; }
 	void Normalise(void);
 	void Multiply(const CQuaternion &q1, const CQuaternion &q2);
+	void Invert(void){	// Conjugate would have been a better name
+		x = -x;
+		y = -y;
+		z = -z;
+	}
 
 	const CQuaternion &operator+=(CQuaternion const &right) {
 		x += right.x;
diff --git a/src/rw/MemoryHeap.cpp b/src/rw/MemoryHeap.cpp
index 45dd1fc2..285a7c70 100644
--- a/src/rw/MemoryHeap.cpp
+++ b/src/rw/MemoryHeap.cpp
@@ -341,7 +341,7 @@ CMemoryHeap::TidyHeap(void)
 	}
 }
 
-// 
+// MIAMI: this is empty
 void
 CMemoryHeap::RegisterMemPointer(void *ptr)
 {