diff --git a/CMakeLists.txt b/CMakeLists.txt
index 388c4777..03353834 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -106,6 +106,7 @@ add_library(misc STATIC
   LEGO1/lego/sources/misc/legoimage.cpp
   LEGO1/lego/sources/misc/legostorage.cpp
   LEGO1/lego/sources/misc/legotexture.cpp
+  LEGO1/lego/sources/misc/legotree.cpp
 )
 register_lego1_target(misc)
 set_property(TARGET misc PROPERTY ARCHIVE_OUTPUT_NAME "misc$<$<CONFIG:Debug>:d>")
diff --git a/LEGO1/lego/legoomni/include/legoanimpresenter.h b/LEGO1/lego/legoomni/include/legoanimpresenter.h
index 2ca9c910..594c13ca 100644
--- a/LEGO1/lego/legoomni/include/legoanimpresenter.h
+++ b/LEGO1/lego/legoomni/include/legoanimpresenter.h
@@ -2,6 +2,7 @@
 #define LEGOANIMPRESENTER_H
 
 #include "lego/sources/misc/legostorage.h"
+#include "lego/sources/misc/legotree.h"
 #include "mxgeometry/mxgeometry3d.h"
 #include "mxvideopresenter.h"
 
@@ -75,37 +76,20 @@ protected:
 // SYNTHETIC: LEGO1 0x10068650
 // LegoAnimPresenter::`scalar deleting destructor'
 
-// VTABLE: LEGO1 0x100db768
-// SIZE 0x08
-class LegoAnimClassBase {
-public:
-	LegoAnimClassBase();
-	virtual ~LegoAnimClassBase();
-
-	virtual void VTable0x4(); // vtable+0x04
-	virtual void VTable0x8(); // vtable+0x08
-	virtual void VTable0xc(); // vtable+0x0c
-
-	undefined4 m_unk0x4; // 0x04
-};
-
-// SYNTHETIC: LEGO1 0x10099de0
-// LegoAnimClassBase::`scalar deleting destructor'
-
 // VTABLE: LEGO1 0x100db8d8
 // SIZE 0x18
-class LegoAnimClass : public LegoAnimClassBase {
+class LegoAnimClass : public LegoTree {
 public:
 	LegoAnimClass();
 	virtual ~LegoAnimClass() override;
 
-	virtual void VTable0x8() override;                        // vtable+0x08
-	virtual void VTable0xc() override;                        // vtable+0x0c
-	virtual MxResult VTable0x10(LegoMemory* p_stream, MxS32); // vtable+0x10
+	virtual LegoResult Write(LegoStorage* p_storage) override; // vtable+0x08
+	virtual LegoTreeNodeData* CreateData() override;           // vtable+0x0c
+	virtual MxResult VTable0x10(LegoMemory* p_stream, MxS32);  // vtable+0x10
 
 	inline MxLong GetUnknown0x8() { return m_unk0x8; }
 
-	// private:
+private:
 	MxLong m_unk0x8;      // 0x08
 	undefined4 m_unk0xc;  // 0x0c
 	undefined4 m_unk0x10; // 0x10
diff --git a/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp b/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp
index 6da994ec..be04b444 100644
--- a/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp
+++ b/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp
@@ -8,7 +8,6 @@
 #include "mxvideomanager.h"
 
 DECOMP_SIZE_ASSERT(LegoAnimPresenter, 0xc0)
-DECOMP_SIZE_ASSERT(LegoAnimClassBase, 0x08)
 DECOMP_SIZE_ASSERT(LegoAnimClass, 0x18)
 
 // FUNCTION: LEGO1 0x10068420
@@ -140,12 +139,7 @@ void LegoAnimPresenter::StreamingTickle()
 		m_subscriber->DestroyChunk(chunk);
 	}
 
-	if (m_unk0x95 == 0) {
-		if (m_unk0x64->m_unk0x8 + m_action->GetStartTime() < m_action->GetElapsedTime()) {
-			m_unk0x95 = 1;
-		}
-	}
-	else {
+	if (m_unk0x95) {
 		ProgressTickleState(e_done);
 		if (m_compositePresenter) {
 			if (m_compositePresenter->IsA("LegoAnimMMPresenter")) {
@@ -153,6 +147,11 @@ void LegoAnimPresenter::StreamingTickle()
 			}
 		}
 	}
+	else {
+		if (m_action->GetElapsedTime() > m_unk0x64->GetUnknown0x8() + m_action->GetStartTime()) {
+			m_unk0x95 = 1;
+		}
+	}
 }
 
 // STUB: LEGO1 0x1006b8c0
@@ -200,33 +199,6 @@ void LegoAnimPresenter::EndAction()
 	MxVideoPresenter::EndAction();
 }
 
