new_contents: #    Copyright 2007 Jeff Epler <jepler@unpythonic.net>
  #
  #    This program is free software; you can redistribute it and/or modify
  #    it under the terms of the GNU General Public License as published by
  #    the Free Software Foundation; either version 2 of the License, or
  #    (at your option) any later version.
  #
  #    This program is distributed in the hope that it will be useful,
  #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  #    GNU General Public License for more details.
  #
  #    You should have received a copy of the GNU General Public License
  #    along with this program; if not, write to the Free Software
  #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  #
  #
  # Info about Xilinx bitfiles:
  #
  # The format consists of several variable length chunks, preceded by what
  # seems to be a constant header (a magic number).
  #
  # After the header, each "chunk" consists of a one byte "tag", a two or
  # four byte length, and "length" bytes of data (the body).
  #
  # In some chunks, the body is a zero terminated printable string.  In
  # others it is a blob of binary data.  The file format doesn't care.
  #
  # Standard Xilinx files use 5 chunks: 'a' through 'd' are zero-terminated
  # strings with information about the file.  'e' is a large binary blob
  # with the actual bitstream in it.  Xilinx uses 2 byte lengths for chunks
  # 'a' thru 'd', and 4 bytes for chunk 'e'.  This library allows other
  # chunks, and assume that all have 4 byte lengths, except 'a' thru 'd'.
  #
  import struct
  
  class BitFile:
      MAXCHUNKS = 50
      SMALLCHUNKS = "abcd"
      MAGIC = "\x00\x09" \
  	    "\x0f\xf0\x0f\xf0" \
  	    "\x0f\xf0\x0f\xf0" \
  	    "\x00\x00\x01"
      ORDER = "abcde"
  
      def __init__(self, chunks={}):
  	self.chunks = dict(chunks)
  
      def __getitem__(self, item):
  	return self.chunks[item]
      def __setitem__(self, item, value):
  	self.chunks[item] = value
      def __delitem__(self, item):
  	del self.chunks[item]
  
      def chunkorder(self, (tag, value)):
  	if tag in self.ORDER:
  	    return self.ORDER.index(tag)
  	return 256 + ord(tag)
  
      @classmethod
      def fromstring(cls, data):
  	if not data.startswith(cls.MAGIC):
  	    raise ValueError, "data does not start with the magic number"
  	i = len(cls.MAGIC)
  	chunks = {}
  	while i < len(data):
  	    tag = data[i]
  	    if tag in cls.SMALLCHUNKS:
  		chunksize = struct.unpack(">H", data[i+1:i+3])[0]
  		i = i + 3
  	    else:
  		chunksize = struct.unpack(">I", data[i+1:i+5])[0]
  		i = i + 5
  	    chunkdata = data[i:i+chunksize]
  	    if tag in chunks:
  		raise ValueError, "bitfile has chunk %r more than once" % tag
  	    chunks[tag] = chunkdata
  	    i = i + chunksize
  	return cls(chunks)
  
      @classmethod
      def fromfile(cls, file):
  	return cls.fromstring(file.read())
  
      @classmethod
      def fromfilename(cls, filename):
  	return cls.fromstring(open(filename, "rb").read())
  
      def tostring(self):
  	result = self.MAGIC
  	for tag, chunkdata in sorted(self.chunks.items(), key=self.chunkorder):
  	    if len(tag) != 1:
  		raise ValueError, "Tag %r must be a length-1 string" % tag
  	    result = result + tag
  	    if tag in self.SMALLCHUNKS:
  		result += struct.pack(">H", len(chunkdata))
  	    else:
  		result += struct.pack(">I", len(chunkdata))
  	    result += chunkdata
  
  	return result
      
      def tofile(cls, file):
  	return file.write(self.tostring())
  
      def tofilename(cls, filename):
  	return open(filename, "wb").write(self.tostring())
  
  if __name__ == '__main__':
      import sys
  
      if len(sys.argv) < 2:
  	c = BitFile()
  	c['a'] = "hello world"
  	c['e'] = "goodbye"
  	c['A'] = "shazam"
  	s = c.tostring()
  	print repr(s)
  	d = BitFile.fromstring(s)
  	print d.chunks
  	assert d.tostring() == s
  
      for bitfile in sys.argv[1:]:
  	bits = open(bitfile, "rb").read()
  	c = BitFile.fromstring(bits)
  	for k, v in sorted(c.chunks.items()):
  	    if k in 'abcd':
  		print k, v
  	    else:
  		print k, len(v)
  	newbits = c.tostring()
  	assert bits == newbits  # Assuming the original is in canonical order!
  	print
  
subject: add python version of bitfile library - thanks jeff