SHARE
TWEET

Untitled

a guest Feb 17th, 2009 4,288 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import sys
  4. import os, struct, array
  5.  
  6. K_TITLE = "title"
  7. K_CREDITS = "credits"
  8. K_ID = "id"
  9. K_ALBUM = "album"
  10. K_FILENAME = "filename"
  11. K_DESCRIPTION = "description"
  12.  
  13. def strip(text):
  14.     '''
  15.    Strip a string by removing not only whitespaces but also all the final 0x0
  16.  
  17.    @param text: the string to strip
  18.    '''
  19.     pos = text.find(chr(0))
  20.     if pos >= 0:
  21.         text = text[:pos]
  22.     return text.strip()
  23.  
  24. class Data(object):
  25.     """
  26.    A convenient object where attributes can be added dynamically
  27.    """
  28.     pass
  29.  
  30. class Pixmap(object):
  31.     """
  32.    This class is the base object manipulated by these file:
  33.    - an image
  34.    - the JPEG data
  35.    - the info about the image
  36.  
  37.    The image is a Array of bits representing the JPEG data
  38.    The info is a dictionary of key (K_XX), texts
  39.    """
  40.     def __init__(self):
  41.         """
  42.        Constructor
  43.        """
  44.         self.jpeg = array.array('B')
  45.         self.info = {}
  46.        
  47.     def getJPEGSize(self):
  48.         '''
  49.        @return the size of the image directly from the JPEG data
  50.        '''
  51.         idata = iter(self.jpeg)
  52.         width = None
  53.         height = None
  54.         try:
  55.             B1 = idata.next()
  56.             B2 = idata.next()
  57.             if B1 != 0xFF or B2 != 0xD8:
  58.                 return -1, -1
  59.             while True:
  60.                 byte = idata.next()
  61.                 while byte != 0xFF:
  62.                     byte = idata.next()
  63.                 while byte == 0xFF:
  64.                     byte = idata.next()
  65.                 if byte >= 0xc0 and byte <= 0xc3:
  66.                     idata.next()
  67.                     idata.next()
  68.                     idata.next()
  69.                     height, width = struct.unpack('>HH',"".join(chr(idata.next()) for b in range(4)) ) #@UnusedVariable
  70.                     break
  71.                 else:
  72.                     offset = struct.unpack('>H', chr(idata.next()) + chr(idata.next()))[0] - 2
  73.                     for _ in xrange(offset):
  74.                         idata.next()
  75.         except StopIteration:
  76.             pass
  77.         return (width, height)
  78.  
  79. class WB1(object):
  80.     """
  81.    This class allows to read wb1, wbd and wbz files
  82.    """
  83.     MAGIC_WB1_NOT_ENCODED = 0xE0FFD8FF
  84.     MAGIC_WB1_ENCODED = 0x42425757
  85.     MAGIC_WBZ = 0x6791AB43
  86.     FILE_MARKER = 0x1082CDE1
  87.     KEY2K = {
  88.         'STR_ImageTitle' :      K_TITLE,
  89.         'title' :               K_TITLE,
  90.         'STR_ImageCredit' :     K_CREDITS,
  91.         'credit' :              K_CREDITS,
  92.         'STR_CollectionTitle' : K_ALBUM,
  93.         'albumTitle' :          K_ALBUM,
  94.         'filename' :            K_FILENAME,
  95.         'FIL_ImageFileName' :   K_FILENAME,
  96.         'id' :                  K_ID,
  97.         }
  98.  
  99.     @staticmethod
  100.     def read(url, data, buildPixmap = True):
  101.         """
  102.        Read the data and read the image data and info
  103.  
  104.        @param[in] url the url to the file
  105.        @param[in] data the file data stream
  106.        @param[in] buildPixmap if True build the QPixmap object
  107.        @return a Pixmap image
  108.                None if the magic number of the file does not correspond to the handled types of file
  109.        """
  110.         try:
  111.             pixmap = Pixmap()
  112.             data.seek(0)
  113.             magic = struct.unpack('<I', data.read(4))[0]
  114.             if magic == WB1.MAGIC_WB1_NOT_ENCODED:
  115.                 WB1.__decodeWB1(url, data, pixmap, buildPixmap)
  116.             elif magic == WB1.MAGIC_WB1_ENCODED:
  117.                 WB1.__decodeWB1_Encoded(url, data, pixmap, buildPixmap)
  118.             elif magic == WB1.MAGIC_WBZ:
  119.                 WB1.__decodeWBZ(url, data, pixmap, buildPixmap)
  120.             else:
  121.                 return None
  122.             return pixmap
  123.         except:
  124.             return None
  125.     @staticmethod
  126.     def decodeJPEG(data, pixmap, buildPixmap, fileSize = 0):
  127.         """
  128.        Decode the encoded JPEG data
  129.  
  130.        @param[in] data the file data stream, it must be positioned at the beginning of the encoded JPEG data
  131.        @param[in,out] pixmap the Pixmap instance that will contain the image
  132.        @param[in] buildPixmap if True build the QPixmap object
  133.        @param[in] fileSize the size of the JPEG data. If 0, read them to the end of the file.
  134.        """
  135.         code = data.read(4)
  136.         if code == "0000":
  137.             key = 0xA4
  138.         elif code == "1111":
  139.             key = 0xF2
  140.         else:
  141.             raise Exception("Invalid file format")
  142.         if fileSize:
  143.             pixmap.jpeg.fromfile(data, fileSize)
  144.         else:
  145.             try:
  146.                 # read bits until EOF
  147.                 while True:
  148.                     pixmap.jpeg.fromfile(data, 0x4000)
  149.             except EOFError:
  150.                 pass
  151.         # decode the image (NB '~x' returns a negative integer so we use 'x ^ 0xFF' instead)
  152.         for i in xrange(100):
  153.             pixmap.jpeg[i] = (pixmap.jpeg[i + 100] ^ (0xFF ^ pixmap.jpeg[i])) ^ key
  154.         if buildPixmap:
  155.             # check that the data are valid
  156.             if not pixmap.pixmap.loadFromData(pixmap.jpeg.tostring()):
  157.                 raise Exception("Invalid file format")
  158.     @staticmethod
  159.     def readJPEG(data, pixmap, buildPixmap, fileSize = 0):
  160.         """
  161.        Read the JPEG data
  162.  
  163.        @param[in] data the file data stream, it must be positioned at the beginning of the encoded JPEG data
  164.        @param[in,out] pixmap the Pixmap instance that will contain the image
  165.        @param[in] buildPixmap if True build the QPixmap object
  166.        @param[in] fileSize the size of the JPEG data. If 0, read them to the end of the file.
  167.        """
  168.         if fileSize:
  169.             pixmap.jpeg.fromfile(data, fileSize)
  170.         else:
  171.             try:
  172.                 # read bits until EOF
  173.                 while True:
  174.                     pixmap.jpeg.fromfile(data, 0x4000)
  175.             except EOFError:
  176.                 pass
  177.         if buildPixmap:
  178.             # check that the data are valid
  179.             if not pixmap.pixmap.loadFromData(pixmap.jpeg.tostring()):
  180.                 raise Exception("Invalid file format")
  181.     @staticmethod
  182.     def __decodeWB1(url, data, pixmap, buildPixmap):
  183.         """
  184.        Reads the contents of a non encoded WB1 file
  185.  
  186.        @param[in] url the url to the file
  187.        @param[in] data the stream to the file data positioned just after the 4 bytes of the magic number
  188.        @param[in] buildPixmap if True build the QPixmap object
  189.        @param[out] pixmap the resulting array of bytes
  190.        """
  191.         WB1.readJPEG(data, pixmap, buildPixmap)
  192.         pixmap.info.update(PhotosInfo.parse(url).get(url))
  193.         pixmap.info.update(AlbumInfo.parse(url).get())
  194.     @staticmethod
  195.     def __decodeWB1_Encoded(url, data, pixmap, buildPixmap):
  196.         """
  197.        Reads the contents of an encoded WB1 file
  198.  
  199.        @param[in] url the url to the file
  200.        @param[in] data the stream to the file data positioned just after the 4 bytes of the magic number
  201.        @param[in] buildPixmap if True build the QPixmap object
  202.        @param[out] pixmap the resulting array of bytes
  203.        """
  204.         WB1.decodeJPEG(data, pixmap, buildPixmap)
  205.         pixmap.info.update(PhotosInfo.parse(url).get(url))
  206.         pixmap.info.update(AlbumInfo.parse(url).get())
  207.     @staticmethod
  208.     def __decodeWBZ(url, data, pixmap, buildPixmap): #@UnusedVariable
  209.         """
  210.        Reads the contents of a WBZ file
  211.  
  212.        @param[in] url the url to the file
  213.        @param[in] data the stream to the file data positioned just after the 4 bytes of the magic number
  214.        @param[in] buildPixmap if True build the QPixmap object
  215.        @param[out] pixmap the resulting Pixmap
  216.        """
  217.         # JPEG data
  218.         firstFileOffset = struct.unpack('<I', data.read(4))[0]
  219.         data.seek(firstFileOffset)
  220.         includedFileMarker = struct.unpack('<I', data.read(4))[0]
  221.         if includedFileMarker != WB1.FILE_MARKER:
  222.             raise Exception(i18n("Invalid file format"))
  223.         headerSize = struct.unpack('<I', data.read(4))[0]
  224.         data.read(4)
  225.         data.read(256) # fileName
  226.         fileSize = struct.unpack('<I', data.read(4))[0]
  227.         data.read(264)
  228.         code = data.read(4)
  229.         if (code == "WWBB"):
  230.             WB1.decodeJPEG(data, pixmap, buildPixmap, fileSize-8)
  231.         else:
  232.             data.seek(firstFileOffset + headerSize)
  233.             WB1.readJPEG(data, pixmap, buildPixmap, fileSize)
  234.         # description (an INI style file)
  235.         includedFileMarker = struct.unpack('<I', data.read(4))[0]
  236.         if includedFileMarker == WB1.FILE_MARKER:
  237.             headerSize = struct.unpack('<I', data.read(4))[0]
  238.             data.read(4)
  239.             data.read(256) # fileName
  240.             fileSize = struct.unpack('<I', data.read(4))[0]
  241.             data.read(264)
  242.             bytes = data.read(fileSize)
  243.             for line in bytes.splitlines():
  244.                 try:
  245.                     key, val = line.split('=',1)
  246.                     k = WB1.KEY2K.get(key, None)
  247.                     if k:
  248.                         pixmap.info[k] = val
  249.                 except:
  250.                     pass
  251.  
  252. class WBC(object):
  253.     """
  254.    This class allows to reab wbc files
  255.    """
  256.     MAGIC_WBC = 0x95FA16AB
  257.     PB_MARKER  = 0xF071CDE2
  258.  
  259.     @staticmethod
  260.     def read(path, data, buildPixmap = True):
  261.         """
  262.        Read the data and read the image data and info
  263.  
  264.        @param[in] path the path to the file
  265.        @param[in] data the file data stream
  266.        @param[in] buildPixmap if True build the QPixmap objects
  267.        @return (title, pixmaps) where title is the file title and pixmaps is an array of Pixmap
  268.                (None, None) if the magic number of the file does not correspond to the handled types of file
  269.        """
  270.         try:
  271.             pixmaps = []
  272.             title = ""
  273.             data.seek(0, 2)
  274.             data_size = data.tell()
  275.             data.seek(0)
  276.             magic = struct.unpack('<I', data.read(4))[0]
  277.             if magic == WBC.MAGIC_WBC:
  278.                 title = WBC.__decodeWBC(path, data, data_size, pixmaps, buildPixmap)
  279.             else:
  280.                 return (None, None)
  281.             return (title, pixmaps)
  282.         except:
  283.                 return (None, None)
  284.  
  285.     @staticmethod
  286.     def __decodeWBC(path, data, data_size, pixmaps, buildPixmap): #@UnusedVariable
  287.         """
  288.        Reads the contents of a non encoded WB1 file
  289.  
  290.        @param[in] path the path to the file
  291.        @param[in] data the stream to the file data positioned just after the 4 bytes of the magic number
  292.        @param[in] data_size the size of the file
  293.        @param[out] title the file title
  294.        @param[out] pixmaps the resulting array of Pixmap
  295.        @param[in] buildPixmap if True build the QPixmap objects
  296.        @return the title of the collection
  297.        """
  298.         struct.unpack('<I', data.read(4))[0] # firstPBOffset
  299.         data.read(4)
  300.         title = strip(data.read(89))
  301.         data.seek(2196)
  302.         nimages = struct.unpack('<I', data.read(4))[0]
  303.         headers = []
  304.         for i in xrange(nimages): #@UnusedVariable
  305.             hImg = Data()
  306.             hImg.PBOffset = struct.unpack('<I', data.read(4))[0]
  307.             hImg.PBSize = struct.unpack('<I', data.read(4))[0]
  308.             data.read(4)
  309.             hImg.PBAdditionDate = struct.unpack('<I', data.read(4))[0]
  310.             data.read(24)
  311.             headers.append(hImg)
  312.         for h in headers:
  313.             pixmap = Pixmap()
  314.             pixmap.info[K_ALBUM] = title
  315.             # read the header
  316.             data.seek(h.PBOffset)
  317.             PBMarker = struct.unpack('<I', data.read(4))[0]
  318.             headerSize = struct.unpack('<I', data.read(4))[0]
  319.             PBSize = struct.unpack('<I', data.read(4))[0]
  320.             pixmap.info[K_FILENAME] = strip(data.read(256))
  321.             pixmap.info[K_TITLE] = strip(data.read(128))
  322.             pixmap.info[K_DESCRIPTION] = strip(data.read(256))
  323.             pixmap.info[K_CREDITS] = strip(data.read(256))
  324.             data.read(8) # originalFileExtension
  325.             JPGFileSize = struct.unpack('<I', data.read(4))[0]
  326.             data.read(4) # BMPFileSize
  327.             data.read(140)
  328.             data.read(4) # dailyDate
  329.             data.read(4) # additionDate
  330.             data.read(4) # fitToScreen
  331.             pixmap.info[K_ID] = strip(data.read(128))
  332.             data.read(96) # childCategory
  333.             data.read(788) # rootCategory
  334.             # read the image
  335.             if (PBMarker != WBC.PB_MARKER) or (h.PBOffset + PBSize > data_size):
  336.                 continue
  337.             data.seek(h.PBOffset + headerSize)
  338.             code = data.read(4)
  339.             if (code == "WWBB"):
  340.                 WB1.decodeJPEG(data, pixmap, buildPixmap, JPGFileSize-8)
  341.             else:
  342.                 data.seek(h.PBOffset + headerSize)
  343.                 WB1.readJPEG(data, pixmap, buildPixmap, JPGFileSize)
  344.             #
  345.             pixmaps.append(pixmap)
  346.         return title
  347.  
  348.  
  349.  
  350. for li in sys.argv[1:]:
  351.     print "Now processing %s..." % li
  352.     # let's process it
  353.     try:
  354.         f = open(unicode(li), 'rb')
  355.     except IOError:
  356.         print "IOError opening %s" % li
  357.  
  358.     try:
  359.         # let's try first a WB[1DZ] file
  360.         pixmap = WB1.read(li, f, buildPixmap = False)
  361.         if pixmap:
  362.             pixmaps = [ pixmap ]
  363.         else:
  364.             # let's try a WBC file
  365.             title, pixmaps = WBC.read(li, f, buildPixmap = False) #@UnusedVariable
  366.     finally:
  367.         del f
  368.     # now let's save the files
  369.     for pixmap in pixmaps:
  370.     # build the output file name
  371.         fnu = pixmap.info.get(K_TITLE, "image") + ".jpg"
  372.         fn = unicode(fnu, 'latin-1');
  373.         # now save it
  374.         of = open(fn, "wb")
  375.         print "Saved %s" % fnu
  376.         try:
  377.             pixmap.jpeg.tofile(of)
  378.         finally:
  379.             of.close()
RAW Paste Data
Want to get better at Python?
Learn to code Python in 2017
Pastebin PRO Summer Special!
Get 40% OFF on Pastebin PRO accounts!
Top