import struct import StringIO import sys import getopt import os.path as path import glob from PIL import Image BLOCK_MAP_END_OF_BLOCK = 0x00 BLOCK_MAP_CHIPSET = 0x01 BLOCK_MAP_WIDTH = 0x02 BLOCK_MAP_HEIGHT = 0x03 BLOCK_MAP_TYPE_OF_LOOP = 0x0B BLOCK_MAP_PARALLAX_BACK = 0x1F BLOCK_MAP_BACKGROUND = 0x20 BLOCK_MAP_HORIZONTAL_PAN = 0x21 BLOCK_MAP_VERTICAL_PAN = 0x22 BLOCK_MAP_HORIZONTAL_PAN_A = 0x23 BLOCK_MAP_HORIZONTAL_PAN_SP = 0x24 BLOCK_MAP_VERTICAL_PAN_A = 0x25 BLOCK_MAP_VERTICAL_PAN_SP = 0x26 BLOCK_MAP_LOWER_LAYER = 0x47 BLOCK_MAP_UPPER_LAYER = 0x48 BLOCK_MAP_EVENTS_LAYER = 0x51 BLOCK_MAP_TIMES_SAVED = 0x5B class 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: return class 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("= 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)