All pastes #796942 Raw Edit

convert.py

public python v1 · immutable
#796942 ·published 2007-11-25 16:28 UTC
rendered paste body
import structimport StringIOimport sysimport getoptimport os.path as pathimport globfrom PIL import ImageBLOCK_MAP_END_OF_BLOCK      = 0x00BLOCK_MAP_CHIPSET           = 0x01BLOCK_MAP_WIDTH             = 0x02BLOCK_MAP_HEIGHT            = 0x03BLOCK_MAP_TYPE_OF_LOOP      = 0x0BBLOCK_MAP_PARALLAX_BACK     = 0x1FBLOCK_MAP_BACKGROUND        = 0x20BLOCK_MAP_HORIZONTAL_PAN    = 0x21BLOCK_MAP_VERTICAL_PAN      = 0x22BLOCK_MAP_HORIZONTAL_PAN_A  = 0x23BLOCK_MAP_HORIZONTAL_PAN_SP = 0x24BLOCK_MAP_VERTICAL_PAN_A    = 0x25BLOCK_MAP_VERTICAL_PAN_SP   = 0x26BLOCK_MAP_LOWER_LAYER       = 0x47BLOCK_MAP_UPPER_LAYER       = 0x48BLOCK_MAP_EVENTS_LAYER      = 0x51BLOCK_MAP_TIMES_SAVED       = 0x5Bclass DataBlock:	def __init__(self, type, data, length):		self.type = type		self.data = StringIO.StringIO(data)		self.length = length		def read_byte(stream):	return struct.unpack("B", stream.read(1))[0]		def read_integer(stream):	value = 0	temp = 0x80		while temp & 0x80:		value <<= 7		temp = read_byte(stream)		value |= temp & 0x7F			return value	def read_string(stream, length):	return stream.read(length)	def read_array2(stream, length):	max = read_integer(stream)		array = {}		while stream.tell() < length:		row = read_integer(stream)		while True:			col = read_integer(stream)			if col == 0:				break							datalen = read_integer(stream)			if not array.has_key(row):				array[row] = {}							array[row][col] = DataBlock(col, stream.read(datalen), datalen)				return array	def loop_type_string(type):	if type == 0x00:		return "None"	elif type == 0x01:		return "Horizontal"	elif type == 0x02:		return "Vertical"	elif type == 0x03:		return "Both"	else:		return "Unknown"	class LcfDataBase:	def __init__(self, path):		self.path = path		self.file = open(path, "rb")				self.chipsets = {}				self.check_header()		for block in self.read_blocks():			if block.type == 0x14:				array = read_array2(block.data, block.length)								for row in array:					chip_name = read_string(array[row][0x01].data, array[row][0x01].length)					chip_filename = read_string(array[row][0x02].data, array[row][0x02].length)										self.chipsets[row] = {						'name': chip_name,						'filename': chip_filename					}	def check_header(self):		header = self.file.read(12)		if header != "\x0bLcfDataBase":			raise "Invalid header. Doesn't appear to be an RM2K database file."				def read_blocks(self):		try:			while True:				type = read_integer(self.file)				length = read_integer(self.file)				data = self.file.read(length)				yield DataBlock(type, data, length)		except Exception, e:			returnclass LcfMapUnit:	def __init__(self, path):		self.path = path		self.file = open(path, "rb")		self.check_header()						# set up default map data				self.chipset_id = 1		self.width = 20		self.height = 15		self.loop_type = 0           		self.parallax = False		self.background = None		self.pan_horizontal = False		self.pan_horizontal_auto = False		self.pan_horizontal_speed = 0		self.pan_vertical = False		self.pan_vertical_auto = False		self.pan_vertical_speed = 0   		self.layers = {}		self.event_count = 0		self.save_count = 0				for block in self.read_blocks():						if block.type == BLOCK_MAP_CHIPSET:				self.chipset_id = read_integer(block.data)			elif block.type == BLOCK_MAP_WIDTH:				self.width = read_integer(block.data)			elif block.type == BLOCK_MAP_HEIGHT:				self.height = read_integer(block.data)			elif block.type == BLOCK_MAP_TYPE_OF_LOOP:				self.loop_type = read_integer(block.data)			elif block.type == BLOCK_MAP_PARALLAX_BACK:				self.parallax = read_integer(block.data) == 1			elif block.type == BLOCK_MAP_BACKGROUND:				self.background = read_string(block.data, block.length)			elif block.type == BLOCK_MAP_HORIZONTAL_PAN:				self.pan_horizontal = read_integer(block.data) == 1			elif block.type == BLOCK_MAP_VERTICAL_PAN:				self.pan_vertical = read_integer(block.data) == 1			elif block.type == BLOCK_MAP_HORIZONTAL_PAN_A:				self.pan_horizontal_auto = read_integer(block.data) == 1			elif block.type == BLOCK_MAP_VERTICAL_PAN_A:				self.pan_vertical_auto = read_integer(block.data) == 1			elif block.type == BLOCK_MAP_HORIZONTAL_PAN_SP:				self.pan_horizontal_speed = read_integer(block.data)			elif block.type == BLOCK_MAP_VERTICAL_PAN_SP:				self.pan_vertical_speed = read_integer(block.data)			elif block.type == BLOCK_MAP_LOWER_LAYER:				self.layers['lower'] = block.data.read()			elif block.type == BLOCK_MAP_UPPER_LAYER:				self.layers['upper'] = block.data.read()			elif block.type == BLOCK_MAP_TIMES_SAVED:				self.save_count = read_integer(block.data)			elif block.type == BLOCK_MAP_END_OF_BLOCK:				break					def check_header(self):		header = self.file.read(11)		if header != "\nLcfMapUnit":			raise "Invalid header. Doesn't appear to be an RM2K map file."			def read_blocks(self):		try:			while True:				type = read_integer(self.file)				length = read_integer(self.file)				data = self.file.read(length)				yield DataBlock(type, data, length)		except Exception, e:			return				def get_tiles_from_image(image):	imw, imh = image.size	x_tiles = imw / 16	y_tiles = imh / 16	tiles = []	for y in range(y_tiles):		for x in range(x_tiles):			tile = image.crop((x, y, 16, 16))			tiles.append(tile)	return tiles	class SphereMap:	class Layer:		def __init__(self, name, width, height):			self.name = name			self.width = width			self.height = height			self.parallax = False			self.tiles = [0 for x in xrange(width * height)]					def setTile(self, x, y, tile):			self.tiles[x + y * self.width] = tile				def __init__(self, tile_size, map_size):		self.layers = []		self.tiles = []		self.tile_width, self.tile_height = tile_size		self.width, self.height = map_size			def addLayerFromImage(self, name, image):		layer = SphereMap.Layer(name, self.width, self.height)				tiles = get_tiles_from_image(image)				tile_index = self.addTiles(tiles)				imw, imh = image.size		x_tiles = imw / 16		y_tiles = imh / 16				for y in range(y_tiles):			for x in range(x_tiles):				layer.setTile(x, y, tile_index)				tile_index += 1						self.layers.append(layer)		return layer			def addTiles(self, tiles):		index = len(self.tiles)		self.tiles.extend(tiles)		return index			def write(path):		file = open(path, "wb")				# 256 byte header				file.write(".rmp")					# signature		file.write(struct.pack("<H", 1))			# version (must be 1)		file.write(struct.pack("<H", 0))			# type (obsolete)		file.write(struct.pack("B", len(self.layers)))		# number of layers		file.write(struct.pack("B", 0))				# reserved		file.write(struct.pack("<H", 0))			# number of entities (we don't support them)		file.write(struct.pack("<H", 0))			# start x		file.write(struct.pack("<H", 0))			# start y		file.write(struct.pack("B", 0))				# start layer		file.write(struct.pack("B", 0))				# start direction		file.write(struct.pack("<H", 9))			# number of strings		file.write(struct.pack("<H", 0))			# number of zones				for i in range(235):			file.write(struct.pack("B", 0))			# reserved bytes					# write 9 empty strings				for i in range(9):			file.write(struct.pack("<H", 0))					# write all the layers				for layer in self.layers:			file.write(struct.pack("<H", layer.width))	# layer width			file.write(struct.pack("<H", layer.height))	# layer height			flags = 0x01	# visible			if layer.parallax:				flags |= 0x02			file.write(struct.pack("<H", flags))		# flags						file.write(struct.pack("<f", 0.0))		# parallax x			file.write(struct.pack("<f", 0.0))		# parallax y			file.write(struct.pack("<f", 0.0))		# scrolling x			file.write(struct.pack("<f", 0.0))		# scrolling y						file.write(struct.pack("<I", 0))		# num segments ?			file.write(struct.pack("B", 0))			# reflective						for i in range(3):				file.write(struct.pack("B"))		# reserved							file.write(struct.pack("<H", len(layer.name))	# layer name length			file.write(layer.name)				# layer name						for tile in layer.tiles:				file.write(struct.pack("<H", tile)						# write the tileset				file.write(".rts")					# header		file.write(struct.pack("<H", 1))			# version (must be 1)		file.write(struct.pack("<H", len(self.tiles)))		# number of tiles		file.write(struct.pack("<H", self.tile_width))		# tile width		file.write(struct.pack("<H", self.tile_height))		# tile height		file.write(struct.pack("<H", 32))			# tile bpp (must be 32)		file.write(struct.pack("B", 0))				# compression		file.write(struct.pack("B", 0))				# no obstructions				for i in range(240):			file.write(struct.pack("B", 0))			# reserved					# TODO: finish this		def print_help():	print "usage: convert.py [action] [args]"	print "Actions:"	print "  info [map]                   Shows information on an RM2k map."	print "  convert [map] [output_map]   Converts an RM2k map to a Sphere map."	print	print "Switches:"	print "  -c [file], --chipset=[file]  Override the map's chipset."	print "  -h, --help                   Prints this help."	print "  -s, --strict                 Fails if any resource isn't found."	print	print "map-converter v0.1 by engine #100. Licensed under the GPL."	print "http://projects.casiotone.org/map-converter/"def run_convert():		opts, leftover = getopt.gnu_getopt(sys.argv[1:], 'hc:s', ['help', 'chipset=', 'strict'])		use_chipset = None	rtp_path = None	strict_mode = False	for opt, arg in opts:		if opt == '-h' or opt == '--help':			print_help()			return		if opt == '-c' or opt == '--chipset':			use_chipset = arg		if opt == '-s' or opt == '--strict':			strict_mode = True				if len(leftover) == 0:		raise Exception("No action specified.")			if len(leftover) >= 1:		action = leftover[0]				if action == "convert":		if len(leftover) == 1:			raise Exception("An rm2k map file must be specified to convert a map.")		if len(leftover) == 2:			raise Exception("An output map file must be specified to convert a map.")					map = LcfMapUnit(leftover[1])		out = leftover[2]		chipset_path = ""		if use_chipset is None:			# attempt to get the chipset from RPG_RT.ldb			db = LcfDataBase(path.join(path.dirname(leftover[1]), "RPG_RT.ldb"))			print "Searching for database chipset #%d: %s (%s)" % (map.chipset_id, db.chipsets[map.chipset_id]['name'], db.chipsets[map.chipset_id]['filename'])			filename = db.chipsets[map.chipset_id]['filename']			chip_path = path.join(path.dirname(leftover[1]), "ChipSet", filename)						chip_files = glob.glob(chip_path + ".*")			if len(chip_files):				chipset_path = chip_files[0]				print "Found at %s" % chipset_path			else:				raise Exception("Chipset could not be found in the project. It may be part of the RTP.")								else:			print "Using specified chipset: %s" % (use_chipset)			chipset_path = use_chipset				chipset_img = Image.open(chipset_path)						parallax_img = None				if map.parallax:			# map uses a parallax background, we need to find that			# this isn't very important so we can fail if strict mode isn't set			print "Searching for parallax background %s" % (map.background)			filename = map.background			bg_path = path.join(path.dirname(leftover[1]), "Panorama", filename)			bg_files = glob.glob(bg_path + ".*")			if len(bg_files):				background_path = bg_files[0]				print "Found at %s" % background_path				parallax_img = Image.open(background_path)			else:				if strict_mode:					raise Exception("Parallax background could not be found in the project. It may be part of the RTP.")				else:					print "Parallax background could not be found. Continuing anyway..."				print "Attempting conversion..."				sphere_map = SphereMap((16, 16), (map.width, map.height))				if parallax_img is not None:			layer = sphere_map.addLayerFromImage("Parallax", parallax_img)			layer.parallax = True						elif action == "info":				if len(leftover) == 1:			raise Exception("An rm2k map file must be specified to view information on.")					map = LcfMapUnit(leftover[1])		print "Map size: %dx%d" % (map.width, map.height)		print "Loop type: %d (%s)" % (map.loop_type, loop_type_string(map.loop_type))		print "Chipset ID: %d" % (map.chipset_id)		print "Parallax: %s" % (map.parallax)		print "Background: %s" % (map.background)		print "Panning:"		if map.pan_horizontal:			print " |- Horizontal: %d %s" % (map.pan_horizontal_speed, ["(Auto)", ""][int(map.pan_horizontal_auto)])		else:			print " |- Horizontal: None"		if map.pan_vertical:			print " |- Vertical: %d %s" % (map.pan_vertical_speed, ["(Auto)", ""][int(map.pan_vertical_auto)])		else:			print " |- Vertical: None"					print "Layer data lengths: %d, %d" % (len(map.layers['lower']), len(map.layers['upper']))		print "Save count: %d" % (map.save_count)		if __name__ == "__main__":	try:		run_convert()	except Exception, e:		print e		sys.exit(1)