-// FUNCTION: LEGO1 0x10099dd0
-LegoAnimClassBase::LegoAnimClassBase()
-{
-	m_unk0x4 = 0;
-}
-
-// STUB: LEGO1 0x10099e00
-LegoAnimClassBase::~LegoAnimClassBase()
-{
-	// TODO
-}
-
-// STUB: LEGO1 0x10099e20
-void LegoAnimClassBase::VTable0x4()
-{
-}
-
-// STUB: LEGO1 0x10099e40
-void LegoAnimClassBase::VTable0x8()
-{
-}
-
-// STUB: LEGO1 0x10099f70
-void LegoAnimClassBase::VTable0xc()
-{
-}
-
 // FUNCTION: LEGO1 0x100a0b30
 LegoAnimClass::LegoAnimClass()
 {
@@ -249,13 +221,15 @@ MxResult LegoAnimClass::VTable0x10(LegoMemory* p_stream, MxS32)
 }
 
 // STUB: LEGO1 0x100a0e30
-void LegoAnimClass::VTable0x8()
+LegoResult LegoAnimClass::Write(LegoStorage* p_storage)
 {
 	// TODO
+	return SUCCESS;
 }
 
 // STUB: LEGO1 0x100a1040
-void LegoAnimClass::VTable0xc()
+LegoTreeNodeData* LegoAnimClass::CreateData()
 {
 	// TODO
+	return NULL;
 }
