public static void DecompressSparse(Stream input, Stream output) { SparseHeader sparseHeader = SparseHeader.Read(input); if (sparseHeader == null) { throw new ArgumentException("Invalid Sparse Image Format"); } output.Seek(0, SeekOrigin.Begin); for (uint index = 0; index < sparseHeader.TotalChunks; index++) { ChunkHeader chunkHeader = ChunkHeader.Read(input); long dataLength = (long)chunkHeader.ChunkSize * sparseHeader.BlockSize; switch (chunkHeader.ChunkType) { case ChunkType.Raw: { byte[] data = ReadBytes(input, (int)dataLength); ByteWriter.WriteBytes(output, data); break; } case ChunkType.Fill: { byte[] fillBytes = ReadBytes(input, 4); byte[] data = new byte[dataLength]; for (int offset = 0; offset < data.Length; offset += 4) { ByteWriter.WriteBytes(data, offset, fillBytes); } ByteWriter.WriteBytes(output, data); break; } case ChunkType.DontCare: { output.Seek(dataLength, SeekOrigin.Current); break; } case ChunkType.CRC: { byte[] crcBytes = ReadBytes(input, 4); break; } default: { throw new ArgumentException("Error: Invalid Chunk Type"); } } } input.Close(); }
/// <param name="sparseIndex">one-based index</param> /// <returns>True if the write was complete (i.e. this was the last sparse)</returns> public static bool WriteCompressedSparse(Stream input, Stream output, long maxSparseSize) { // We will write the header later output.Seek(SparseHeader.Length, SeekOrigin.Begin); long currentSparseSize = SparseHeader.Length; uint chunkCount = 0; if (input.Position != 0) { WriteDontCareChunk(output, (uint)(input.Position / BlockSize)); chunkCount++; } MemoryStream rawChunk = new MemoryStream(); byte[] fill = null; int fillCount = 0; // We keep reading as long as we have a room for an additional chunk // (the largest chunk is a raw chunk) while (currentSparseSize + ChunkHeader.Length + BlockSize <= maxSparseSize && input.Position < input.Length) { byte[] block = new byte[BlockSize]; input.Read(block, 0, BlockSize); byte[] currentFill = SparseCompressionHelper.TryToCompressBlock(block); if (fill != null) { if (currentFill != null) { if (ByteUtils.AreByteArraysEqual(fill, currentFill)) { fillCount++; } else { WriteFillChunk(output, fill, (uint)fillCount); chunkCount++; fillCount = 1; fill = currentFill; currentSparseSize += ChunkHeader.Length + 4; } } else { WriteFillChunk(output, fill, (uint)fillCount); chunkCount++; ByteWriter.WriteBytes(rawChunk, block); fill = null; fillCount = 0; currentSparseSize += ChunkHeader.Length + BlockSize; } } else // fill == null { if (currentFill != null) { WriteRawChunk(output, rawChunk); chunkCount++; rawChunk = new MemoryStream(); fillCount = 1; fill = currentFill; currentSparseSize += ChunkHeader.Length + 4; } else { ByteWriter.WriteBytes(rawChunk, block); currentSparseSize += BlockSize; } } } if (rawChunk.Length > 0) { WriteRawChunk(output, rawChunk); chunkCount++; } else { WriteFillChunk(output, fill, (uint)fillCount); chunkCount++; } bool complete = (input.Position == input.Length); if (!complete) { WriteDontCareChunk(output, (uint)((input.Length - input.Position) / BlockSize)); chunkCount++; } output.Seek(0, SeekOrigin.Begin); SparseHeader sparseHeader = new SparseHeader(); sparseHeader.BlockSize = BlockSize; sparseHeader.TotalBlocks = (uint)(input.Length / BlockSize); sparseHeader.TotalChunks = chunkCount; sparseHeader.WriteBytes(output); output.Close(); return(complete); }
private static void PrintSparseImageStatistics(string path) { FileStream stream; try { stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); } catch (IOException) { Console.WriteLine("Cannot open " + path); return; } catch (UnauthorizedAccessException) { Console.WriteLine("Cannot open {0} - Access Denied", path); return; } SparseHeader sparseHeader = SparseHeader.Read(stream); if (sparseHeader == null) { Console.WriteLine("Invalid Sparse Image Format"); return; } Console.WriteLine("Total Blocks: " + sparseHeader.TotalBlocks); Console.WriteLine("Total Chunks: " + sparseHeader.TotalChunks); long outputSize = 0; for (uint index = 0; index < sparseHeader.TotalChunks; index++) { ChunkHeader chunkHeader = ChunkHeader.Read(stream); Console.Write("Chunk type: {0}, size: {1}, total size: {2}", chunkHeader.ChunkType.ToString(), chunkHeader.ChunkSize, chunkHeader.TotalSize); int dataLength = (int)(chunkHeader.ChunkSize * sparseHeader.BlockSize); switch (chunkHeader.ChunkType) { case ChunkType.Raw: { SparseDecompressionHelper.ReadBytes(stream, dataLength); Console.WriteLine(); outputSize += dataLength; break; } case ChunkType.Fill: { byte[] fillBytes = SparseDecompressionHelper.ReadBytes(stream, 4); uint fill = LittleEndianConverter.ToUInt32(fillBytes, 0); Console.WriteLine(", value: 0x{0}", fill.ToString("X8")); outputSize += dataLength; break; } case ChunkType.DontCare: { Console.WriteLine(); break; } case ChunkType.CRC: { byte[] crcBytes = SparseDecompressionHelper.ReadBytes(stream, 4); uint crc = LittleEndianConverter.ToUInt32(crcBytes, 0); Console.WriteLine(", value: 0x{0}", crc.ToString("X8")); break; } default: { Console.WriteLine(); Console.WriteLine("Error: Invalid Chunk Type"); return; } } } stream.Close(); Console.WriteLine("Output size: {0}", outputSize); }