raise LoadException('Invalid PAK header') # Initial Values unknownh1 = df.readshort() unknownh2 = df.readshort() numfiles = df.readint() compressed_idx_size = df.readint() unknowni1 = df.readint() # Now load in the index decobj = zlib.decompressobj() indexdata = decobj.decompress(df.read()) zeroindex = df.tell() - len(decobj.unused_data) decobj = None fileindex = {} for i in range(numfiles): index = PakIndex(indexdata[:272]) indexdata = indexdata[272:] fileindex[index.filename] = index for filename in fileindex.keys(): index = fileindex[filename] print filename df.seek(zeroindex + index.abs_index) # On Windows, we need to specify bufsize or memory gets clobbered filedata = zlib.decompress( df.read(index.size_compressed), 15, index.size_real) f = open(filename, 'wb') f.write(filedata) f.close
class B1Gfx(Gfx): """ Grphics structure for Book 1 """ book = 1 wall_types: Dict[Any, Any] = {} wall_gfx_group: Dict[Any, Any] = {} tilebuf_mult = 1 item_dim = 42 item_cols = 10 item_rows = 24 tile_width = 52 tile_height = 26 floor_cols = 6 floor_rows = 32 decal_cols = 6 decal_rows = 32 obj_a_width = 52 obj_a_height = 52 obj_a_cols = 6 obj_a_rows = 16 obj_a_offset = 1 obj_b_width = 52 obj_b_height = 78 obj_b_cols = 6 obj_b_rows = 10 obj_b_offset = 101 obj_c_width = 52 obj_c_height = 78 obj_c_cols = 6 obj_c_rows = 10 obj_c_offset = 161 obj_d_width = 52 obj_d_height = 130 obj_d_cols = 5 obj_d_rows = 1 obj_d_offset = 251 walldecal_cols = 6 walldecal_rows = 10 GFX_SET_A = 1 GFX_SET_B = 2 GFX_SET_C = 3 GFX_SET_TREE = 4 def __init__(self, datadir, eschalondata): # Wall graphic groupings for i in range(101): self.wall_gfx_group[i] = self.GFX_SET_A for i in range(101, 161): self.wall_gfx_group[i] = self.GFX_SET_B for i in range(161, 251): self.wall_gfx_group[i] = self.GFX_SET_C for i in range(251, 256): self.wall_gfx_group[i] = self.GFX_SET_TREE # Wall object types for i in (list(range(127)) + list(range(132, 142)) + list(range(143, 153)) + list(range(154, 161)) + list(range(214, 251))): self.wall_types[i] = self.TYPE_OBJ for i in range(161, 214): self.wall_types[i] = self.TYPE_WALL for i in (list(range(251, 256)) + list(range(127, 132)) + [142, 153]): self.wall_types[i] = self.TYPE_TREE # Restricted entities (only one direction) self.restrict_ents = [50, 55, 58, 66, 67, 71] # Book 1 specific vars (just the PAK structure stuff) self.unknownh1 = -1 self.unknownh2 = -1 self.numfiles = -1 self.compressed_idx_size = -1 self.unknowni1 = -1 self.fileindex = {} self.zeroindex = -1 # Book 1 specific caches self.itemcache = None # Graphics PAK file self.pakloc = os.path.join(eschalondata.gamedir, 'gfx.pak') if os.path.isfile(self.pakloc): self.df = Savefile(self.pakloc) else: self.df = None # Set our loaded status self.loaded = False # Finally call the parent constructor super(B1Gfx, self).__init__(datadir, eschalondata) def readfile(self, filename: str) -> object: """ Reads a given filename out of the PAK. """ if self.loaded: filepath = os.path.join( self.eschalondata.gamedir, 'packedgraphics', filename) if os.path.isfile(filepath): return open(filepath, 'rb').read() if filename in self.fileindex: self.df.open_r() self.df.seek(self.zeroindex + self.fileindex[filename].abs_index) # On Windows, we need to specify bufsize or memory gets clobbered filedata = zlib.decompress( self.df.read(self.fileindex[filename].size_compressed), 15, self.fileindex[filename].size_real) self.df.close() return filedata else: raise LoadException( 'Filename %s not found in archive' % (filename)) else: raise LoadException('PAK Index has not been loaded') def initialread(self): """ Read in the main file index. """ df = self.df if df is None: self.loaded = True return df.open_r() header = df.read(4) if (header != b'!PAK'): df.close() raise LoadException('Invalid PAK header') # Initial Values self.unknownh1 = df.readshort() self.unknownh2 = df.readshort() self.numfiles = df.readint() self.compressed_idx_size = df.readint() self.unknowni1 = df.readint() # Now load in the index decobj = zlib.decompressobj() indexdata = decobj.decompress(df.read()) self.zeroindex = df.tell() - len(decobj.unused_data) for i in range(self.numfiles): index = PakIndex(indexdata[:272]) indexdata = indexdata[272:] self.fileindex[index.filename] = index # Close and clean up df.close() self.loaded = True def get_item(self, item, size=None, gdk=True): if (self.itemcache is None): self.itemcache = GfxCache(self.readfile( 'items_mastersheet.png'), 42, 42, 10) return self.itemcache.getimg(item.pictureid + 1, size, gdk) def get_floor(self, floornum, size=None, gdk=False): if (floornum == 0): return None if (self.floorcache is None): self.floorcache = GfxCache(self.readfile( 'iso_tileset_base.png'), 52, 26, 6) return self.floorcache.getimg(floornum, size, gdk) def get_decal(self, decalnum, size=None, gdk=False): if (decalnum == 0): return None if (self.decalcache is None): self.decalcache = GfxCache(self.readfile( 'iso_tileset_base_decals.png'), 52, 26, 6) return self.decalcache.getimg(decalnum, size, gdk) # Returns a tuple, first item is the surface, second is the extra height to add while drawing def get_object(self, objnum, size=None, gdk=False, treeset=0): """ Note that we ignore the treeset flag in book 1 """ if (objnum == 0): return (None, 0, 0) try: gfxgroup = self.wall_gfx_group[objnum] except KeyError: return (None, 0, 0) if gfxgroup == self.GFX_SET_A: if (self.objcache1 is None): self.objcache1 = GfxCache(self.readfile( 'iso_tileset_obj_a.png'), 52, 52, 6) return (self.objcache1.getimg(objnum, size, gdk), 1, 0) elif gfxgroup == self.GFX_SET_B: if (self.objcache2 is None): self.objcache2 = GfxCache(self.readfile( 'iso_tileset_obj_b.png'), 52, 78, 6) return (self.objcache2.getimg(objnum - 100, size, gdk), 2, 0) elif gfxgroup == self.GFX_SET_C: if (self.objcache3 is None): self.objcache3 = GfxCache(self.readfile( 'iso_tileset_obj_c.png'), 52, 78, 6) return (self.objcache3.getimg(objnum - 160, size, gdk), 2, 0) else: if (self.objcache4 is None): self.objcache4 = GfxCache( self.readfile('iso_trees.png'), 52, 130, 5) if (objnum in self.treemap): return (self.objcache4.getimg(self.treemap[objnum], size, gdk), 4, 0) else: return (None, 4, 0) def get_object_decal(self, decalnum, size=None, gdk=False): if (decalnum == 0): return None if (self.objdecalcache is None): self.objdecalcache = GfxCache(self.readfile( 'iso_tileset_obj_decals.png'), 52, 78, 6) return self.objdecalcache.getimg(decalnum, size, gdk) def get_flame(self, size=None, gdk=False): """ Grabs the flame graphic, so it's clear when viewing maps. I provide my own image here instead of using the game's because the file bundled with the game doesn't have transparency information, and I don't feel like doing a conversion. """ if (self.flamecache is None): with open(os.path.join(self.datadir, 'torch_single.png'), 'rb') as df: flamedata = df.read() self.flamecache = B1GfxEntCache(flamedata, 1, 1) if (size is None): size = self.tile_width return self.flamecache.getimg(1, int(size * self.flamecache.size_scale), gdk) def get_entity(self, entnum, direction, size=None, gdk=False): entity = self.eschalondata.get_entity(entnum) if not entity: return None entnum = entity.gfxfile if (entnum not in self.entcache): filename = 'mo%d.png' % (entnum) if (entnum in self.restrict_ents): self.entcache[entnum] = B1GfxEntCache( self.readfile(filename), 2, 1) else: self.entcache[entnum] = B1GfxEntCache(self.readfile(filename)) cache = self.entcache[entnum] if (size is None): size = self.tile_width return cache.getimg(direction, int(size * cache.size_scale), gdk) def get_avatar(self, avatarnum): if avatarnum < 0 or avatarnum > 7: return None if avatarnum not in self.avatarcache: if avatarnum == 7: if os.path.exists(os.path.join(self.eschalondata.gamedir, 'mypic.png')): self.avatarcache[avatarnum] = GdkPixbuf.Pixbuf.new_from_file( os.path.join(self.eschalondata.gamedir, 'mypic.png')) else: return None else: self.avatarcache[avatarnum] = GfxCache( self.readfile('{}.png'.format(avatarnum)), 60, 60, 1).pixbuf return self.avatarcache[avatarnum]
class B1Gfx(Gfx): """ Grphics structure for Book 1 """ book = 1 wall_types = {} wall_gfx_group = {} tilebuf_mult = 1 item_dim = 42 item_cols = 10 item_rows = 24 tile_width = 52 tile_height = 26 floor_cols = 6 floor_rows = 32 decal_cols = 6 decal_rows = 32 obj_a_width = 52 obj_a_height = 52 obj_a_cols = 6 obj_a_rows = 16 obj_a_offset = 1 obj_b_width = 52 obj_b_height = 78 obj_b_cols = 6 obj_b_rows = 10 obj_b_offset = 101 obj_c_width = 52 obj_c_height = 78 obj_c_cols = 6 obj_c_rows = 10 obj_c_offset = 161 obj_d_width = 52 obj_d_height = 130 obj_d_cols = 5 obj_d_rows = 1 obj_d_offset = 251 walldecal_cols = 6 walldecal_rows = 10 GFX_SET_A = 1 GFX_SET_B = 2 GFX_SET_C = 3 GFX_SET_TREE = 4 def __init__(self, datadir, eschalondata): # Wall graphic groupings for i in range(101): self.wall_gfx_group[i] = self.GFX_SET_A for i in range(101, 161): self.wall_gfx_group[i] = self.GFX_SET_B for i in range(161, 251): self.wall_gfx_group[i] = self.GFX_SET_C for i in range(251, 256): self.wall_gfx_group[i] = self.GFX_SET_TREE # Wall object types for i in (range(127) + range(132, 142) + range(143, 153) + range(154, 161) + range(214, 251)): self.wall_types[i] = self.TYPE_OBJ for i in range(161, 214): self.wall_types[i] = self.TYPE_WALL for i in (range(251, 256) + range(127, 132) + [142, 153]): self.wall_types[i] = self.TYPE_TREE # Restricted entities (only one direction) self.restrict_ents = [50, 55, 58, 66, 67, 71] # Book 1 specific vars (just the PAK structure stuff) self.unknownh1 = -1 self.unknownh2 = -1 self.numfiles = -1 self.compressed_idx_size = -1 self.unknowni1 = -1 self.fileindex = {} self.zeroindex = -1 # Book 1 specific caches self.itemcache = None # Graphics PAK file self.pakloc = os.path.join(eschalondata.gamedir, 'gfx.pak') if os.path.isfile(self.pakloc): self.df = Savefile(self.pakloc) else: self.df = None # Set our loaded status self.loaded = False # Finally call the parent constructor super(B1Gfx, self).__init__(datadir, eschalondata) def readfile(self, filename): """ Reads a given filename out of the PAK. """ if self.loaded: filepath = os.path.join(self.eschalondata.gamedir, 'packedgraphics', filename) if os.path.isfile(filepath): return open(filepath, 'rb').read() if (filename in self.fileindex): self.df.open_r() self.df.seek(self.zeroindex + self.fileindex[filename].abs_index) # On Windows, we need to specify bufsize or memory gets clobbered filedata = zlib.decompress( self.df.read(self.fileindex[filename].size_compressed), 15, self.fileindex[filename].size_real) self.df.close() return filedata else: raise LoadException('Filename %s not found in archive' % (filename)) else: raise LoadException('PAK Index has not been loaded') def initialread(self): """ Read in the main file index. """ df = self.df if df is None: self.loaded = True return df.open_r() header = df.read(4) if (header != '!PAK'): df.close() raise LoadException('Invalid PAK header') # Initial Values self.unknownh1 = df.readshort() self.unknownh2 = df.readshort() self.numfiles = df.readint() self.compressed_idx_size = df.readint() self.unknowni1 = df.readint() # Now load in the index decobj = zlib.decompressobj() indexdata = decobj.decompress(df.read()) self.zeroindex = df.tell() - len(decobj.unused_data) decobj = None for i in range(self.numfiles): index = PakIndex(indexdata[:272]) indexdata = indexdata[272:] self.fileindex[index.filename] = index # Close and clean up df.close() self.loaded = True def get_item(self, item, size=None, gdk=True): if (self.itemcache is None): self.itemcache = GfxCache(self.readfile('items_mastersheet.png'), 42, 42, 10) return self.itemcache.getimg(item.pictureid+1, size, gdk) def get_floor(self, floornum, size=None, gdk=False): if (floornum == 0): return None if (self.floorcache is None): self.floorcache = GfxCache(self.readfile('iso_tileset_base.png'), 52, 26, 6) return self.floorcache.getimg(floornum, size, gdk) def get_decal(self, decalnum, size=None, gdk=False): if (decalnum == 0): return None if (self.decalcache is None): self.decalcache = GfxCache(self.readfile('iso_tileset_base_decals.png'), 52, 26, 6) return self.decalcache.getimg(decalnum, size, gdk) # Returns a tuple, first item is the surface, second is the extra height to add while drawing def get_object(self, objnum, size=None, gdk=False, treeset=0): """ Note that we ignore the treeset flag in book 1 """ if (objnum == 0): return (None, 0, 0) try: gfxgroup = self.wall_gfx_group[objnum] except KeyError: return (None, 0, 0) if gfxgroup == self.GFX_SET_A: if (self.objcache1 is None): self.objcache1 = GfxCache(self.readfile('iso_tileset_obj_a.png'), 52, 52, 6) return (self.objcache1.getimg(objnum, size, gdk), 1, 0) elif gfxgroup == self.GFX_SET_B: if (self.objcache2 is None): self.objcache2 = GfxCache(self.readfile('iso_tileset_obj_b.png'), 52, 78, 6) return (self.objcache2.getimg(objnum-100, size, gdk), 2, 0) elif gfxgroup == self.GFX_SET_C: if (self.objcache3 is None): self.objcache3 = GfxCache(self.readfile('iso_tileset_obj_c.png'), 52, 78, 6) return (self.objcache3.getimg(objnum-160, size, gdk), 2, 0) else: if (self.objcache4 is None): self.objcache4 = GfxCache(self.readfile('iso_trees.png'), 52, 130, 5) if (objnum in self.treemap): return (self.objcache4.getimg(self.treemap[objnum], size, gdk), 4, 0) else: return (None, 4, 0) def get_object_decal(self, decalnum, size=None, gdk=False): if (decalnum == 0): return None if (self.objdecalcache is None): self.objdecalcache = GfxCache(self.readfile('iso_tileset_obj_decals.png'), 52, 78, 6) return self.objdecalcache.getimg(decalnum, size, gdk) def get_flame(self, size=None, gdk=False): """ Grabs the flame graphic, so it's clear when viewing maps. I provide my own image here instead of using the game's because the file bundled with the game doesn't have transparency information, and I don't feel like doing a conversion. """ if (self.flamecache is None): df = open(os.path.join(self.datadir, 'torch_single.png'), 'rb') flamedata = df.read() df.close() self.flamecache = B1GfxEntCache(flamedata, 1, 1) if (size is None): size = self.tile_width return self.flamecache.getimg(1, int(size*self.flamecache.size_scale), gdk) def get_entity(self, entnum, direction, size=None, gdk=False): entity = self.eschalondata.get_entity(entnum) if not entity: return None entnum = entity.gfxfile if (entnum not in self.entcache): filename = 'mo%d.png' % (entnum) if (entnum in self.restrict_ents): self.entcache[entnum] = B1GfxEntCache(self.readfile(filename), 2, 1) else: self.entcache[entnum] = B1GfxEntCache(self.readfile(filename)) cache = self.entcache[entnum] if (size is None): size = self.tile_width return cache.getimg(direction, int(size*cache.size_scale), gdk) def get_avatar(self, avatarnum): if (avatarnum < 0 or avatarnum > 7): return None if (avatarnum not in self.avatarcache): if (avatarnum == 7): if (os.path.exists(os.path.join(self.eschalondata.gamedir, 'mypic.png'))): self.avatarcache[avatarnum] = gtk.gdk.pixbuf_new_from_file(os.path.join(self.eschalondata.gamedir, 'mypic.png')) else: return None else: self.avatarcache[avatarnum] = GfxCache(self.readfile('%d.png' % (avatarnum)), 60, 60, 1).pixbuf return self.avatarcache[avatarnum]