public static Envelope ReadEnvelope(ChunkReader reader)
        {
            var env = new Envelope(); // allocate the Envelope structure
            env.Index = reader.ReadVariableLengthIndex(); // index

            EnvelopeKey lastKey = null;
            while (reader.BytesLeft > 0)
            {
                // process subchunks as they're encountered
                var id = reader.ReadID<EnvelopeType>();
                var sz = reader.ReadUInt16();
                sz += (ushort)(sz & 1);

                using (var subChunkReader = reader.GetSubChunk(sz))
                {
                    switch (id)
                    {
                        case EnvelopeType.ID_TYPE:
                        {
                            env.Type = subChunkReader.ReadUInt16();
                            break;
                        }

                        case EnvelopeType.ID_NAME:
                        {
                            env.Name = subChunkReader.ReadString();
                            break;
                        }

                        case EnvelopeType.ID_PRE:
                        {
                            env.PreBehavior = (EvalType)subChunkReader.ReadUInt16();
                            break;
                        }

                        case EnvelopeType.ID_POST:
                        {
                            env.PostBehavior = (EvalType)subChunkReader.ReadUInt16();
                            break;
                        }

                        case EnvelopeType.ID_KEY:
                        {
                            lastKey = new EnvelopeKey(
                                                subChunkReader.ReadSingle(),	// time
                                                subChunkReader.ReadSingle()		// value
                                                );
                            env.Keys.Add(lastKey);

                            //TODO: not sort all the time
                            env.Keys.Sort((Comparison<EnvelopeKey>)delegate(EnvelopeKey k1, EnvelopeKey k2)
                            {
                                return k1.Time > k2.Time ? 1 : k1.Time < k2.Time ? -1 : 0;
                            });
                            break;
                        }

                        case EnvelopeType.ID_SPAN:
                        {
                            if (lastKey == null) // We should've encountered an ID_KEY before an ID_SPAN
                                throw new Exception("Key not defined"); //TODO: make proper exception class

                            lastKey.Shape = subChunkReader.ReadID<KeyShape>();
                            switch (lastKey.Shape)
                            {
                                case KeyShape.ID_TCB:
                                {
                                    lastKey.Tension		= subChunkReader.ReadSingle();
                                    lastKey.Continuity	= subChunkReader.ReadSingle();
                                    lastKey.Bias		= subChunkReader.ReadSingle();
                                    break;
                                }

                                case KeyShape.ID_BEZI:
                                case KeyShape.ID_HERM:
                                case KeyShape.ID_BEZ2:
                                {
                                    Array.Clear(lastKey.param, 0, lastKey.param.Length);
                                    int i = 0;
                                    while (i < 4 && subChunkReader.BytesLeft > 0)
                                    {
                                        lastKey.param[i] = subChunkReader.ReadSingle();
                                        i++;
                                    }
                                    break;
                                }

                                case KeyShape.ID_LINE:
                                    break;

                                default:
                                    Console.WriteLine("Unknown envelope span shape type " + reader.GetIDString((uint)lastKey.Shape));
                                    break;
                            }
                            break;
                        }

                        case EnvelopeType.ID_CHAN:
                        {
                            var plug = new LightwavePlugin();
                            plug.name	= subChunkReader.ReadString();
                            plug.flags	= subChunkReader.ReadUInt16();
                            plug.data	= subChunkReader.ReadBytes((uint)reader.BytesLeft);
                            env.ChannelFilters.Add(plug);
                            break;
                        }

                        default:
                            Console.WriteLine("Unknown envelope type " + reader.GetIDString((uint)id));
                            break;
                    }
                }
            }

            return env;
        }
        //======================================================================
        // AddTextureVelocity()
        //
        // Add a triple of envelopes to simulate the old texture velocity
        // parameters.
        //======================================================================
        static uint AddTextureVelocity(float[] pos, float[] vel, List<Envelope> elist)
        {
            Envelope env = null;
            for (var i = 0; i < 3; i++)
            {
                env = new Envelope();
                var key0 = new EnvelopeKey(0,					// time
                                    pos[i]);					// value
                var key1 = new EnvelopeKey(1,					// time
                                     pos[i] + vel[i] * 30.0f);	// value

                key0.Shape = key1.Shape = KeyShape.ID_LINE;

                env.Index = (uint)(elist.Count + 1); // Q: shouldn't this be + 0?
                env.Type = 0x0301 + i;
                env.Name = "Position." + i;
                env.Keys.Add(key0);
                env.Keys.Add(key1);
                env.PreBehavior		= EvalType.BEH_LINEAR;
                env.PostBehavior	= EvalType.BEH_LINEAR;

                elist.Add(env);
            }

            return env.Index - 2; // Q: why the -2?
        }