private void ReadSectionDetails(ArchiveSectionMetadataBuilder[] sections) { #region Counts bool hasStreamCounts = false; ArchiveMetadataToken token; for (;;) { token = ReadToken(); if (token == ArchiveMetadataToken.End || token == ArchiveMetadataToken.CRC || token == ArchiveMetadataToken.Size) break; if (token == ArchiveMetadataToken.NumUnpackStream) { hasStreamCounts = true; foreach (var section in sections) section.SubStreamCount = ReadNumberAsInt32(); } else { SkipDataBlock(); } } if (!hasStreamCounts) foreach (var section in sections) section.SubStreamCount = 1; #endregion #region Sizes foreach (var section in sections) { // v3.13 was broken and wrote empty sections // v4.07 added compat code to skip empty sections if (section.SubStreamCount.Value == 0) continue; var remaining = section.OutputLength; var subsections = new DecodedStreamMetadata[section.SubStreamCount.Value]; for (int i = 0; i < subsections.Length - 1; i++) { if (token == ArchiveMetadataToken.Size) { var size = ReadNumberAsInt64(); if (size == 0 || size >= remaining) throw new InvalidDataException(); subsections[i] = new DecodedStreamMetadata(size, null); remaining -= size; } } if (remaining == 0) throw new InvalidDataException(); subsections[subsections.Length - 1] = new DecodedStreamMetadata(remaining, null); section.Subsections = subsections; } if (token == ArchiveMetadataToken.Size) token = ReadToken(); #endregion #region Checksums int requiredChecksumCount = 0; int totalChecksumCount = 0; ImmutableArray<Checksum?>.Builder checksums = null; foreach (var section in sections) { // If there is only one stream and we have a section checksum 7z doesn't store the checksum again. if (!(section.SubStreamCount == 1 && section.OutputChecksum.HasValue)) requiredChecksumCount += section.SubStreamCount.Value; totalChecksumCount += section.SubStreamCount.Value; } for (;;) { if (token == ArchiveMetadataToken.End) { if (checksums == null) { checksums = ImmutableArray.CreateBuilder<Checksum?>(totalChecksumCount); for (int i = 0; i < totalChecksumCount; i++) checksums.Add(null); } break; } else if (token == ArchiveMetadataToken.CRC) { checksums = ImmutableArray.CreateBuilder<Checksum?>(totalChecksumCount); var vector = ReadOptionalBitVector(requiredChecksumCount); int requiredChecksumIndex = 0; foreach (var section in sections) { if (section.SubStreamCount == 1 && section.OutputChecksum.HasValue) { checksums.Add(section.OutputChecksum.Value); } else { for (int i = 0; i < section.SubStreamCount; i++) { if (vector[requiredChecksumIndex++]) checksums.Add(new Checksum(ReadInt32())); else checksums.Add(null); } } } System.Diagnostics.Debug.Assert(requiredChecksumIndex == requiredChecksumCount); System.Diagnostics.Debug.Assert(checksums.Count == totalChecksumCount); } else { SkipDataBlock(); } token = ReadToken(); } #endregion }
private ArchiveSectionMetadataBuilder ReadSection(int rawInputStreamIndex) { var section = new ArchiveSectionMetadataBuilder(); int totalInputCount = 0; int totalOutputCount = 0; var decoderCount = ReadNumberAsInt32(); if (decoderCount == 0) throw new InvalidDataException(); section.Decoders = new DecoderMetadataBuilder[decoderCount]; for (int i = 0; i < decoderCount; i++) { var decoder = ReadDecoder(); totalInputCount += decoder.InputCount; totalOutputCount += decoder.OutputCount; section.Decoders[i] = decoder; } // One output is the final output, the others need to be wired up. var usedOutputMask = new bool[totalOutputCount]; for (int i = 1; i < totalOutputCount; i++) { int inputIndex = ReadNumberAsInt32(); int inputDecoderIndex = 0; for (;;) { if (inputDecoderIndex == decoderCount) throw new InvalidDataException(); if (inputIndex < section.Decoders[inputDecoderIndex].InputCount) break; inputIndex -= section.Decoders[inputDecoderIndex].OutputCount; inputDecoderIndex += 1; } int outputIndex = ReadNumberAsInt32(); // Detect duplicate output connections through the output mask. if (outputIndex >= totalOutputCount || usedOutputMask[outputIndex]) throw new InvalidDataException(); usedOutputMask[outputIndex] = true; // Separate the output index into decoder index and stream index int outputDecoderIndex = 0; for (;;) { if (outputDecoderIndex == decoderCount) throw new InvalidDataException(); var outputCount = section.Decoders[outputDecoderIndex].OutputCount; if (outputIndex < outputCount) break; outputIndex -= outputCount; outputDecoderIndex += 1; } // Detect duplicate input connections by checking for the placeholder. if (section.Decoders[inputDecoderIndex].InputInfo[inputIndex].StreamIndex != Int32.MaxValue) throw new InvalidDataException(); section.Decoders[inputDecoderIndex].InputInfo[inputIndex] = new DecoderInputMetadata(outputDecoderIndex, outputIndex); } bool foundFinalOutput = false; for (int finalOutputIndex = 0, i = 0; i < decoderCount; i++) { var decoder = section.Decoders[i]; for (int j = 0; j < decoder.OutputCount; j++) { if (!usedOutputMask[finalOutputIndex++]) { if (foundFinalOutput) throw new InvalidDataException(); foundFinalOutput = true; section.OutputStream = new DecoderInputMetadata(i, j); } } } if (!foundFinalOutput) throw new InvalidDataException(); // Outputs must be wired up to unique inputs. Inputs which are not wired to outputs must be wired to raw streams. // Note that negative overflow is not possible and positive overflow is ok in this calculation, // it will fail the range check and trigger the exception, which is the intended behavior. var requiredRawStreams = 1 + totalInputCount - totalOutputCount; if (requiredRawStreams <= 0) throw new InvalidDataException(); section.RequiredRawInputStreamCount = requiredRawStreams; if (requiredRawStreams == 1) { bool connected = false; foreach (var decoder in section.Decoders) { for (int i = 0; i < decoder.InputCount; i++) { if (decoder.InputInfo[i].StreamIndex == Int32.MaxValue) { if (connected) throw new InvalidDataException(); connected = true; decoder.InputInfo[i] = new DecoderInputMetadata(null, 0); } } } if (!connected) throw new InvalidDataException(); } else { for (int i = 0; i < requiredRawStreams; i++) { int inputIndex = ReadNumberAsInt32(); int inputDecoderIndex = 0; for (;;) { if (inputDecoderIndex == decoderCount) throw new InvalidDataException(); var decoder = section.Decoders[inputDecoderIndex]; if (inputIndex < decoder.InputCount) break; inputIndex -= decoder.OutputCount; inputDecoderIndex += 1; } var decoderInput = section.Decoders[inputDecoderIndex].InputInfo; if (decoderInput[inputIndex].StreamIndex != Int32.MaxValue) throw new InvalidDataException(); decoderInput[inputIndex] = new DecoderInputMetadata(null, rawInputStreamIndex + i); } } return section; }
private ArchiveSectionMetadataBuilder[] ReadSectionHeader(ImmutableArray<Stream> streams) { SkipToToken(ArchiveMetadataToken.Folder); var sectionCount = ReadNumberAsInt32(); var sections = new ArchiveSectionMetadataBuilder[sectionCount]; int rawStreamCount = 0; using (SelectStream(streams)) { for (int i = 0; i < sectionCount; i++) { var section = ReadSection(rawStreamCount); rawStreamCount += section.RequiredRawInputStreamCount; sections[i] = section; } } SkipToToken(ArchiveMetadataToken.CodersUnpackSize); foreach (var section in sections) { for (int decoderIndex = 0; decoderIndex < section.Decoders.Length; decoderIndex++) { var decoder = section.Decoders[decoderIndex]; var outputCount = decoder.OutputCount; var outputBuilder = ImmutableArray.CreateBuilder<DecoderOutputMetadata>(outputCount); for (int outputIndex = 0; outputIndex < outputCount; outputIndex++) { var length = ReadNumberAsInt64(); outputBuilder.Add(new DecoderOutputMetadata(length)); var stream = new DecoderInputMetadata(decoderIndex, outputIndex); if (section.OutputStream == stream) section.OutputLength = length; } decoder.OutputInfo = outputBuilder; } } for (;;) { var token = ReadToken(); if (token == ArchiveMetadataToken.End) break; if (token == ArchiveMetadataToken.CRC) { var vector = ReadOptionalBitVector(sectionCount); for (int i = 0; i < sectionCount; i++) { if (vector[i]) sections[i].OutputChecksum = new Checksum(ReadInt32()); else sections[i].OutputChecksum = null; } } else { SkipDataBlock(); } } return sections; }