public static Clip ReadClip(ChunkReader reader)
        {
            // index
            var index			= reader.ReadUInt32();

            // first subchunk header
            var type			= reader.ReadID<ClipType>();
            var subchunk_size	= reader.ReadUInt16();
            subchunk_size += (ushort)(subchunk_size & 1);

            Clip clip = null;
            using (var subChunkReader = reader.GetSubChunk(subchunk_size))
            {
                switch (type)
                {
                    case ClipType.ID_STIL:
                    {
                        var tmp_clip	= new ClipStill(index);
                        tmp_clip.Name = subChunkReader.ReadString();
                        clip = tmp_clip;
                        break;
                    }

                    case ClipType.ID_ISEQ:
                    {
                        var tmp_clip	= new ClipSequence(index);
                        tmp_clip.Digits = subChunkReader.ReadUInt8();
                        tmp_clip.Flags	= subChunkReader.ReadUInt8();
                        tmp_clip.Offset = subChunkReader.ReadSInt16();
                        subChunkReader.ReadUInt16();  // Legacy Cruft: Nothing to see here
                        tmp_clip.Start	= subChunkReader.ReadSInt16();
                        tmp_clip.End	= subChunkReader.ReadSInt16();
                        tmp_clip.Prefix = subChunkReader.ReadString();
                        tmp_clip.Suffix = subChunkReader.ReadString();
                        clip = tmp_clip;
                        break;
                    }

                    case ClipType.ID_ANIM:
                    {
                        var tmp_clip	= new ClipAnim(index);
                        tmp_clip.Name	= subChunkReader.ReadString();
                        tmp_clip.Server = subChunkReader.ReadString();
                        tmp_clip.Data	= subChunkReader.ReadBytes((uint)subChunkReader.BytesLeft);
                        clip = tmp_clip;
                        break;
                    }

                    case ClipType.ID_XREF:
                    {
                        var tmp_clip			= new ClipCloned(index);
                        tmp_clip.clip_reference_index	= subChunkReader.ReadUInt32();
                        tmp_clip.Name			= subChunkReader.ReadString();
                        clip = tmp_clip;
                        break;
                    }

                    case ClipType.ID_STCC:
                    {
                        var tmp_clip	= new ClipColorCycle(index);
                        tmp_clip.lo		= subChunkReader.ReadSInt16();
                        tmp_clip.hi		= subChunkReader.ReadSInt16();
                        tmp_clip.Name	= subChunkReader.ReadString();
                        clip = tmp_clip;
                        break;
                    }

                    default:
                        throw new Exception("Unknown Clip type"); // TODO: create proper exception class for this ...
                }
            }

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

                using (var subChunkReader = reader.GetSubChunk(subchunk_size))
                {
                    switch (id)
                    {
                        //case ClipDataType.ID_CLRS: // Color Space RGB   - CLRS { flags[U2], colorspace[U2], filename[FNAM0] }
                        //case ClipDataType.ID_CLRA: // Color Space Alpha - CLRA { flags[U2], colorspace[U2], filename[FNAM0] }
                        //case ClipDataType.ID_FILT: // Image Filtering   - FILT { flags[U2] }
                        //case ClipDataType.ID_DITH: // Image Dithering	  - DITH { flags[U2] }

                        // Contrast - CONT { contrast-delta[FP4], envelope[VX] }
                        case ClipDataType.ID_CONT: { clip.Contrast.value = subChunkReader.ReadSingle(); clip.Contrast.envelope_index = subChunkReader.ReadVariableLengthIndex(); break; }

                        // Brightness - BRIT { brightness-delta[FP4], envelope[VX] }
                        case ClipDataType.ID_BRIT: { clip.Brightness.value = subChunkReader.ReadSingle(); clip.Brightness.envelope_index = subChunkReader.ReadVariableLengthIndex(); break; }

                        // Saturation - SATR { saturation-delta[FP4], envelope[VX] }
                        case ClipDataType.ID_SATR: { clip.Saturation.value = subChunkReader.ReadSingle(); clip.Saturation.envelope_index = subChunkReader.ReadVariableLengthIndex(); break; }

                        // Hue - HUE { hue-rotation[FP4], envelope[VX] }
                        case ClipDataType.ID_HUE: { clip.Hue.value = subChunkReader.ReadSingle(); clip.Hue.envelope_index = subChunkReader.ReadVariableLengthIndex(); break; }

                        // Gamma Correction - GAMM { gamma[F4], envelope[VX] }
                        case ClipDataType.ID_GAMM: { clip.Gamma.value = subChunkReader.ReadSingle(); clip.Gamma.envelope_index = subChunkReader.ReadVariableLengthIndex(); break; }

                        // Negative - NEGA { enable[U2] }
                        case ClipDataType.ID_NEGA: { clip.Negative = subChunkReader.ReadUInt16() != 0; break; }

                        // Time - TIME { start-time[FP4], duration[FP4], frame-rate[FP4] }
                        case ClipDataType.ID_TIME:
                        {
                            clip.StartTime			= subChunkReader.ReadSingle();
                            clip.Duration			= subChunkReader.ReadSingle();
                            clip.FrameRate			= subChunkReader.ReadSingle();
                            break;
                        }

                        // Plug-in Image Filters - IFLT { server-name[S0], flags[U2], data[...] }
                        case ClipDataType.ID_IFLT:

                        // Plug-in Pixel Filters - PFLT { server-name[S0], flags[U2], data[...] }
                        case ClipDataType.ID_PFLT:
                        {
                            var filt = new LightwavePlugin();
                            filt.name	= subChunkReader.ReadString();
                            filt.flags	= subChunkReader.ReadUInt16();
                            filt.data	= subChunkReader.ReadBytes((uint)subChunkReader.BytesLeft);

                            if (id == ClipDataType.ID_IFLT)
                                clip.ImageFilters.Add(filt);
                            else
                                clip.PixelFilters.Add(filt);
                            break;
                        }

                        case ClipDataType.ID_FLAG: // not mentioned in documentation ...
                            var flags = subChunkReader.ReadUInt16(); // unknown what they mean ...
                            break;

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

            return clip;
        }
 //======================================================================
 // AddClip()
 //
 // Add a clip to the clip list.  Used to store the contents of an RIMG or
 // TIMG surface subchunk.
 //======================================================================
 static uint AddClip(string src, List<Clip> clist)
 {
     int pos = src.IndexOf("(sequence)");
     if (pos != -1)
     {
         var clip = new ClipSequence((uint)clist.Count);
         clip.Prefix = src.Substring(pos);
         clip.Digits = 3;
         clist.Add(clip);
         return clip.Index;
     } else
     {
         var clip = new ClipStill((uint)clist.Count);
         clip.Name = src;
         clist.Add(clip);
         return clip.Index;
     }
 }