Paul Nechifor

[română]

Transform any file to BMP and back again

Ode to Joy

For example the file Ode to Joy looks like this

Doesn't everybody ask themselves how a file would look visualy? When I was looking through Wikipedia I saw how simple the BMP file format is and I thought to make a Python program to store the bytes from any file into a BMP (to be viewed) but to be able to reconstruct the original file.

After that I thought that this program can be useful for something: PIRACY! For example someone can transform an MP3 in multiple BMP files (of a smaller size, say 500KiB) and put them on his blag. Who wants the song can download the images and transform them back to the MP3. The idea to represent a file as something else reminds me of the illegal number.

The sourcecode is below, but you can also get it from file2bmp.py.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#!/usr/bin/env python

# Name: file2bmp
# Author: Paul Nechifor <[email protected]>
# Started: 05.06.2009
# Updated: 07.07.2009
# Description: Transforms any file into a BMP and vice versa.

from math import *
import sys

def hput(bmp, s):
	"""Writes 2 hex digits as a byte in the file"""
	if len(s) % 3 == 1: print "bubu!"; exit(1)
	for i in xrange(0, len(s), 3):
		bmp.write(chr(int(s[i:i+2], 16)))

def intstr(n):
	"""Transforms an int32 in the format needed by hput()"""
	str = ""
	for i in xrange(4):
		str += "%02x " % (n & 255)
		n >>= 8
	return str

def memint(str):
	"""Transforms a binary representation of an int32 to an number"""
	n = 0
	for i in xrange(4):
		n = (n << 8) | ord(str[3-i])
	return n

def makeBMP(filename):
	"""Reads the file whose name is given in the parameter and writes the BMP file"""
	file = open(filename).read()
	good = len(file)
	print "There are", good, "bytes."
	if good % 3 == 1: file += chr(0)*2
	elif good % 3 == 2: file += chr(0)
	pixeli = len(file) / 3
	print "That means", pixeli, "pixels."

	xy = int(ceil(sqrt(pixeli)))
	print "Making a", xy, "by", xy, "image."
	rest = xy*xy - pixeli
	print "Adding", rest, "pixels to make it a", xy*xy, "- pixel image."
	file += chr(0) * (rest*3)
	print "There are now %d bytes for %d pixels." % (len(file), xy*xy)
	mod = (xy * 3) % 4
	if mod != 0: pad = 4 - mod
	else: pad = 0
	print "Adding a padding of %d bytes so that the %d-pixel line will be %d bytes long." % (pad, xy, xy*3 + pad)

	new = ""
	for i in xrange(0, len(file), xy*3):
		new += file[i:i + xy*3] + chr(0)*pad
	file = new
	data = len(file)
	print "Now there are %d bytes." % (data)
	header = 54
	total = header + data
	print "The image will be %d bytes long." % (total)

	f = open(filename+".bmp", "w")

	hput(f, "42 4D")		# Magic number ("BM")
	hput(f, intstr(total))	# Size of the BMP file
	#hput(f, "00 00")		# Application Specific
	#hput(f, "00 00")		# Application Specific
	# here i'll put how may bytes are good
	hput(f, intstr(good))
	hput(f, "36 00 00 00")	# The offset where the bitmap data (pixels) can be found.
	hput(f, "28 00 00 00")	# The number of bytes in the header (from this point).
	hput(f, intstr(xy))		# The width of the bitmap in pixels
	hput(f, intstr(xy))		# The height of the bitmap in pixels
	hput(f, "01 00")		# Number of color planes being used.
	hput(f, "18 00")		# The number of bits/pixel.
	hput(f, "00 00 00 00")	# No compression used
	hput(f, intstr(data))	# The size of the raw BMP data (after this header)
	hput(f, "13 0B 00 00")	# The horizontal resolution of the image
	hput(f, "13 0B 00 00")	# The vertical resolution of the image
	hput(f, "00 00 00 00")	# Number of colors in the palette
	hput(f, "00 00 00 00")	# Means all colors are important

	f.write(file)
	f.close()

def getFromBMP(input, original):
	file = open(input).read()
	xy = memint(file[18:24])
	print "The image size is %dx%d." % (xy, xy)
	line = xy * 3
	print "A line is %d bytes long." % (line)
	padding = int(ceil(line / 4.0) * 4  -  line)
	print "This means %d bytes are padding bytes." % (padding)
	good = memint(file[6:10])
	print "There are %d good bytes after the padding is removed" % (good)

	file = file[54:] # removing the header
	new = ""
	for i in xrange(0, len(file), line + padding):
		new += file[i:i+line]
	
	f = open(original, "w")
	f.write(new[:good]) # writing only the good bytes
	f.close()	


if len(sys.argv) < 3:
	print "Usage: file2bmp.py to file.txt        -  creates file.txt.bmp\n" + \
		  "       file2bmp.py from file.txt.bmp  -  recreates file.txt"
else:
	if sys.argv[1] == "to":
		makeBMP(sys.argv[2])
	elif sys.argv[1] == "from":
		out = sys.argv[2]
		if out[-4:].lower() == ".bmp": out = out[:-4]
		else: out = out + "_from"
		getFromBMP(sys.argv[2], out)
	else:
		print "Must be 'to' or 'from'"

Created in 2009 by Paul NechiforProjects