function rev(v as uinteger)as uinteger
dim as ubyte ptr p = cast(ubyte ptr,@v)
return p[0] shr 24 or p[1] shr 16 or p[2] shr 8 or p[3]
end function
type chunk
as integer csize,cname
end type
dim as integer l,c
dim as ubyte d() 'buffer
dim as chunk ck
dim as string fl '= "_untitled.png"
input "Enter PNG file : ",fl
if open (fl for binary access read as #1) <> 0 then
?"Unable to open " & fl : sleep : end
end if
l = lof(1)
print fl & " : " & l & " bytes"
print
redim d(l)
get #1,,d(0),8
if *cast(uinteger ptr,@(d(0))) <> &h474E5089 or _
*cast(uinteger ptr,@(d(4))) <> &hA1A0A0D then
?"Invalid PNG Header " : close #1 : end
end if
c += 8 'inc counter
print "Adding Header : 8 bytes"
while not eof(1)
get #1,,ck 'get chunk size and name
dim s as string *4 = *cast(zstring ptr,@ck.cname)
dim as integer lc = rev(ck.csize)
if s = "IDAT" or s = "IHDR" or s = "IEND" then 'you may add more
seek 1,seek(1) - 8 'go back
get #1,,d(c),lc + 12
c += lc + 12
print "Adding " & s & " : " & lc + 12 & " bytes"
else
seek #1,seek(1) + lc + 4 'next chunk
print "Ignoring " & s & " : " & lc + 12 & " bytes"
end if
wend
close #1
redim preserve d(c - 1)
print
print "writing _" & fl
open "_" & fl for binary access write as #1
put #1,,d()
close #1
open "_" & fl for input as #1
print "_" & fl & " : " & lof(1) & " bytes"
close #
print
print "press any key to quit"
sleep
''
don't ever use U/Integer for anything, that depends on a fixed size, as below:
(If the code is supposed to be *portable*, aka: useful for both: FBC 32/64 bit!)
' FBC 32: OK, FBC 64: not OK!
if *cast(uinteger ptr,@(d(0))) <> &h474E5089 or _ ' clearly a 32-bit value
*cast(uinteger ptr,@(d(4))) <> &hA1A0A0D then
?"Invalid PNG Header " : close #1 : end
end if
In this particular instance (ULong), or "one shot" (ULongInt):
If *CPtr(ULongInt Ptr, @(d(0))) <> &h474E5089A1A0A0D Then ' clearly a 64-bit value
Print "Invalid PNG Header! Press a key, to END ";
Close #1 : Sleep : End 1 ' indicate ERROR
End If
The header check is also currently little-endian dependent. It could be made to work in big-endian by using the rev() function (which should work correctly in either endian, but will only actually reverse the byte order in little-endian).
I seem to recall that PNG chunk names use one of the bits to determine whether a chunk is "critical" or not. (Any critical chunks should not be removed without good reason.)
I think in effect all critical chunks have the first letter of their name set to uppercase, so you could check for that. http://www.libpng.org/pub/png/book/chapter08.html
(I'd say it's also usually worth keeping tRNS chunks.)
@jj2007, Thanks for testing, I'll fix the path.
@MrSwiss, Thanks for the correction, I'll try on Linux 64 first.
@counting_pine, Thanks for the link and input about some critical chunks.
I will fix the bug and test on 64 bit OS.
Browsing on Win32 at the moment.