Advertisement
creamygoat

fpicdecode.py

Jan 29th, 2013
359
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.88 KB | None | 0 0
  1. #!/usr/bin/python
  2.  
  3. '''Decodes a paletted image and extracts the encoded file.
  4.  
  5. NAME
  6.  fpicdecode
  7.  
  8. SYNOPSIS
  9.  fpicdecode imagefile [decodedfile]
  10.  
  11. DESCRIPTION
  12.  The fpidencode script decodes a 256-colur image file and extracts
  13.  the embedded with fpicencode.
  14.  
  15.  This script can be found on Pastebin.
  16.  Encoder: http://pastebin.com/mysKcugD
  17.  Decoder: http://pastebin.com/YKSSX9n7
  18.  
  19. '''
  20.  
  21. import sys
  22. import os.path
  23. from math import *
  24. from PIL import Image
  25. import zlib
  26.  
  27. class Error (Exception):
  28.   pass
  29.  
  30. def rup2(x):
  31.   return (x + 1) & ~1
  32.  
  33. def chunkid(iff, ix):
  34.   if ix > len(iff) - 4:
  35.     raise Error('EOF in IFF chunk ID.')
  36.   return ''.join([chr(x) for x in iff[ix : ix + 4]])
  37.  
  38. def chunkint(iff, ix):
  39.   if ix > len(iff) - 4:
  40.     raise Error('EOF in IFF chunk integer.')
  41.   r = 0
  42.   for i in range(4):
  43.     r += iff[ix + 3 - i] << (8 * i)
  44.   return r
  45.  
  46. def chunkhdr(iff, ix):
  47.   if ix > len(iff) - 8:
  48.     raise Error('Incomplete chunk header.')
  49.   cid = chunkid(iff, ix)
  50.   clen = chunkint(iff, ix + 4)
  51.   tlen = 8 + rup2(clen)
  52.   if ix + tlen > len(iff):
  53.     raise Error('Incomplete chunk data.')
  54.   return (cid, clen)
  55.  
  56. def isdatamodulated(data):
  57.   if chunkid(data, 0) == 'FORM':
  58.     return False
  59.   else:
  60.     fourbytes = bytearray(
  61.       [((data[2 * i] & 15) << 4) + (data[2 * i + 1] & 15) for i in range(4)]
  62.     )
  63.     return chunkid(fourbytes, 0) == 'FORM'
  64.  
  65. def main():
  66.  
  67.   rc = 0 # Return code: 0 = OK
  68.   iszipped = False
  69.  
  70.   # fetch arguments to set infname, outfname (and invoked command name).
  71.   infname = 'source.png'
  72.   outfname = ''
  73.   args = list(sys.argv)
  74.   cn = args[0]
  75.   args = args[1:]
  76.   if len(args) >= 1:
  77.     infname = args[0]
  78.   else:
  79.     print cn + ': A source image filename is expected.'
  80.     rc = 1
  81.   if len(args) >= 2:
  82.     outfname = args[1]
  83.  
  84.   if rc:
  85.     return rc
  86.  
  87.   try:
  88.  
  89.     # Load the image and fetch the encoded data, a custom IFF FORM.
  90.     # An IFF FORM has only four bytes of fixed data, the form type.
  91.     # Standard FORMS include 'ILBM' and '8SVX'. Ours is 'Raw '.
  92.     im = Image.open(infname)
  93.     w, h = im.size
  94.     if w < 8 or h < 4:
  95.       raise Error('The image is far too small.')
  96.     mdata = bytearray(w * h)
  97.     for y in range(h):
  98.       for x in range(w):
  99.         mdata[x + w * y] = im.getpixel((x, y))
  100.     if isdatamodulated(mdata[:8]):
  101.       edata = bytearray(len(mdata) // 2)
  102.       for i in range(len(edata)):
  103.         mh = mdata[2 * i] & 15
  104.         ml = mdata[2 * i + 1] & 15
  105.         edata[i] = (mh << 4) + ml
  106.     else:
  107.       edata = mdata
  108.  
  109.     # Fetch the Raw Data HeaDer ('RDHD'), which has 20 bytes of fixed
  110.     # data, the 'NAME' chunk (a C string for the default output filename)
  111.     # and the 'DATA' chunk, which has the payload, compressed according
  112.     # to the compression method indicated in the first byte of the header.
  113.     hd = None
  114.     nd = None
  115.     cdata = None
  116.     eid, elen = chunkhdr(edata, 0)
  117.     edataend = 8 + elen
  118.     if eid == 'FORM' and elen >= 4:
  119.       fid = chunkid(edata, 8)
  120.       if fid == 'Raw ':
  121.         ix = 12
  122.         while ix + 8 <= edataend:
  123.           cid, clen = chunkhdr(edata, ix)
  124.           chunkdata = bytearray(edata[ix + 8: ix + 8 + clen])
  125.           if cid == 'RDHD':
  126.             hd = chunkdata
  127.           elif cid == 'NAME':
  128.             nd = chunkdata
  129.           elif cid == 'DATA':
  130.             cdata = chunkdata
  131.           ix += 8 + rup2(clen)
  132.     if (hd is None) or (cdata is None):
  133.       raise Error('No valid IFF file is encoded in the image.')
  134.     if len(hd) < 20:
  135.       raise Error('RDHD chunk is incomplete.')
  136.  
  137.     # Decompress the payload, if it is compressed. Though both GIFs
  138.     # and PNGs are typically compressed, the benefit of a compressed
  139.     # payload is compact image dimensions.
  140.     compressionmethod = hd[0]
  141.     if compressionmethod not in [0, 1]:
  142.       raise Error('Unknown compression type indicated in IFF file.')
  143.     iszipped = hd[0] == 1
  144.     if iszipped:
  145.       fdata = bytearray(zlib.decompress(str(cdata)))
  146.     else:
  147.       fdata = cdata
  148.  
  149.     # Protect the user from potentially malicious default output
  150.     # filenames, such as those with paths before the filename or
  151.     # those beginning with a "." (invisible on Unix-like systems).
  152.     if outfname == '':
  153.       fname = ''
  154.       if nd is not None:
  155.         fname = str(nd)
  156.         while fname[-1] == chr(0):
  157.           fname = fname[:-1]
  158.         for i in range(len(fname)):
  159.           if fname[i] < ' ':
  160.             fname = ''
  161.             break;
  162.         fname = os.path.split(fname)[1]
  163.         if fname[0 : 1] == '.':
  164.           fname = 'dot-' + fname[1:]
  165.       outfname = fname
  166.     if outfname == '':
  167.       outfname = 'decoded.dat'
  168.  
  169.     # Save the payload.
  170.     fh = open(outfname, 'wb')
  171.     try:
  172.       fh.write(str(fdata))
  173.     finally:
  174.       fh.close()
  175.  
  176.   except (Error, Exception), E:
  177.     print cn + ': ' + str(E)
  178.     rc = 2
  179.  
  180.   return rc
  181.  
  182. if __name__ == '__main__':
  183.   sys.exit(main())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement