public static ShaderProgramChunk Parse(BytecodeReader reader)
		{
			var program = new ShaderProgramChunk
			{
				Version = ShaderVersion.ParseShex(reader),

				// Length Token (LenTok)
				// Always follows VerTok
				// [31:00] Unsigned integer count of number of DWORDs in program code, including version and length tokens.
				// So the minimum value is 0x00000002 (if an empty program is ever valid).
				Length = reader.ReadUInt32()
			};

			while (!reader.EndOfBuffer)
			{
				// Opcode Format (OpcodeToken0)
				//
				// [10:00] D3D10_SB_OPCODE_TYPE
				// if( [10:00] == D3D10_SB_OPCODE_CUSTOMDATA )
				// {
				//    Token starts a custom-data block.  See "Custom-Data Block Format".
				// }
				// else // standard opcode token
				// {
				//    [23:11] Opcode-Specific Controls
				//    [30:24] Instruction length in DWORDs including the opcode token.
				//    [31]    0 normally. 1 if extended operand definition, meaning next DWORD
				//            contains extended opcode token.
				// }
				var opcodeHeaderReader = reader.CopyAtCurrentPosition();
				var opcodeToken0 = opcodeHeaderReader.ReadUInt32();
				var opcodeHeader = new OpcodeHeader
				{
					OpcodeType = opcodeToken0.DecodeValue<OpcodeType>(0, 10),
					Length = opcodeToken0.DecodeValue(24, 30),
					IsExtended = (opcodeToken0.DecodeValue(31, 31) == 1)
				};

				OpcodeToken opcodeToken;
				if (opcodeHeader.OpcodeType == OpcodeType.CustomData)
				{
					opcodeToken = CustomDataToken.Parse(reader, opcodeToken0);
				}
				else if (opcodeHeader.OpcodeType.IsDeclaration())
				{
					opcodeToken = DeclarationToken.Parse(reader, opcodeHeader.OpcodeType);
				}
				else // Not custom data or declaration, so must be instruction.
				{
					opcodeToken = InstructionToken.Parse(reader, opcodeHeader);
				}

				opcodeToken.Header = opcodeHeader;
				program.Tokens.Add(opcodeToken);
			}

			program.LinkControlFlowInstructions();

			return program;
		}
		public static ResourceDefinitionChunk Parse(BytecodeReader reader)
		{
			var headerReader = reader.CopyAtCurrentPosition();

			uint constantBufferCount = headerReader.ReadUInt32();
			uint constantBufferOffset = headerReader.ReadUInt32();
			uint resourceBindingCount = headerReader.ReadUInt32();
			uint resourceBindingOffset = headerReader.ReadUInt32();
			var target = ShaderVersion.ParseRdef(headerReader);
			uint flags = headerReader.ReadUInt32();

			var creatorOffset = headerReader.ReadUInt32();
			var creatorReader = reader.CopyAtOffset((int) creatorOffset);
			var creator = creatorReader.ReadString();

			var result = new ResourceDefinitionChunk
			{
				Target = target,
				Flags = (ShaderFlags) flags,
				Creator = creator
			};

			if (target.MajorVersion >= 5)
			{
				string rd11 = headerReader.ReadUInt32().ToFourCcString();
				if (rd11 != "RD11")
					throw new ParseException("Expected RD11.");

				var unknown1 = headerReader.ReadUInt32(); // TODO
				Debug.Assert(unknown1 == 60);

				var unknown2 = headerReader.ReadUInt32();
				Debug.Assert(unknown2 == 24);

				var unknown3 = headerReader.ReadUInt32();
				Debug.Assert(unknown3 == 32);

				var unknown4 = headerReader.ReadUInt32();
				Debug.Assert(unknown4 == 40);

				var unknown5 = headerReader.ReadUInt32();
				Debug.Assert(unknown5 == 36);

				var unknown6 = headerReader.ReadUInt32();
				Debug.Assert(unknown6 == 12);

				result.InterfaceSlotCount = headerReader.ReadUInt32();
			}

			var constantBufferReader = reader.CopyAtOffset((int) constantBufferOffset);
			for (int i = 0; i < constantBufferCount; i++)
				result.ConstantBuffers.Add(ConstantBuffer.Parse(reader, constantBufferReader, result.Target));

			var resourceBindingReader = reader.CopyAtOffset((int) resourceBindingOffset);
			for (int i = 0; i < resourceBindingCount; i++)
				result.ResourceBindings.Add(ResourceBinding.Parse(reader, resourceBindingReader));

			return result;
		}
		public static InterfacesChunk Parse(BytecodeReader reader, uint sizeInBytes)
		{
			var headerReader = reader.CopyAtCurrentPosition();

			var result = new InterfacesChunk();

			var classInstanceCount = headerReader.ReadUInt32();
			var classTypeCount = headerReader.ReadUInt32();
			var interfaceSlotRecordCount = headerReader.ReadUInt32();

			// Will be same as interfaceSlotRecordCount unless there are interface arrays.
			result.InterfaceSlotCount = headerReader.ReadUInt32();

			headerReader.ReadUInt32(); // Think this is offset to start of interface slot info, but we don't need it.

			var classTypeOffset = headerReader.ReadUInt32();
			var availableClassReader = reader.CopyAtOffset((int) classTypeOffset);

			var interfaceSlotOffset = headerReader.ReadUInt32();
			var interfaceSlotReader = reader.CopyAtOffset((int) interfaceSlotOffset);

			for (uint i = 0; i < classTypeCount; i++)
			{
				var classType = ClassType.Parse(reader, availableClassReader);
				classType.ID = i; // Really??
				result.AvailableClassTypes.Add(classType);
			}

			for (uint i = 0; i < classInstanceCount; i++)
			{
				var classInstance = ClassInstance.Parse(reader, availableClassReader);
				result.AvailableClassInstances.Add(classInstance);
			}

			uint startSlot = 0;
			for (uint i = 0; i < interfaceSlotRecordCount; i++)
			{
				var interfaceSlot = InterfaceSlot.Parse(reader, interfaceSlotReader);
				interfaceSlot.StartSlot = startSlot; // Really??
				result.InterfaceSlots.Add(interfaceSlot);

				startSlot += interfaceSlot.SlotSpan;
			}

			return result;
		}
		public static InputOutputSignatureChunk Parse(BytecodeReader reader, ChunkType chunkType,
			ProgramType programType)
		{
			InputOutputSignatureChunk result;
			switch (chunkType)
			{
				case ChunkType.Isgn :
					result = new InputSignatureChunk();
					break;
				case ChunkType.Osgn :
				case ChunkType.Osg5 :
					result = new OutputSignatureChunk();
					break;
				case ChunkType.Pcsg :
					result = new PatchConstantSignatureChunk();
					break;
				default :
					throw new ArgumentOutOfRangeException("chunkType", "Unrecognised chunk type: " + chunkType);
			}

			var chunkReader = reader.CopyAtCurrentPosition();
			var elementCount = chunkReader.ReadUInt32();
			var uniqueKey = chunkReader.ReadUInt32();

			SignatureElementSize elementSize;
			switch (chunkType)
			{
				case ChunkType.Osg5 :
					elementSize = SignatureElementSize._7;
					break;
				case ChunkType.Isgn:
				case ChunkType.Osgn:
				case ChunkType.Pcsg:
					elementSize = SignatureElementSize._6;
					break;
				default:
					throw new ArgumentOutOfRangeException("chunkType", "Unrecognised chunk type: " + chunkType);
			}

			for (int i = 0; i < elementCount; i++)
				result.Parameters.Add(SignatureParameterDescription.Parse(reader, chunkReader, chunkType, elementSize,
					programType));

			return result;
		}
		public static BytecodeChunk ParseChunk(BytecodeReader chunkReader, BytecodeContainer container)
		{
			// Type of chunk this is.
			uint fourCc = chunkReader.ReadUInt32();

			// Total length of the chunk in bytes.
			uint chunkSize = chunkReader.ReadUInt32();

			ChunkType chunkType;
		    if (KnownChunkTypes.ContainsKey(fourCc))
		    {
		        chunkType = KnownChunkTypes[fourCc];
		    }
		    else
		    {
		        System.Diagnostics.Debug.WriteLine("Chunk type '" + fourCc.ToFourCcString() + "' is not yet supported.");
		        return null;
		    }

		    var chunkContentReader = chunkReader.CopyAtCurrentPosition((int) chunkSize);
			BytecodeChunk chunk;
			switch (chunkType)
			{
				case ChunkType.Ifce :
					chunk = InterfacesChunk.Parse(chunkContentReader, chunkSize);
					break;
				case ChunkType.Isgn :
				case ChunkType.Osgn:
				case ChunkType.Osg5:
				case ChunkType.Pcsg:
					chunk = InputOutputSignatureChunk.Parse(chunkContentReader, chunkType,
						container.ResourceDefinition.Target.ProgramType);
					break;
				case ChunkType.Rdef:
					chunk = ResourceDefinitionChunk.Parse(chunkContentReader);
					break;
				case ChunkType.Sdbg :
				case ChunkType.Spdb :
					chunk = DebuggingChunk.Parse(chunkContentReader, chunkType, (int) chunkSize);
					break;
				case ChunkType.Sfi0:
					chunk = Sfi0Chunk.Parse(chunkContentReader);
					break;
				case ChunkType.Shdr:
				case ChunkType.Shex:
					chunk = ShaderProgramChunk.Parse(chunkContentReader);
					break;
				case ChunkType.Stat:
					chunk = StatisticsChunk.Parse(chunkContentReader, chunkSize);
					break;
				default :
					throw new ParseException("Invalid chunk type: " + chunkType);
			}

			chunk.Container = container;
			chunk.FourCc = fourCc;
			chunk.ChunkSize = chunkSize;
			chunk.ChunkType = chunkType;

			return chunk;
		}