diff --git a/pngchunk/pngchunk.vb b/pngchunk/pngchunk.vb index f357a4e..114fe76 100755 --- a/pngchunk/pngchunk.vb +++ b/pngchunk/pngchunk.vb @@ -24,10 +24,13 @@ If Not FileIO.FileSystem.FileExists(filename) Then Console.WriteLine("File {0} not found!", filename) + Exit Sub End If Dim fs As New IO.FileStream(filename, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read) Dim fi As New IO.FileInfo(filename) + Dim sizedigits As Integer = Math.Ceiling(Math.Log10(fs.Length)) + While Not fs.Position = fs.Length Dim startposition As Long = fs.Position FindHeader(fs) @@ -59,40 +62,54 @@ End If - Dim buf() As Byte + Dim lenbuf(3), nameanddatabuf(), databuf(), crcbuf(3) As Byte Dim len As Integer Dim chunklength As UInt32 Dim chunktype As String = String.Empty While chunktype <> "IEND" AndAlso fs.Position <> fs.Length - ReDim buf(3) - len = fs.Read(buf, 0, 4) + Console.ForegroundColor = ConsoleColor.White + + 'read chunk length + len = fs.Read(lenbuf, 0, 4) If len <> 4 Then Console.WriteLine("Incomplete chunk length header") Exit While End If - 'big endian - chunklength = &H1000000L * buf(0) + &H10000L * buf(1) + &H100L * buf(2) + buf(3) - Console.Write("{0,10}:", chunklength) + 'read and print chunk length and offset in the file + chunklength = BigEndianUInt32(lenbuf) + Console.Write("{0," & sizedigits & "} @ {1,-" & sizedigits & "}:", chunklength, fs.Position - 4) - ReDim buf(chunklength + 3) - len = fs.Read(buf, 0, chunklength + 4) + 'read in the chunk data and type + ReDim nameanddatabuf(chunklength + 3) + ReDim databuf(chunklength - 1) + len = fs.Read(nameanddatabuf, 0, chunklength + 4) + + 'rewind and read only the chunk data + fs.Seek(-chunklength, IO.SeekOrigin.Current) + fs.Read(databuf, 0, chunklength) + + 'check for incomplete reads If len <> chunklength + 4 Then Console.WriteLine("Incomplete chunk data") Exit While End If - chunktype = System.Text.Encoding.ASCII.GetString(buf, 0, 4) - Console.Write(chunktype & " ") - Dim datachecksum As UInt32 = Crc32.ComputeChecksum(buf) - ReDim buf(3) - len = fs.Read(buf, 0, 4) + 'get chunk type as string + chunktype = System.Text.Encoding.ASCII.GetString(nameanddatabuf, 0, 4) + Console.Write(chunktype & " ") + + 'compute checksum of the chunk + Dim datachecksum As UInt32 = Crc32.ComputeChecksum(nameanddatabuf) + + 'read reported checksum and compare it to the file + len = fs.Read(crcbuf, 0, 4) If len <> 4 Then Console.WriteLine("Incomplete CRC data") End If - Dim chunkchecksum As UInt32 = &H1000000L * buf(0) + &H10000L * buf(1) + &H100L * buf(2) + buf(3) + Dim chunkchecksum As UInt32 = BigEndianUInt32(crcbuf) If chunkchecksum = datachecksum Then Console.Write("ok") Else @@ -100,6 +117,10 @@ End If Console.WriteLine() + 'print info for some chunks + Console.ForegroundColor = ConsoleColor.Gray + PrintChunkInfo(chunktype, databuf) + End While If extract Then @@ -110,6 +131,10 @@ End While fs.Close() + +#If CONFIG = "Debug" Then + Console.ReadKey() +#End If End Sub Public Sub FindHeader(fs As IO.FileStream) @@ -128,6 +153,10 @@ End While End Sub + Private Function BigEndianUInt32(ByRef buf As Byte(), Optional offset As Integer = 0) As UInt32 + Return &H1000000L * buf(0 + offset) + &H10000L * buf(1 + offset) + &H100L * buf(2 + offset) + buf(3 + offset) + End Function + Public Sub CopyPart(srcfile As String, destfile As String, offset As Long, bytes As Long) Dim srcfs As New IO.FileStream(srcfile, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read) Dim destfs As New IO.FileStream(destfile, IO.FileMode.Create, IO.FileAccess.Write, IO.FileShare.Read) @@ -139,6 +168,110 @@ destfs.Close() End Sub + Private Sub PrintChunkInfo(chunktype As String, ByRef data As Byte()) + Select Case chunktype + Case "IHDR" + 'image width + Console.WriteLine("width: {0} px", BigEndianUInt32(data, 0)) + + 'image height + Console.WriteLine("height: {0} px", BigEndianUInt32(data, 4)) + + 'bit depth + Dim bitdepth As Byte = data(8) + If {1, 2, 4, 8, 16}.Contains(bitdepth) Then + Console.WriteLine("bit depth: {0} bits/sample", bitdepth) + Else + Console.WriteLine("bit depth: {0} bits/sample INVALID", bitdepth) + End If + + 'color types + Dim colortype As Byte = data(9) + Dim colortypename As String + Select Case colortype + Case 0 + colortypename = "greyscale" + If Not {1, 2, 4, 8, 16}.Contains(bitdepth) Then + colortypename &= ", INVALID BIT DEPTH" + End If + Case 2 + colortypename = "truecolor" + If Not {8, 16}.Contains(bitdepth) Then + colortypename &= ", INVALID BIT DEPTH" + End If + Case 3 + colortypename = "indexed" + If Not {1, 2, 4, 8}.Contains(bitdepth) Then + colortypename &= ", INVALID BIT DEPTH" + End If + Case 4 + colortypename = "greyscale with alpha" + If Not {8, 16}.Contains(bitdepth) Then + colortypename &= ", INVALID BIT DEPTH" + End If + Case 6 + colortypename = "truecolor with alpha" + If Not {8, 16}.Contains(bitdepth) Then + colortypename &= ", INVALID BIT DEPTH" + End If + Case Else + colortypename = "UNKNOWN" + End Select + Console.WriteLine("color type: {0} ({1})", colortype, colortypename) + + 'compression method + If data(10) = 0 Then + Console.WriteLine("compression method: 0 (deflate)") + Else + Console.WriteLine("compression method: {0} (UNKNOWN)", data(10)) + End If + + 'filter method + If data(11) = 0 Then + Console.WriteLine("filter method: 0 (adaptive)") + Else + Console.WriteLine("filter method: {0} (UNKNOWN)", data(11)) + End If + + 'interlace method + If data(12) = 0 Then + Console.WriteLine("interlace method: 0 (no interlace)") + ElseIf data(12) = 1 Then + Console.WriteLine("interlace method: 1 (Adam7)") + Else + Console.WriteLine("interlace method: {0} (UNKNOWN)", data(12)) + End If + + 'end IHDR + + Case "pHYs" + Dim unitsaremetres As Boolean = (data(8) = 1) + If unitsaremetres Then + Console.WriteLine("pixels per unit, x: {0} px ({1:g} ppi)", BigEndianUInt32(data, 0), BigEndianUInt32(data, 0) * 0.0254) + Console.WriteLine("pixels per unit, y: {0} px ({1:g} ppi)", BigEndianUInt32(data, 4), BigEndianUInt32(data, 0) * 0.0254) + Else + Console.WriteLine("pixels per unit, x: {0} px", BigEndianUInt32(data, 0)) + Console.WriteLine("pixels per unit, y: {0} px", BigEndianUInt32(data, 4)) + End If + If data(8) = 0 Then + Console.WriteLine("units: 0 (unknown)") + ElseIf data(8) = 1 Then + Console.WriteLine("units: 1 (metre)") + Else + Console.WriteLine("units: {0} (UNKNOWN)", data(8)) + End If + + Case "tEXt" + Dim keywordlength As Integer = Array.FindIndex(data, Function(x) x = &H0) - 1 + Dim keyword(keywordlength - 1) As Byte + Dim textstring(data.Length - keywordlength - 1 - 1) As Byte + Array.Copy(data, 0, keyword, 0, keywordlength) + Array.Copy(data, keywordlength + 1, textstring, 0, data.Length - keywordlength - 1) + Console.WriteLine("keyword: {0}", System.Text.Encoding.GetEncoding("ISO-8859-1").GetString(keyword)) + Console.WriteLine("value: {0}", System.Text.Encoding.GetEncoding("ISO-8859-1").GetString(textstring)) + End Select + End Sub + Public Class Crc32 Shared table As UInteger()