diff --git a/LEGO1/lego/sources/misc/legotree.cpp b/LEGO1/lego/sources/misc/legotree.cpp
new file mode 100644
index 00000000..e7f24513
--- /dev/null
+++ b/LEGO1/lego/sources/misc/legotree.cpp
@@ -0,0 +1,110 @@
+#include "legotree.h"
+
+#include "decomp.h"
+#include "legostorage.h"
+
+DECOMP_SIZE_ASSERT(LegoTreeNodeData, 0x04)
+DECOMP_SIZE_ASSERT(LegoTreeNode, 0x010)
+DECOMP_SIZE_ASSERT(LegoTree, 0x08)
+
+// FUNCTION: LEGO1 0x10099d60
+LegoTreeNode::LegoTreeNode()
+{
+	m_data = NULL;
+	m_numChildren = 0;
+	m_children = NULL;
+}
+
+// FUNCTION: LEGO1 0x10099da0
+LegoTreeNode::~LegoTreeNode()
+{
+	if (m_data) {
+		delete m_data;
+	}
+	if (m_children) {
+		delete[] m_children;
+	}
+}
+
+// FUNCTION: LEGO1 0x10099dd0
+LegoTree::LegoTree()
+{
+	m_root = NULL;
+}
+
+// FUNCTION: LEGO1 0x10099e00
+LegoTree::~LegoTree()
+{
+	if (m_root) {
+		Delete(m_root);
+	}
+}
+
+// FUNCTION: LEGO1 0x10099e20
+LegoResult LegoTree::Read(LegoStorage* p_storage)
+{
+	return Read(p_storage, m_root);
+}
+
+// FUNCTION: LEGO1 0x10099e40
+LegoResult LegoTree::Write(LegoStorage* p_storage)
+{
+	return Write(p_storage, m_root);
+}
+
+// FUNCTION: LEGO1 0x10099e60
+LegoResult LegoTree::Read(LegoStorage* p_storage, LegoTreeNode*& p_node)
+{
+	LegoResult result;
+	p_node = new LegoTreeNode();
+	p_node->SetData(CreateData());
+	if ((result = p_node->GetData()->Read(p_storage)) != SUCCESS) {
+		return result;
+	}
+	LegoU32 numChildren;
+	if ((result = p_storage->Read(&numChildren, sizeof(numChildren))) != SUCCESS) {
+		return result;
+	}
+	if (numChildren) {
+		p_node->SetChildren(new LegoTreeNode*[numChildren]);
+		for (LegoU32 i = 0; i < numChildren; i++) {
+			LegoTreeNode* node;
+			if ((result = Read(p_storage, node)) != SUCCESS) {
+				return result;
+			}
+			p_node->SetNumChildren(p_node->GetNumChildren() + 1);
+			p_node->SetChild(i, node);
+		}
+	}
+	return SUCCESS;
+}
+
+// FUNCTION: LEGO1 0x1009a020
+LegoResult LegoTree::Write(LegoStorage* p_storage, LegoTreeNode* p_node)
+{
+	LegoResult result;
+	if (p_node->GetData()) {
+		if ((result = p_node->GetData()->Write(p_storage)) != SUCCESS) {
+			return result;
+		}
+	}
+	LegoU32 numChildren = p_node->GetNumChildren();
+	if ((result = p_storage->Write(&numChildren, sizeof(numChildren))) != SUCCESS) {
+		return result;
+	}
+	for (LegoU32 i = 0; i < p_node->GetNumChildren(); i++) {
+		if ((result = Write(p_storage, p_node->GetChild(i))) != SUCCESS) {
+			return result;
+		}
+	}
+	return SUCCESS;
+}
+
+// FUNCTION: LEGO1 0x1009a0a0
+void LegoTree::Delete(LegoTreeNode* p_node)
+{
+	for (LegoU32 i = 0; i < p_node->GetNumChildren(); i++) {
+		Delete(p_node->GetChild(i));
+	}
+	delete p_node;
+}
diff --git a/LEGO1/lego/sources/misc/legotree.h b/LEGO1/lego/sources/misc/legotree.h
new file mode 100644
index 00000000..185c178f
--- /dev/null
+++ b/LEGO1/lego/sources/misc/legotree.h
@@ -0,0 +1,77 @@
+#ifndef __LEGOTREE_H
+#define __LEGOTREE_H
+
+#ifdef _DEBUG
+#include <stdio.h>
+#endif
+#include "legotypes.h"
+
+class LegoStorage;
+
+// VTABLE: LEGO1 0x100db778
+// SIZE 0x4
+class LegoTreeNodeData {
+public:
+	LegoTreeNodeData() {}
+	virtual ~LegoTreeNodeData() {}
+
+	// FUNCTION: LEGO1 0x10099fe0
+	virtual LegoResult Read(LegoStorage* p_storage) { return SUCCESS; } // vtable+0x4
+
+	// FUNCTION: LEGO1 0x10099ff0
+	virtual LegoResult Write(LegoStorage* p_storage) { return SUCCESS; } // vtable+0x8
+
+	// SYNTHETIC: LEGO1 0x1009a000
+	// LegoTreeNodeData::`scalar deleting destructor'
+};
+
+// VTABLE: LEGO1 0x100db764
+// SIZE 0x10
+class LegoTreeNode {
+public:
+	LegoTreeNode();
+	virtual ~LegoTreeNode();
+	LegoTreeNodeData* GetData() { return m_data; }
+	void SetData(LegoTreeNodeData* p_data) { m_data = p_data; }
+	LegoU32 GetNumChildren() { return m_numChildren; }
+	void SetNumChildren(LegoU32 p_numChildren) { m_numChildren = p_numChildren; }
+	LegoTreeNode* GetChild(LegoU32 p_i) { return m_children[p_i]; }
+	void SetChild(LegoU32 p_i, LegoTreeNode* p_child) { m_children[p_i] = p_child; }
+	LegoTreeNode** GetChildren() { return m_children; }
+	void SetChildren(LegoTreeNode** p_children) { m_children = p_children; }
+
+	// SYNTHETIC: LEGO1 0x10099d80
+	// LegoTreeNode::`scalar deleting destructor'
+
+protected:
+	LegoTreeNodeData* m_data;  // 0x4
+	LegoU32 m_numChildren;     // 0x8
+	LegoTreeNode** m_children; // 0xc
+};
+
+// VTABLE: LEGO1 0x100db768
+// SIZE 0x8
+class LegoTree {
+public:
+	LegoTree();
+	virtual ~LegoTree();
+	LegoTreeNode* GetRoot() { return m_root; }
+	void SetRoot(LegoTreeNode* p_root) { m_root = p_root; }
+	virtual LegoResult Read(LegoStorage* p_storage);  // vtable+0x4
+	virtual LegoResult Write(LegoStorage* p_storage); // vtable+0x8
+
+	// SYNTHETIC: LEGO1 0x10099de0
+	// LegoTree::`scalar deleting destructor'
+
+protected:
+	LegoResult Read(LegoStorage* p_storage, LegoTreeNode*& p_node);
+	LegoResult Write(LegoStorage* p_storage, LegoTreeNode* p_node);
+	void Delete(LegoTreeNode* p_node);
+
+	// FUNCTION: LEGO1 0x10099f70
+	virtual LegoTreeNodeData* CreateData() { return new LegoTreeNodeData(); } // vtable+0xc
+
+	LegoTreeNode* m_root; // 0x4
+};
+
+#endif // __LEGOTREE_H