Commit [166202] Maximize Restore History

Trying to merge iesh-infinity branch

Jaroslav Benkovsky Jaroslav Benkovsky 2009-01-29

<< < 1 2 3 (Page 3 of 3)
added TODO
added ieparse.py
added iesh.e3p
added infinity
added infinity/dlg_debugger.py
added infinity/format.py
added infinity/formats
added infinity/formats/are.py
added infinity/formats/bam.py
added infinity/formats/bcs.py
added infinity/formats/cre.py
added mos2tis.py
removed ie_shell
removed ie_shell/formats
changed Makefile
changed README
changed examples
changed examples/button_frames.py
changed iesh
changed setup.py
copied ie_shell/__init__.py -> infinity/__init__.py
copied ie_shell/builtins.py -> infinity/formats/tlk.py
copied ie_shell/core.py -> infinity/core.py
copied ie_shell/formats/__init__.py -> infinity/formats/__init__.py
copied ie_shell/formats/are.py -> infinity/formats/mos.py
copied ie_shell/formats/bam.py -> infinity/formats/dlg.py
copied ie_shell/formats/biff.py -> infinity/formats/biff.py
copied ie_shell/formats/chui.py -> infinity/formats/itm.py
copied ie_shell/formats/cre.py -> infinity/formats/chui.py
copied ie_shell/formats/dlg.py -> infinity/formats/wmap.py
copied ie_shell/formats/dtypes.py -> infinity/dtypes.py
copied ie_shell/formats/format.py -> infinity/formats/tis.py
copied ie_shell/formats/ids.py -> infinity/formats/ids.py
copied ie_shell/formats/itm.py -> infinity/formats/spl.py
copied ie_shell/formats/key.py -> infinity/formats/key.py
copied ie_shell/formats/pro.py -> infinity/formats/pro.py
copied ie_shell/formats/spl.py -> infinity/formats/wed.py
copied ie_shell/formats/stream.py -> infinity/formats/d2a.py
copied ie_shell/formats/tis.py -> infinity/formats/wfx.py
copied ie_shell/formats/tlk.py -> infinity/builtins.py
copied ie_shell/formats/vvc.py -> infinity/formats/vvc.py
copied ie_shell/formats/wed.py -> infinity/formats/stor.py
copied ie_shell/formats/wfx.py -> infinity/defaults.py
copied ie_shell/formats/wmap.py -> infinity/stream.py
TODO Diff Switch to side-by-side view
Loading...
ieparse.py Diff Switch to side-by-side view
Loading...
iesh.e3p Diff Switch to side-by-side view
Loading...
infinity
Directory.
infinity/dlg_debugger.py Diff Switch to side-by-side view
Loading...
infinity/format.py Diff Switch to side-by-side view
Loading...
infinity/formats
Directory.
infinity/formats/are.py Diff Switch to side-by-side view
Loading...
infinity/formats/bam.py Diff Switch to side-by-side view
Loading...
infinity/formats/bcs.py Diff Switch to side-by-side view
Loading...
infinity/formats/cre.py Diff Switch to side-by-side view
Loading...
mos2tis.py Diff Switch to side-by-side view
Loading...
ie_shell
File was removed.
ie_shell/formats
File was removed.
Makefile Diff Switch to side-by-side view
Loading...
README Diff Switch to side-by-side view
Loading...
examples
Directory.
examples/button_frames.py Diff Switch to side-by-side view
Loading...
iesh Diff Switch to side-by-side view
Loading...
setup.py Diff Switch to side-by-side view
Loading...
ie_shell/__init__.py to infinity/__init__.py
File was copied or renamed.
ie_shell/builtins.py to infinity/formats/tlk.py
--- a/ie_shell/builtins.py
+++ b/infinity/formats/tlk.py
@@ -1,148 +1,177 @@
-#-*-python-*-
-
-import os.path
-
-from ie_shell import core
-from ie_shell.formats.stream import ResourceStream, FileStream
-
-###################################################
-def load_game (game_dir, chitin_file = core.chitin_file, dialog_file = core.dialog_file):
-    """Loads key and dialog files from the specified directory.
-    The directory parameter is mandatory, the others are optional.
-    Most of the other commands assume that these two files are
-    already loaded. The loaded objects are stored in core.keys and
-    core.strrefs."""
-
-    core.game_dir = game_dir
-    core.chitin_file = chitin_file
-    core.dialog_file = dialog_file
-    
-    # Load RESREF index file (CHITIN.KEY)
-    core.keys = core.get_format ('KEY') (os.path.join (game_dir, chitin_file))
-    core.keys.decode_header ()
-    print "Loading %d file refs and %d RESREFs. This may take ages" %(core.keys.header['num_of_bifs'], core.keys.header['num_of_resrefs'])
-    core.keys.decode_file ()
+# -*-python-*-
+# ie_shell.py - Simple shell for Infinity Engine-based game files
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.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.
 
 
-    # LOAD STRREF index file (DIALOG.TLK)
-    core.strrefs = core.get_format ('TLK') (os.path.join (game_dir, dialog_file))
-    core.strrefs.decode_header ()
-    print "Loading %d STRREFs. This may take eternity" %(core.strrefs.header['num_of_strrefs'])
-    core.strrefs.decode_file ()
+import re
+import string
+import sys
+
+from infinity import core
+from infinity.format import Format, register_format
+
+class TLK_Format (Format):
+    
+    header_desc = (
+        { 'key': 'signature',
+          'type': 'STR4',
+          'off': 0x0000,
+          'label': 'Signature' },
+            
+        { 'key': 'version',
+          'type': 'STR4',
+          'off':0x0004,
+          'label': 'Version'},
+            
+        { 'key': 'unknown',
+          'type': 'WORD',
+          'off': 0x0008,
+          'label': '???' },
+
+        { 'key': 'num_of_strrefs',
+          'type': 'DWORD',
+          'off': 0x000A,
+          'label': '# of strref entries'},
+            
+        { 'key': 'string_offset',
+          'type': 'DWORD',
+          'off': 0x000E,
+          'label': 'First string data offset'},
+       )
+        
+
+    strref_record_desc = (
+        { 'key': 'content_type',
+          'type': 'WORD',
+          'off': 0x0000,
+          'label': 'Content of this entry' },
+       
+        { 'key': 'sound_resref',
+          'type': 'RESREF',
+          'off': 0x0002,
+          'label': 'Sound resref' },
+         
+        { 'key': 'volume_variance',
+          'type': 'DWORD',
+          'off': 0x000A,
+          'label': 'Volume variance' },
+          
+        { 'key': 'pitch_variance',
+          'type': 'DWORD',
+          'off': 0x000E,
+          'label': 'Pitch variance' },
+            
+        { 'key': 'string_offset',
+          'type': 'DWORD',
+          'off': 0x0012,
+          'label': 'String data rel offset' },
+
+        { 'key': 'string_len',
+          'type': 'DWORD',
+          'off': 0x0016,
+          'label': 'String data size' },
+
+        { 'key': 'string',
+          'type': '_STRING',
+          'off': 0x0000,
+          'label': 'String' },
+
+        )
+
+    def __init__ (self):
+        Format.__init__ (self)
+        
+        self.expect_signature = 'TLK'
+
+        self.strref_list = []
+        
+
+    def read (self, stream):
+        self.read_header (stream)
+
+        off = 0x0012
+            
+        if not self.get_option ('format.tlk.decode_strrefs'):
+            return
+        
+        tick_size = core.get_option ('format.tlk.tick_size')
+        tack_size = core.get_option ('format.tlk.tack_size')
+
+        for i in range (self.header['num_of_strrefs']):
+            
+            obj = {}
+            self.read_strref_record (stream, off, obj)
+            self.strref_list.append (obj)
+            off = off + 26
+
+            if not (i % tick_size):
+                sys.stdout.write('.')
+                if not (i % tack_size):
+                    sys.stdout.write('%d' %i)
+                sys.stdout.flush ()
+        print
+
+    def write (self, stream):
+        self.header['num_strings'] = len (self.strref_list)
+        self.header['string_offset'] = self.get_struc_size (self.header_desc, self.header) + len (self.strref_list) * self.get_struc_size (self.strref_record_desc, None)
+        self.write_struc (stream, 0x0000, self.header_desc, self.header)
+        
+        strref_offset = self.get_struc_size (self.header_desc, self.header)
+        string_offset = 0
+        strref_size = self.get_struc_size (self.strref_record_desc, None)
+        for strref in self.strref_list:
+            # FIXME: possibly test strref type instead
+            if len (strref['string']) != 0:
+                strref['string_offset'] = string_offset
+                stream.write_sized_string (strref['string'], self.header['string_offset'] + string_offset, len (strref['string']))
+            else:
+                strref['string_offset'] = 0
+            self.write_struc (stream, strref_offset, self.strref_record_desc, strref)
+            # FIXME: or raw_string ?
+            strref_offset += strref_size
+            string_offset += len (strref['string'])
+        
+
+    def printme (self):
+        self.print_header ()
+
+        i = 0
+        for obj in self.strref_list:
+            print '#%d' %i
+            self.print_strref_record (obj)
+            i = i + 1
 
 
-###################################################
-def load_object (name, type = None):
-
-    try:
-        fh = open (name)
-    except:
-        fh = None
-
-    if fh:
-        fh.close ()
-        return FileStream(name).load_object ()
-    else:
-        return ResourceStream(name, type).load_object ()
-
-    
-###################################################
-def find_str (text):
-    """Finds all strings in loaded DIALOG.TLK file matching regular expression
-    and prints their STRREFs"""
-    
-    for o in core.strrefs.get_strref_by_str_re(text):
-        print core.strrefs.strref_list.index(o), o['string']
-
-###################################################
-def export_obj (name, filename, type = None, index = 0):
-    """Exports resource `name' into file `filename'. If the `name' is not
-    unique, specify resource type with `type' and eventually `index' if
-    there's still more than one"""
-    
-    oo = core.keys.get_resref_by_name_re(name)
-    if type != None:
-        oo = filter (lambda o: o['type'] == type, oo)
-
-    if len (oo) > 1 and type == None:
-        print "More than one result"
-        return
-
-    o = oo[index]
-     
-    src_file = core.keys.bif_list[o['locator_src_ndx']]
-    b = core.formats['BIFF'] (os.path.join (core.game_dir, src_file['file_name']))
-    b.decode_file ()
-    b.save_file_data (filename, b.file_list[o['locator_ntset_ndx']])
+    def read_strref_record (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.strref_record_desc, obj)
+        obj['string'] = stream.read_sized_string (self.header['string_offset'] + obj['string_offset'], obj['string_len'])
+        obj['string_raw'] = obj['string']
+        if core.lang_trans:
+            obj['string'] = string.translate (obj['string'], core.lang_trans)
+        
+    def print_strref_record (self, obj):
+        self.print_struc (obj, self.strref_record_desc)
 
 
-###################################################
-def iterate_objects_by_type (type, fn):
 
-    # FIXME: this function opens and decodes a bif file EACH time some
-    #   object from it is accessed, so it's slow as hell. It should use
-    #   some caching
+    def get_strref_by_str_re (self, text):
+        rx = re.compile (text)
+        return filter (lambda s, rx=rx: rx.search (s['string']), self.strref_list)
 
-    #def resref_to_obj (res):
-    #    print res['resref_name']
-    #    return ResourceStream (res['resref_name'], type).load_object ()
-
-    for res in filter (lambda res: res['type'] == type, core.keys.resref_list):
-        print res['resref_name']
-        obj = ResourceStream (res['resref_name'], type).load_object ()
-        fn (obj)
-    
-
-###################################################
-def sprintf (format_str, params):
-    return  format_str %(params)
-
-def printf (format_str, params):
-    print sprintf (format_str, params)
-
-def loaded_object (obj):
-    obj.decode_file ()
-    return obj
-
-###################################################
-def pok():
-    def p (obj):
-        obj.decode_file ()
-        obj.print_file ()
-
-    iterate_objects_by_type (0x03ed, p)
-
-###################################################
-def load_ids ():
-    def p (obj):
-        obj.decode_file ()
-        print obj.stream.resref
-        #obj.print_file ()
-        
-    iterate_objects_by_type (0x03f0, p)
-
-###################################################
-def get_restype_stats ():
-    stats = {}
-    for o in core.keys.resref_list:
-        if not stats.has_key (o['type']):
-            stats[o['type']] = 1
-        else:
-            stats[o['type']] = stats[o['type']] + 1
-
-    return stats
 
         
-###################################################
-def print_restype_stats ():
-    stats = get_restype_stats ()
-    for s in stats.keys ():
-        if core.restype_hash.has_key (s):
-            type = core.restype_hash[s]
-        else:
-            type = '??'
-        print "0x%04x (%s):\t%5d" %(s, type, stats[s])
-
-
-###################################################
-# End of file builtins.py
+register_format ('TLK', 'V1', TLK_Format)
ie_shell/core.py to infinity/core.py
--- a/ie_shell/core.py
+++ b/infinity/core.py
@@ -1,7 +1,30 @@
 # -*-python-*-
+# iesh / ie_shell.py - Simple shell for Infinity Engine-based game files
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.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.
+
+"""
+Core and global definitions for the `infinity' package
+
+"""
 
 import string
-
+import sys
+
+import defaults
 
 global formats
 formats = {}
@@ -13,24 +36,27 @@
 keys = None
 
 global options
-options = {}
+options = defaults.options
 
 # Loaded IDS files
 global ids
 ids = {}
 
+# These variables are filled after call to load_game()
 game_dir = None
-chitin_file = 'CHITIN.KEY'
-dialog_file = 'dialog.tlk'
+chitin_file = None
+dialog_file = None
 
 xor_key = "\x88\xa8\x8f\xba\x8a\xd3\xb9\xf5\xed\xb1\xcf\xea\xaa\xe4\xb5\xfb\xeb\x82\xf9\x90\xca\xc9\xb5\xe7\xdc\x8e\xb7\xac\xee\xf7\xe0\xca\x8e\xea\xca\x80\xce\xc5\xad\xb7\xc4\xd0\x84\x93\xd5\xf0\xeb\xc8\xb4\x9d\xcc\xaf\xa5\x95\xba\x99\x87\xd2\x9d\xe3\x91\xba\x90\xca"
-
+"""Key used to `encrypt' some objects in IE files by XOR"""
 
 global slash_trans
 slash_trans = string.maketrans ('\\', '/')
 
 t_cp1250 = '\xe1\xe8\xef\xe9\xec\xed\xf2\xf3\xf8\x9a\x9d\xfa\xf9\xfd\x9e\xc1\xc8\xcf\xc9\xcc\xcd\xd2\xd3\xd8\x8a\x8d\xda\xd9\xdd\x8e'
 t_iso8859_2 = '\xe1\xe8\xef\xe9\xec\xed\xf2\xf3\xf8\xb9\xbb\xfa\xf9\xfd\xbe\xc1\xc8\xcf\xc9\xcc\xcd\xd2\xd3\xd8\xa9\xab\xda\xd9\xdd\xae'
+
+
 
 global lang_trans
 lang_trans = string.maketrans (t_cp1250, t_iso8859_2)
@@ -156,14 +182,22 @@
     }
 
 
-def register_format (signature, version, klass):
+def register_format (signature, version, klass,  desc = None):
+    """Register class `klass' for reading, parsing and (possibly) writing
+    IE file format with given `signature' and `version'.  `desc' allows
+    to specify text displayed in format list and should be used to 
+    describe status/progress of implementation."""
+
     #core.formats[(signature, version)] = klass
-    formats[signature] = klass
+    #formats[signature] = klass
+    formats[(signature,  version)] = (klass,  desc)
+    # FIXME: this is ugly temporary hack
+    formats[(signature,  None)] = (klass,  desc)
 
 
 def get_format (signature, version = None):
     try:
-        return formats[signature]
+        return formats[(signature,  version)][0]
     except:
         return None
 
@@ -175,3 +209,50 @@
     except:
         return None
 
+def id_to_symbol (idsfile, id):
+    # FIXME: ugly
+    import traceback
+    from stream import ResourceStream
+    idsfile = idsfile.upper ()
+    
+    if not ids.has_key (idsfile):
+        try:
+            # FIXME: ugly & should use 'IDS' instead of 0x3F0
+            idsobj = ResourceStream ().open (idsfile, 0x03F0).load_object ()
+            #idsobj.read ()
+            ids[idsfile] = idsobj
+        except Exception, e:
+            traceback.print_exc()
+            print e
+            return id
+
+    try:
+        return ids[idsfile].ids[id]
+    except KeyError, e:
+        sys.stderr.write ("Warning: No such id %d in %s\n" %(id, idsfile))
+        return id
+        
+    #try:
+    #    return core.ids[idsfile.upper ()].ids[id]
+    #except:
+    #    return None
+    
+
+def get_option (key):
+    try:
+        return options[key][0]
+    except KeyError:
+        raise RuntimeError ("Unknown option `%s'" %key)
+
+def set_option (key,  value,  desc = None):
+    try:
+        opt = options[key]
+        opt[0] = value
+    except KeyError:
+        # option does not exist yet, require desc and create it
+        if desc is None:
+            raise RuntimeError ("Unknown option `%s', `desc' required to create it" %key)
+        options[key] = [value,  desc]
+
+
+# End of file core.py
ie_shell/formats/__init__.py to infinity/formats/__init__.py
--- a/ie_shell/formats/__init__.py
+++ b/infinity/formats/__init__.py
@@ -1,18 +1,20 @@
 # -*-python-*-
-
-import format
 
 import are
 import bam
+import bcs
 import biff
 import chui
 import cre
+import d2a
 import dlg
 import ids
 import itm
 import key
+import mos
 import pro
 import spl
+import stor
 import tis
 import tlk
 import vvc
ie_shell/formats/are.py to infinity/formats/mos.py
--- a/ie_shell/formats/are.py
+++ b/infinity/formats/mos.py
@@ -1,6 +1,6 @@
 # -*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,17 +16,16 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: pro.py,v 1.1 2006/07/08 14:29:26 edheldil Exp $
-
-from ie_shell.formats.format import Format, register_format, core
-
-class ARE_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'AREA'
-
-
-        self.header_desc = (
+
+import struct
+import sys
+
+from infinity.format import Format, register_format
+from infinity.stream import CompressedStream
+
+
+class MOS_Format (Format):
+    header_desc = (
             { 'key': 'signature',
               'type': 'STR4',
               'off': 0x0000,
@@ -37,222 +36,240 @@
               'off':0x0004,
               'label': 'Version'},
             
-            { 'key': 'wed',
-              'type': 'RESREF',
+            { 'key': 'width',
+              'type': 'WORD',
               'off': 0x0008,
-              'label': 'Corresponding WED file' },
-
-            { 'key': 'unsaved_time',
+              'label': 'Width (pixels)'},
+            
+            { 'key': 'height',
+              'type': 'WORD',
+              'off': 0x000A,
+              'label': 'Height (pixels)'},
+
+            { 'key': 'columns',
+              'type': 'WORD',
+              'off': 0x000C,
+              'label': 'Columns (blocks)'},
+
+            { 'key': 'rows',
+              'type': 'WORD',
+              'off': 0x000E,
+              'label': 'Rows (blocks)'},
+
+            { 'key': 'block_size',
               'type': 'DWORD',
               'off': 0x0010,
-              'label': 'Seconds since last save' },
-
-
-            { 'key': 'area_flag',
+              'label': 'Block size (pixels)'},
+
+            { 'key': 'palette_off',
               'type': 'DWORD',
               'off': 0x0014,
-              'mask': {
-                  0x01: 'can save',
-                  0x02: 'tutorial',
-                  0x04: 'dead magic',
-                  0x08: 'dream'
-                  },
-              'label': 'Area flag (AREAFLAG.IDS)'},
-
-            { 'key': 'north_area',
-              'type': 'RESREF',
-              'off': 0x0018,
-              'label': 'Area to the North'},
-
-            { 'key': 'unknown_20',
+              'label': 'Palettes offset'},
+            )
+
+    # NOTE: not actually used
+    tile_desc = (
+            { 'key': 'tile_data',
+              'type': '_BYTES',
+              'off': 0x0000,
+              'label': 'Tile data'},
+
+            { 'key': 'palette',
+              'type': '_BYTES',
+              'off': 0x0000,
+              'label': 'Palette'},
+            )
+
+    palette_entry_desc = (
+            { 'key': 'b',
+              'type': 'BYTE',
+              'off': 0x0000,
+              'label': 'B'},
+
+            { 'key': 'g',
+              'type': 'BYTE',
+              'off': 0x0001,
+              'label': 'G'},
+
+            { 'key': 'r',
+              'type': 'BYTE',
+              'off': 0x0002,
+              'label': 'R'},
+
+            { 'key': 'a',
+              'type': 'BYTE',
+              'off': 0x0003,
+              'label': 'A'},
+            )
+
+
+    def __init__ (self):
+        Format.__init__ (self)
+        self.expect_signature = 'MOS'
+
+        self.tile_list = []
+
+
+    def read (self, stream):
+        self.read_header (stream)
+
+        tile_cnt = self.header['columns'] * self.header['rows']
+        off = self.header['palette_off']
+        offset_tiles = self.header['palette_off'] + tile_cnt * (256 * 4 + 4)
+        
+        for i in range (tile_cnt):
+            obj = {}
+            palette = []
+            self.read_palette (stream, off, palette)
+            obj['palette'] = palette
+            self.tile_list.append (obj)
+            off += 256 * 4
+
+        i = 0
+        for y in range (self.header['rows']):
+            for x in range (self.header['columns']):
+                obj = self.tile_list[i]
+                obj['width'] = min (self.header['width'] - x * self.header['block_size'], self.header['block_size'])
+                obj['height'] = min (self.header['height'] - y * self.header['block_size'], self.header['block_size'])
+                obj['offset'] = offset_tiles + stream.read_dword (off)
+                
+                self.read_tile (stream, obj)
+                off += 4
+                i += 1
+
+        return self
+
+
+    def printme (self):
+        self.print_header ()
+
+        if self.get_option ('format.mos.print_palettes'):
+            i = 0
+            for obj in self.tile_list:
+                print 'Palette #%d' %i
+                self.print_palette (obj['palette'])
+                i = i + 1
+
+        if self.get_option ('format.mos.print_tiles'):
+            i = 0
+            for obj in self.tile_list:
+                print 'Tile #%d' %i
+                #print obj['offset']
+                self.print_tile (obj)
+                i = i + 1
+
+
+    def read_palette (self, stream, offset, obj):
+        for i in range (256):
+            obj2 = {}
+            self.read_struc (stream, offset, self.palette_entry_desc, obj2)
+            obj.append (obj2)
+            offset = offset + 4
+
+        return obj
+
+    def print_palette (self, palette):
+        i = 0
+        for obj in palette:
+            print "%3d: %3d %3d %3d %3d (#%02x%02x%02x%02x)" %(i, obj['r'], obj['g'], obj['b'], obj['a'], obj['r'], obj['g'], obj['b'], obj['a'])
+            i = i + 1
+
+
+    def read_tile (self, stream, obj):
+        size = obj['width'] * obj['height']
+        bin_data = stream.read_blob (obj['offset'], size)
+        obj['tile_data'] = struct.unpack ('%dB' %size, bin_data)
+
+
+    def print_tile (self, obj):
+        gray = ' #*+:.'
+        grsz = len (gray) - 1
+        ndx = 0
+
+        for i in range (obj['height']):
+            for j in range (obj['width']):
+                pix = obj['tile_data'][ndx]
+
+                p = obj['palette'][pix]
+                gr = 1 + (p['r'] + p['g'] + p['b']) / (3 * (255 / grsz))
+                if gr >= grsz:
+                    gr = grsz - 1
+                sys.stdout.write (gray[gr])
+                #sys.stdout.write (gray[gr])
+                #print gray[gr],
+                ndx = ndx + 1
+            print
+        print
+    
+    # FIXME: use stream instead of fh?
+    def write_ppm (self, fh):
+        fh.write ("P6\n")
+        fh.write ("# ie_shell\n");
+        fh.write ("%d %d\n" %(self.header['width'], self.header['height']));
+        fh.write ("255\n");
+
+        for line in range (self.header['height']):
+            row = line / self.header['block_size']
+            scanline = line % self.header['block_size']
+            
+            for i in range (self.header['columns']):
+                tile = self.tile_list[(row * self.header['columns']) + i]
+                pal = tile['palette']
+            
+                o = scanline * tile['width']
+                for x in range (tile['width']):
+                    pix = tile['tile_data'][o + x]
+                    col = pal[pix]
+                    fh.write ('%c%c%c' %(col['r'], col['g'], col['b']))
+
+
+class MOSC_Format (MOS_Format):
+    envelope_desc = (
+            { 'key': 'signature',
+              'type': 'STR4',
+              'off': 0x0000,
+              'label': 'Signature' },
+            
+            { 'key': 'version',
+              'type': 'STR4',
+              'off':0x0004,
+              'label': 'Version'},
+            
+            { 'key': 'uncompressed_size',
               'type': 'DWORD',
-              'off': 0x0020,
-              'label': 'Unknown north 20'},
-
-            { 'key': 'east_area',
-              'type': 'RESREF',
-              'off': 0x0024,
-              'label': 'Area to the East'},
-
-            { 'key': 'unknown_2C',
-              'type': 'DWORD',
-              'off': 0x002C,
-              'label': 'Unknown east 20'},
-
-            { 'key': 'south_area',
-              'type': 'RESREF',
-              'off': 0x0030,
-              'label': 'Area to the South'},
-
-            { 'key': 'unknown_38',
-              'type': 'DWORD',
-              'off': 0x0038,
-              'label': 'Unknown south 38'},
-
-            { 'key': 'west_area',
-              'type': 'RESREF',
-              'off': 0x003C,
-              'label': 'Area to the West'},
-
-            { 'key': 'unknown_44',
-              'type': 'DWORD',
-              'off': 0x0044,
-              'label': 'Unknown west 44'},
-
-
-            { 'key': 'flags',
-              'type': 'WORD',
-              'off': 0x0048,
-              'mask': {
-                  0x01: 'outdoor',
-                  0x02: 'day/night',
-                  0x04: 'weather',
-                  0x08: 'city',
-                  0x10: 'forest',
-                  0x20: 'dungeon',
-                  0x40: 'extended night',
-                  0x80: 'can rest inddors',
-                  },
-              'label': 'Flag (AREATYPE.IDS)'},
-
-
-            { 'key': 'rain_chance',
-              'type': 'WORD',
-              'off': 0x004A,
-              'label': 'Rain chance'},
-
-            { 'key': 'snow_chance',
-              'type': 'WORD',
-              'off': 0x004C,
-              'label': 'Snow chance'},
-
-            { 'key': 'fog_chance',
-              'type': 'WORD',
-              'off': 0x004E,
-              'label': 'Fog chance (unimpl)'},
-
-            { 'key': 'lightning_chance',
-              'type': 'WORD',
-              'off': 0x0050,
-              'label': 'Lightning chance'},
-
-            { 'key': 'unknown_52',
-              'type': 'WORD',
-              'off': 0x0052,
-              'label': 'Unknown 52'},
-
-
-            { 'key': 'actor_off',
-              'type': 'DWORD',
-              'off': 0x0054,
-              'label': 'Actors offset'},
-
-            { 'key': 'actor_cnt',
-              'type': 'WORD',
-              'off': 0x0058,
-              'label': '# of actors'},
-
-            { 'key': 'infopoint_cnt',
-              'type': 'WORD',
-              'off': 0x005A,
-              'label': '# of infopoints, triggerpoints and exits'},
-
-            { 'key': 'infopoint_off',
-              'type': 'DWORD',
-              'off': 0x005C,
-              'label': 'Offset of infopoints, triggerpoints and exits'},
-
-            { 'key': 'spawnpoint_off',
-              'type': 'DWORD',
-              'off': 0x0060,
-              'label': 'Spawnpoint offset'},
-
-            { 'key': 'spawnpoint_cnt',
-              'type': 'DWORD',
-              'off': 0x0064,
-              'label': '# of spawnpoints'},
-
-            { 'key': 'entrance_off',
-              'type': 'DWORD',
-              'off': 0x0068,
-              'label': 'Entrances offset'},
-
-            { 'key': 'entrance_cnt',
-              'type': 'DWORD',
-              'off': 0x006C,
-              'label': '# of entrances'},
-
-            { 'key': 'container_off',
-              'type': 'DWORD',
-              'off': 0x0070,
-              'label': 'Containers offset'},
-
-            { 'key': 'container_cnt',
-              'type': 'WORD',
-              'off': 0x0074,
-              'label': '# of containers'},
-
-            { 'key': 'item_cnt',
-              'type': 'WORD',
-              'off': 0x0076,
-              'label': '# of items'},
-
-            { 'key': 'item_off',
-              'type': 'DWORD',
-              'off': 0x0078,
-              'label': 'Item offset'},
-
-            { 'key': 'vertex_off',
-              'type': 'DWORD',
-              'off': 0x007C,
-              'label': 'Vertices offset'},
-
-            { 'key': 'vertex_cnt',
-              'type': 'WORD',
-              'off': 0x0080,
-              'label': '# of vertices'},
-
-            { 'key': 'ambient_cnt',
-              'type': 'WORD',
-              'off': 0x0082,
-              'label': '# of ambient sounds'},
-
-            { 'key': 'ambient_off',
-              'type': 'DWORD',
-              'off': 0x0084,
-              'label': 'Ambients offset'},
-
-            )
-
-
-
-
-    def decode_file (self):
-        self.decode_header ()
-#        if self.header['projectile_type'] == 3:
-#            self.decode_area_header ()
-
-
-    def print_file (self):
-        self.print_header ()
-#        if self.header['projectile_type'] == 3:
-#            self.print_area_header ()
-
-
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
+              'off': 0x0008,
+              'label': 'Uncompressed size'},
+            
+            )
+
+    def __init__ (self):
+        MOS_Format.__init__ (self)
+        self.expect_signature = 'MOSC'
+
+
+    def read (self, stream):
+        self.read_envelope (stream)
+        # FIXME: size??
+        data = stream.read_blob (0x0C)
+
+        #self.stream.close ()
+        stream = CompressedStream ().open (data)
+
+        return MOS_Format.read (self, stream)
+
+    def printme (self):
+        self.print_envelope ()
+        MOS_Format.printme (self)
+
+    def read_envelope (self, stream):
+        self.envelope = {}
+        self.read_struc (stream, 0x0000, self.envelope_desc, self.envelope)
         
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
+    def print_envelope (self):
+        self.print_struc (self.envelope, self.envelope_desc)
         
 
-#     def decode_area_header (self):
-#         self.area_header = {}
-#         self.decode_by_desc (0x0000, self.area_header_desc, self.area_header)
-        
-#     def print_area_header (self):
-#         self.print_by_desc (self.area_header, self.area_header_desc)
-
-        
-register_format ('AREA', 'V1.0', ARE_Format)
+
+
+register_format ('MOS', 'V1', MOS_Format)
+register_format ('MOSC', 'V1', MOSC_Format)
ie_shell/formats/bam.py to infinity/formats/dlg.py
--- a/ie_shell/formats/bam.py
+++ b/infinity/formats/dlg.py
@@ -1,6 +1,6 @@
 # -*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,27 +16,12 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: bam.py,v 1.3 2006/07/08 14:29:26 edheldil Exp $
-
-import struct
-import sys
-
-from ie_shell.formats.format import Format, register_format
-from ie_shell.formats.stream import CompressedStream
-
-
-class BAM_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'BAM'
-
-        self.frame_list = []
-        self.cycle_list = []
-        self.palette_entry_list = []
-        self.frame_lut_entry_list = []
-
-
-        self.header_desc = (
+
+from infinity import core
+from infinity.format import Format, register_format
+
+class DLG_Format (Format):
+    header_desc = [
             { 'key': 'signature',
               'type': 'STR4',
               'off': 0x0000,
@@ -47,337 +32,329 @@
               'off':0x0004,
               'label': 'Version'},
             
-            { 'key': 'frame_cnt',
-              'type': 'WORD',
+            { 'key': 'state_cnt',
+              'type': 'DWORD',
               'off': 0x0008,
-              'label': 'Frame count'},
-            
-            { 'key': 'cycle_cnt',
-              'type': 'BYTE',
-              'off': 0x000A,
-              'label': 'Cycle count'},
-
-            { 'key': 'comp_color_ndx',
-              'type': 'BYTE',
-              'off': 0x000B,
-              'label': 'Compressed color index'},
-
-            { 'key': 'transp_color_ndx',
-              'type': '_BYTE',
-              'off': 0x000B,
-              'label': 'Transparent color index'},
-
-            { 'key': 'frame_off',
+              'label': 'Number of states'},
+
+            { 'key': 'state_off',
               'type': 'DWORD',
               'off': 0x000C,
-              'label': 'Frame and Cycle entries offset'},
-
-            { 'key': 'palette_off',
+              'label': 'State table offset'},
+
+            { 'key': 'transition_cnt',
               'type': 'DWORD',
               'off': 0x0010,
-              'label': 'Palette offset'},
-
-            { 'key': 'frame_lut_off',
+              'label': 'Number of transitions'},
+
+            { 'key': 'transition_off',
               'type': 'DWORD',
               'off': 0x0014,
-              'label': 'Frame lookup table offset'},
+              'label': 'Transition table offset'},
+
+
+            { 'key': 'state_trigger_off',
+              'type': 'DWORD',
+              'off': 0x0018,
+              'label': 'State trigger table offset'},
+
+            { 'key': 'state_trigger_cnt',
+              'type': 'DWORD',
+              'off': 0x001C,
+              'label': 'Number of state triggers'},
+
+
+            { 'key': 'transition_trigger_off',
+              'type': 'DWORD',
+              'off': 0x0020,
+              'label': 'Transition trigger table offset'},
+
+            { 'key': 'transition_trigger_cnt',
+              'type': 'DWORD',
+              'off': 0x0024,
+              'label': 'Number of transition triggers'},
+
+
+            { 'key': 'action_off',
+              'type': 'DWORD',
+              'off': 0x0028,
+              'label': 'Action table offset'},
+
+            { 'key': 'action_cnt',
+              'type': 'DWORD',
+              'off': 0x002C,
+              'label': 'Number of actions'},
+
+            ]
+
+    state_desc = (
+            { 'key': 'npc_text',
+              'type': 'STRREF',
+              'off': 0x0000,
+              'label': 'NPC text'},
+
+            { 'key': 'first_transition_index',
+              'type': 'DWORD',
+              'off': 0x0004,
+              'label': 'First transition index'},
+
+            { 'key': 'transition_cnt',
+              'type': 'DWORD',
+              'off': 0x0008,
+              'label': 'Number of transitions'},
+
+            { 'key': 'trigger_index',
+              'type': 'DWORD',
+              'off': 0x000C,
+              'label': 'Trigger'},
+
             )
 
 
-        self.frame_desc = (
-            { 'key': 'width',
-              'type': 'WORD',
-              'off': 0x0000,
-              'label': 'Frame width'},
-
-            { 'key': 'height',
-              'type': 'WORD',
-              'off': 0x0002,
-              'label': 'Frame height'},
-
-            { 'key': 'xcenter',
-              'type': 'WORD',
+    transition_desc = (
+            { 'key': 'flags',
+              'type': 'DWORD',
+              'mask': {0x01:'has text', 0x02:'has trigger', 0x04:'has action', 0x08:'terminates dlg', 0x10:'journal entry', 0x20:'unknown', 0x40:'add quest journal', 0x80:'del quest journal', 0x100:'add done quest journal'},
+              'off': 0x0000,
+              'label': 'Flags'},
+
+            { 'key': 'pc_text',
+              'type': 'STRREF',
               'off': 0x0004,
-              'label': 'Frame center X'},
-
-            { 'key': 'ycenter',
-              'type': 'WORD',
-              'off': 0x0006,
-              'label': 'Frame center Y'},
-
-            { 'key': 'rle_encoded',
-              'type': 'DWORD',
+              'label': 'PC text'},
+
+            { 'key': 'journal_text',
+              'type': 'STRREF',
               'off': 0x0008,
-              'bits': '31-31',
-              'label': 'RLE encoded'},
-
-            { 'key': 'frame_data_off',
-              'type': 'DWORD',
-              'off': 0x0008,
-              'bits': '30-0',
-              'label': 'Frame data offset'},
+              'label': 'Journal text'},
+
+            { 'key': 'trigger_index',
+              'type': 'DWORD',
+              'off': 0x000C,
+              'label': 'Trigger'},
+
+            { 'key': 'action_index',
+              'type': 'DWORD',
+              'off': 0x0010,
+              'label': 'Action'},
+
+            { 'key': 'next_dialog',
+              'type': 'RESREF',
+              'off': 0x0014,
+              'label': 'Next dialog resref'},
+
+            { 'key': 'next_state',
+              'type': 'DWORD',
+              'off': 0x001C,
+              'label': 'Next state'},
+
             )
-
-        self.cycle_desc = (
-            { 'key': 'frame_cnt',
-              'type': 'WORD',
-              'off': 0x0000,
-              'label': 'Frame count'},
-
-            { 'key': 'frame_lut_ndx',
-              'type': 'WORD',
-              'off': 0x0002,
-              'label': 'Frame LUT index'},
-
-            { 'key': 'frames',
+        
+    script_desc = (
+            { 'key': 'script_off',
+              'type': 'DWORD',
+              'off': 0x0000,
+              'label': 'Script offset'},
+
+            { 'key': 'script_len',
+              'type': 'DWORD',
+              'off': 0x0004,
+              'label': 'Script size'},
+            
+            { 'key': 'code',
               'type': '_STRING',
               'off': 0x0000,
-              'label': 'Frame indices'},
+              'label': 'Script code'},
+            
             )
 
-        self.palette_entry_desc = (
-            { 'key': 'r',
-              'type': 'BYTE',
-              'off': 0x0000,
-              'label': 'R'},
-
-            { 'key': 'g',
-              'type': 'BYTE',
-              'off': 0x0001,
-              'label': 'G'},
-
-            { 'key': 'b',
-              'type': 'BYTE',
-              'off': 0x0002,
-              'label': 'B'},
-
-            { 'key': 'a',
-              'type': 'BYTE',
-              'off': 0x0003,
-              'label': 'A'},
-            )
-
-        self.frame_lut_entry_desc = (
-            { 'key': 'frame',
-              'type': 'WORD',
-              'off': 0x0000,
-              'label': 'Frame'},
-            )
-
-    def decode_file (self):
-        self.decode_header ()
-
-        off = self.header['frame_off']
-        for i in range (self.header['frame_cnt']):
+    def __init__ (self):
+        Format.__init__ (self)
+        self.expect_signature = 'DLG'
+
+        self.state_list = []
+        self.transition_list = []
+        self.state_trigger_list = []
+        self.transition_trigger_list = []
+        self.action_list = []
+
+
+
+    def read (self, stream):
+        self.read_header (stream)
+
+        self.read_list (stream,  'state')
+        self.read_list (stream,  'transition')
+        
+#        off = self.header['state_off']
+#        for i in range (self.header['state_cnt']):
+#            obj = {}
+#            self.read_state (stream, off, obj)
+#            self.state_list.append (obj)
+#            off = off + 16
+
+#        off = self.header['transition_off']
+#        for i in range (self.header['transition_cnt']):
+#            obj = {}
+#            self.read_transition (stream, off, obj)
+#            self.transition_list.append (obj)
+#            off = off + 32
+
+
+        off = self.header['state_trigger_off']
+        for i in range (self.header['state_trigger_cnt']):
             obj = {}
-            self.decode_frame (off, obj)
-            self.frame_list.append (obj)
-            off = off + 12
-
-        for i in range (self.header['cycle_cnt']):
+            self.read_script (stream, off, obj)
+            self.state_trigger_list.append (obj)
+            off = off + 8
+
+        off = self.header['transition_trigger_off']
+        for i in range (self.header['transition_trigger_cnt']):
             obj = {}
-            self.decode_cycle (off, obj)
-            self.cycle_list.append (obj)
-            off = off + 4
-
-        self.decode_palette (self.header['palette_off'])
-
-
-    def print_file (self):
+            self.read_script (stream, off, obj)
+            self.transition_trigger_list.append (obj)
+            off = off + 8
+
+        off = self.header['action_off']
+        for i in range (self.header['action_cnt']):
+            obj = {}
+            self.read_script (stream, off, obj)
+            self.action_list.append (obj)
+            off = off + 8
+
+
+#         off = self.header['feature_block_off'] + self.header['casting_feature_block_off'] * 48
+#         for i in range (self.header['casting_feature_block_cnt']):
+#             obj = {}
+#             self.decode_feature_block (off, obj)
+#             self.casting_feature_block_list.append (obj)
+#             off = off + 48
+
+
+    def printme (self):
         self.print_header ()
 
+        self.print_list ('state')
+        self.print_list ('transition')
+        
+#        i = 0
+#        for obj in self.state_list:
+#            print 'State #%d' %i
+#            self.print_state (obj)
+#            i = i + 1
+
+#        i = 0
+#        for obj in self.transition_list:
+#            print 'Transition #%d' %i
+#            self.print_transition (obj)
+#            i = i + 1
+
         i = 0
-        for obj in self.frame_list:
-            print 'Frame #%d' %i
-            self.print_frame (obj)
+        for obj in self.state_trigger_list:
+            print 'State trigger#%d' %i
+            self.print_script (obj)
             i = i + 1
 
         i = 0
-        for obj in self.cycle_list:
-            print 'Cycle #%d' %i
-            self.print_cycle (obj)
+        for obj in self.transition_trigger_list:
+            print 'Transition trigger #%d' %i
+            self.print_script (obj)
             i = i + 1
 
-        if self.get_option ('bam_print_palette'):
-            self.print_palette ()
-
-
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
+        i = 0
+        for obj in self.action_list:
+            print 'Action #%d' %i
+            self.print_script (obj)
+            i = i + 1
+
+
+#    def read_state (self, stream, offset, obj):
+#        self.read_struc (stream, offset, self.state_desc, obj)
+#
+#    def read_transition (self, stream, offset, obj):
+#        self.read_struc (stream, offset, self.transition_desc, obj)
+
+    def read_script (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.script_desc, obj)
+
+        obj['code'] = stream.read_sized_string (obj['script_off'], obj['script_len'])
+        obj['code_raw'] = obj['code']
+        #if core.lang_trans:
+        #    obj['code'] = string.translate (obj['code'], core.lang_trans)
+
+
+#         obj['feature_list'] = []
+#         off2 = self.header['feature_block_off'] + obj['feature_off'] * 48
+#         for j in range (obj['feature_cnt']):
+#             obj2 = {}
+#             self.decode_feature_block (off2, obj2)
+#             obj['feature_list'].append (obj2)
+#             off2 = off2 + 48
+            
+#    def print_state (self, obj):
+#        self.print_struc (obj, self.state_desc)
+#
+#    def print_transition (self, obj):
+#        self.print_struc (obj, self.transition_desc)
+
+    def print_script (self, obj):
+        self.print_struc (obj, self.script_desc)
+
+
+    def print_flow (self):
+        i = 0
+        for state in self.state_list:
+            print 'State %d:' %i
+            #self.print_state (state)
+            if (state['trigger_index'] != 0xffffffff):
+                print '  Trigger:', self.state_trigger_list[state['trigger_index']]['code']
+            if core.strrefs:
+                print '  Text:', core.strrefs.strref_list[state['npc_text']]['string']
+            else:
+                print '  Text:', state['npc_text']
+
+            j = 0
+            for transition in self.transition_list[state['first_transition_index']:state['first_transition_index']+state['transition_cnt']]:
+                print '\n  Trans %d:' %j
+                self.print_transition (transition)
+                print "\n"
+                j = j + 1
+                
+            i = i + 1
+
+    def print_transition (self, transition):
+        if (transition['flags'] & 0x02) and transition['trigger_index'] != 0xffffffff:
+            print '    Trigger:', self.transition_trigger_list[transition['trigger_index']]['code']
+
+        if (transition['flags'] & 0x01) and transition['pc_text'] != 0xffffffff:
+            if core.strrefs:
+                print '    Text:', core.strrefs.strref_list[transition['pc_text']]['string']
+            else:
+                print '    Text:', transition['pc_text']
+
+        if (transition['flags'] & 0x04) and transition['action_index'] != 0xffffffff:
+            print '    Action:', self.action_list[transition['action_index']]['code']
+
+        if not (transition['flags'] & 0x08):
+            print '    ->', transition['next_dialog'], ":", transition['next_state']
+        else:
+            print '    -> FIN'
+
+
+
+class DLG_V19_Format (DLG_Format):
+    """Referenced in IESDP, but where is it used? And what's the signature???"""
+    
+    # FIXME: struc descs are not inherited from parent class!!!
+#    DLG_V19_Format.header_desc.append (
+#            { 'key': 'flags',
+#              'type': 'DWORD',
+#              'off': 0x0030,
+#              'label': 'Flags'},
+#              )
+
+    def __init__ (self):
+        DLG_Format.__init__ (self)
         
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
-        
-
-    def decode_frame (self, offset, obj):
-        self.decode_by_desc (offset, self.frame_desc, obj)
-
-        if self.get_option ('bam_decode_frame_data'):
-            if self.get_option ('bam_force_rle') or obj['rle_encoded']:
-                self.decode_rle_frame_data (obj)
-            else:
-                self.decode_frame_data (obj)
-    
-    def print_frame (self, obj):
-        self.print_by_desc (obj, self.frame_desc)
-
-        if self.get_option ('bam_print_frame_bitmap'):
-            self.print_frame_bitmap (obj)
-
-    def decode_cycle (self, offset, obj):
-        self.decode_by_desc (offset, self.cycle_desc, obj)
-        obj['frame_list'] = []
-        
-        off2 = self.header['frame_lut_off'] + 2 * obj['frame_lut_ndx']
-        obj2 = {}
-        for i in range (obj['frame_cnt']):
-            self.decode_by_desc (off2, self.frame_lut_entry_desc, obj2)
-            obj['frame_list'].append (obj2['frame'])
-            obj['frames'] = obj['frames'] + str(obj2['frame']) + ' '
-            off2 = off2 + 2
-
-    def print_cycle (self, obj):
-        self.print_by_desc (obj, self.cycle_desc)
-
-
-    def decode_palette (self, offset):
-        transp_color = None
-        
-        for i in range (256):
-            obj = {}
-            self.decode_by_desc (offset, self.palette_entry_desc, obj)
-            self.palette_entry_list.append (obj)
-
-            if transp_color == None and obj['r'] == 0 and obj['g'] == 255 and obj['b'] == 0:
-                transp_color = i
-
-            offset = offset + 4
-
-        if transp_color == None:
-            transp_color = 0
-        self.header['transp_color_ndx'] = transp_color
-
-
-    def print_palette (self):
-        i = 0
-        for obj in self.palette_entry_list:
-            print "%3d: %3d %3d %3d %3d (#%02x%02x%02x%02x)" %(i, obj['r'], obj['g'], obj['b'], obj['a'], obj['r'], obj['g'], obj['b'], obj['a'])
-            i = i + 1
-
-
-    def decode_frame_data (self, obj):
-        size = obj['width'] * obj['height']
-        bin_data = self.stream.decode_blob (obj['frame_data_off'], size)
-        obj['frame_data'] = struct.unpack ('%dB' %size, bin_data)
-
-    def decode_rle_frame_data (self, obj):
-        off = obj['frame_data_off']
-        size = obj['width'] * obj['height']
-        compressed_color = self.header['comp_color_ndx']
-
-        data = []
-        while len (data) < size:
-            pix = struct.unpack ('B', self.stream.get_char (off))[0]
-            if pix == compressed_color:
-                off = off + 1
-                cnt = struct.unpack ('B', self.stream.get_char (off))[0]
-                for j in range (cnt + 1):
-                    data.append (compressed_color)
-            else:
-                data.append (pix)
-
-            off = off + 1
-            
-        obj['frame_data'] = data
-
-    def print_frame_data (self, obj):
-        ndx = 0
-        for i in range (obj['height']):
-            for j in range (obj['width']):
-                print '%3d' %obj['frame_data'][ndx],
-                ndx = ndx + 1
-            print
-        print
-
-    def print_frame_bitmap (self, obj):
-        gray = ' #*+:.'
-        grsz = len (gray) - 1
-        transparent_color = self.header['transp_color_ndx']
-        ndx = 0
-        for i in range (obj['height']):
-            for j in range (obj['width']):
-                pix = obj['frame_data'][ndx]
-                if pix == transparent_color:
-                    gr = 0
-                else:
-                    p = self.palette_entry_list[pix]
-                    gr = 1 + (p['r'] + p['g'] + p['b']) / (3 * (255 / grsz))
-                    if gr >= grsz:
-                        gr = grsz - 1
-                sys.stdout.write (gray[gr])
-                #sys.stdout.write (gray[gr])
-                #print gray[gr],
-                ndx = ndx + 1
-            print
-        print
-        
-
-
-class BAMC_Format (BAM_Format):
-    def __init__ (self, filename):
-        BAM_Format.__init__ (self, filename)
-        self.expect_signature = 'BAMC'
-
-
-        self.envelope_desc = (
-            { 'key': 'signature',
-              'type': 'STR4',
-              'off': 0x0000,
-              'label': 'Signature' },
-            
-            { 'key': 'version',
-              'type': 'STR4',
-              'off':0x0004,
-              'label': 'Version'},
-            
-            { 'key': 'uncompressed_size',
-              'type': 'DWORD',
-              'off': 0x0008,
-              'label': 'Uncompressed size'},
-            
-            )
-
-    def decode_file (self):
-        self.decode_envelope ()
-        data = self.stream.decode_blob (0x0C)
-
-        self.stream.close ()
-        self.stream = CompressedStream (data)
-
-        BAM_Format.decode_file (self)
-
-    def print_file (self):
-        self.print_envelope ()
-        BAM_Format.print_file (self)
-
-    def decode_envelope (self):
-        self.envelope = {}
-        self.decode_by_desc (0x0000, self.envelope_desc, self.envelope)
-        
-    def print_envelope (self):
-        self.print_by_desc (self.envelope, self.envelope_desc)
-        
-
-
-# assume frame data is always RLE encoded
-BAM_Format.default_options['bam_force_rle'] = 1
-
-# decode and load frame data
-BAM_Format.default_options['bam_decode_frame_data'] = 1
-BAM_Format.default_options['bam_print_frame_bitmap'] = 1
-BAM_Format.default_options['bam_print_palette'] = 1
-
-
-register_format ('BAM', 'V1', BAM_Format)
-register_format ('BAMC', 'V1  ', BAMC_Format)
+register_format ('DLG', 'V1.0', DLG_Format)
+#register_format ('DLG', 'V1.09', DLG_V19_Format) # What's the signature????
ie_shell/formats/biff.py to infinity/formats/biff.py
--- a/ie_shell/formats/biff.py
+++ b/infinity/formats/biff.py
@@ -1,6 +1,6 @@
 # -*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,20 +16,14 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: biff.py,v 1.3 2006/07/08 14:29:26 edheldil Exp $
 
 import gzip
-from ie_shell.formats.format import Format, register_format
+from infinity.format import Format, register_format
+from infinity.stream import CompressedStream
+
 
 class BIFF_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'BIFF'
-
-        self.file_list = []
-        self.tileset_list = []
-
-        self.header_desc = (
+    header_desc = (
             { 'key': 'signature',
               'type': 'STR4',
               'off': 0x0000,
@@ -57,7 +51,7 @@
             )
         
 
-        self.file_record_desc = (
+    file_record_desc = (
             { 'key': 'locator',
               'type': 'DWORD',
               'off': 0x0000,
@@ -78,13 +72,13 @@
               'off': 0x000C,
               'label': 'File res type' },
             
-            { 'key': 'unknown',
+            { 'key': 'unknown_0E',
               'type': 'WORD',
               'off': 0x000E,
-              'label': '???' },
+              'label': 'Unknown 0E' },
             )
 
-        self.tileset_record_desc = (
+    tileset_record_desc = (
             { 'key': 'locator',
               'type': 'DWORD',
               'off': 0x0000,
@@ -117,24 +111,34 @@
 
             )
 
-    def decode_file (self):
-        self.decode_header ()
+    def __init__ (self):
+        Format.__init__ (self)
+        self.expect_signature = 'BIFF'
+
+        self.file_list = []
+        self.tileset_list = []
+
+
+    def read (self, stream):
+        self.read_header (stream)
 
         off = self.header['files_offset']
         for i in range (self.header['num_of_files']):
             obj = {}
-            self.decode_file_record (off, obj)
+            self.read_file_record (stream, off, obj)
             self.file_list.append (obj)
             off = off + 16
 
         for i in range (self.header['num_of_tilesets']):
             obj = {}
-            self.decode_tileset_record (off, obj)
+            self.read_tileset_record (stream, off, obj)
             self.tileset_list.append (obj)
             off = off + 20
 
-
-    def print_file (self):
+        if self.get_option ('format.biff.read_data'):
+            self.read_all_data (stream)
+
+    def printme (self):
         self.print_header ()
 
         i = 0
@@ -150,57 +154,59 @@
             i = i + 1
 
 
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
-        
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
-        
-
-    def decode_file_record (self, offset, obj):
-        self.decode_by_desc (offset, self.file_record_desc, obj)
+    def read_file_record (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.file_record_desc, obj)
         
     def print_file_record (self, obj):
-        self.print_by_desc (obj, self.file_record_desc)
-
-
-    def decode_tileset_record (self, offset, obj):
-        self.decode_by_desc (offset, self.tileset_record_desc, obj)
+        self.print_struc (obj, self.file_record_desc)
+
+
+    def read_tileset_record (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.tileset_record_desc, obj)
         
     def print_tileset_record (self, obj):
-        self.print_by_desc (obj, self.tileset_record_desc)
-
-
-
-    def get_ntset_data (self, obj):
-        obj['data'] = self.stream.decode_blob (obj['data_offset'], obj['data_size'])
-
-    def get_tileset_data (self, obj):
+        self.print_struc (obj, self.tileset_record_desc)
+
+    def read_all_data (self, stream):
+        for obj in self.file_list:
+            self.read_ntset_data (stream, obj)
+                
+        for obj in self.tileset_list:
+            self.read_tileset_data (stream, obj)
+
+
+    def read_ntset_data (self, stream, obj):
+        obj['data'] = stream.read_blob (obj['data_offset'], obj['data_size'])
+
+    def read_tileset_data (self, stream, obj):
         # FIXME: also add bytes for the TIS header
-        obj['data'] = self.stream.decode_blob (obj['data_offset'], obj['tile_size'] * obj['tile_cnt'])
-
-
-    def get_file_data (self, obj):
+        obj['data'] = stream.read_blob (obj['data_offset'], obj['tile_size'] * obj['tile_cnt'])
+
+    # FIXME: the following API is ugly
+
+    def get_file_data (self, stream, obj):
+        # avoid rereading already read data and also do not replace data read from uncompressed stream
+        #   in the case of a CBF file
+        if obj.has_key ('data'):
+            return obj['data']
+            
         if obj.has_key ('tile_cnt'):
-            return self.get_tileset_data (obj)
+            return self.read_tileset_data (stream, obj)
         else:
-            return self.get_ntset_data (obj)
-
-
-    def save_file_data (self, filename, obj):
-        self.get_file_data (obj)
+            return self.read_ntset_data (stream, obj)
+
+    # FIXME: this is ugly
+
+    def save_file_data (self, stream, filename, obj):
+        # FIXME: does not work with CBF files and the outer stream
+        self.get_file_data (stream, obj)
         fh = open (filename, 'w')
         fh.write (obj['data'])
         fh.close ()
         
 
-class BIFC_V1_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'BIF '
-
-        self.header_desc = (
+class BIFC_V1_Format (BIFF_Format):
+    envelope_desc = (
             { 'key': 'signature',
               'type': 'STR4',
               'off': 0x0000,
@@ -233,29 +239,36 @@
             
              )
 
-    def decode_file (self):
-        self.decode_header ()
-
-        self.decode_by_desc (0x000c + self.header['filename_len'], (self.header_desc[4], ), self.header)
-        self.decode_by_desc (0x0010 + self.header['filename_len'], ( self.header_desc[5], ), self.header)
-
-        #self.stream.seek (..)
-        data = self.stream.decode_blob (0x0014 + self.header['filename_len'], self.header['compressed_size'])
-        self.data = gzip.zlib.decompress (data)
-
-
-    def print_file (self):
-        self.print_header ()
-
-
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
-        
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
-        
-
+    def __init__ (self):
+        BIFF_Format.__init__ (self)
+        self.expect_signature = 'BIF '
+
+    def read (self, stream):
+        self.read_envelope (stream)
+
+        self.read_struc (stream, 0x000C + self.envelope['filename_len'], (self.envelope_desc[4], ), self.envelope)
+        self.read_struc (stream, 0x0010 + self.envelope['filename_len'], ( self.envelope_desc[5], ), self.envelope)
+
+        data = stream.read_blob (0x0014 + self.envelope['filename_len'], self.envelope['compressed_size'])
+        stream2 = CompressedStream ().open (data)
+        res = BIFF_Format.read (self, stream2)
+        # read the data with uncompressed stream while we have it
+        self.read_all_data (stream2)
+        return res
+        
+
+    def printme (self):
+        self.print_envelope ()
+        print
+        BIFF_Format.printme (self)
+
+    def read_envelope (self, stream):
+        self.envelope = {}
+        self.read_struc (stream, 0x0000, self.envelope_desc, self.envelope)
+        
+    def print_envelope (self):
+        self.print_struc (self.envelope, self.envelope_desc)
+        
 
 
 register_format ('BIFF', 'V1', BIFF_Format)
ie_shell/formats/chui.py to infinity/formats/itm.py
--- a/ie_shell/formats/chui.py
+++ b/infinity/formats/itm.py
@@ -1,6 +1,6 @@
 # -*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,19 +16,11 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: chui.py,v 1.1 2005/03/02 20:44:22 edheldil Exp $
-
-from ie_shell.formats.format import Format, register_format
-
-class CHUI_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'CHUI'
-
-        self.window_list = []
-
-
-        self.header_desc = (
+
+from infinity.format import Format, register_format
+
+class ITM_Format (Format):
+    header_desc = (
             { 'key': 'signature',
               'type': 'STR4',
               'off': 0x0000,
@@ -39,482 +31,525 @@
               'off':0x0004,
               'label': 'Version'},
             
-            { 'key': 'num_of_windows',
-              'type': 'DWORD',
+            { 'key': 'item_name',
+              'type': 'STRREF',
               'off': 0x0008,
-              'label': '# of windows'},
+              'label': 'Item name'},
             
-            { 'key': 'control_table_offset',
-              'type': 'DWORD',
+            { 'key': 'item_name_identified',
+              'type': 'STRREF',
               'off': 0x000C,
-              'label': 'Control table offset'},
-
-            { 'key': 'window_offset',
-              'type': 'DWORD',
+              'label': 'Item Name Identified'},
+
+            { 'key': 'drop_sound',
+              'type': 'RESREF',
               'off': 0x0010,
-              'label': 'First window offset'},
+              'label': 'Drop sound'},
+
+            { 'key': 'flags',
+              'type': 'DWORD',
+              'off': 0x0018,
+              'mask': {0x0001: 'Critical', 0x0002: 'TwoHanded', 0x0004: 'Movable', 0x0008: 'Displayable', 0x0010: 'Cursed', 0x0020: 'NotCopyable', 0x0040: 'Magical', 0x0080: 'Bow', 0x0100: 'Silver', 0x0200: 'ColdIron', 0x0400: 'Stolen', 0x0800: 'Conversable', 0x1000: 'Pulsating'},
+              'label': 'Flags'},
+
+            { 'key': 'item_type',
+              'type': 'WORD',
+              'off': 0x001C,
+              'enum': {0:'', 1:''},
+              'label': 'Item type'},
+
+            { 'key': 'usability_mask',
+              'type': 'DWORD',
+              'off': 0x001E,
+              'label': 'Usability mask'},
+
+            { 'key': 'inventory_icon_type',
+              'type': 'STR2',
+              'off': 0x0022,
+              'enum': {'  ': 'Fists/Nothing', 'AX': 'Axe', 'DD': 'Dagger', 'CL': 'Club', 'WH': 'Hammer', 'S1': 'Karach', 'CB': 'Crossbow'},
+              'label': 'Inventory icon type'},
+
+            { 'key': 'min_level',
+              'type': 'WORD',
+              'off': 0x0024,
+              'label': 'Min level'},
+
+            { 'key': 'min_str',
+              'type': 'WORD',
+              'off': 0x0026,
+              'label': 'Min strength'},
+
+            { 'key': 'min_str_bonus',
+              'type': 'WORD',
+              'off': 0x0028,
+              'label': 'Min strength bonus'},
+
+            { 'key': 'min_int',
+              'type': 'WORD',
+              'off': 0x002A,
+              'label': 'Min intelligence'},
+
+            { 'key': 'min_dex',
+              'type': 'WORD',
+              'off': 0x002C,
+              'label': 'Min dexterity'},
+
+            { 'key': 'min_wis',
+              'type': 'WORD',
+              'off': 0x002E,
+              'label': 'Min wisdom'},
+
+            { 'key': 'min_con',
+              'type': 'WORD',
+              'off': 0x0030,
+              'label': 'Min constitution'},
+
+            { 'key': 'min_cha',
+              'type': 'WORD',
+              'off': 0x0032,
+              'label': 'Min charisma'},
+
+            { 'key': 'price',
+              'type': 'DWORD',
+              'off': 0x0034,
+              'label': 'Price'},
+
+            { 'key': 'stack_amount',
+              'type': 'WORD',
+              'off': 0x0038,
+              'label': 'Stack amount'},
+
+            { 'key': 'item_icon',
+              'type': 'RESREF',
+              'off': 0x003A,
+              'label': 'Item icon'},
+
+            { 'key': 'lore_to_id',
+              'type': 'WORD',
+              'off': 0x0042,
+              'label': 'Lore to identify'},
+
+            { 'key': 'ground_icon',
+              'type': 'RESREF',
+              'off': 0x0044,
+              'label': 'Ground icon'},
+
+            { 'key': 'weight',
+              'type': 'DWORD',
+              'off': 0x004C,
+              'label': 'Weight'},
+
+            { 'key': 'item_desc',
+              'type': 'STRREF',
+              'off': 0x0050,
+              'label': 'Item description'},
+
+            { 'key': 'item_desc_identified',
+              'type': 'STRREF',
+              'off': 0x0054,
+              'label': 'Item description identified'},
+
+            { 'key': 'pick_up_sound',
+              'type': 'RESREF',
+              'off': 0x0058,
+              'label': 'Pick up sound'},
+
+            { 'key': 'enchantment',
+              'type': 'DWORD',
+              'off': 0x0060,
+              'label': 'Enchantment'},
+
+            { 'key': 'extended_header_off',
+              'type': 'DWORD',
+              'off': 0x0064,
+              'label': 'Extended header offset'},
+
+            { 'key': 'extended_header_cnt',
+              'type': 'WORD',
+              'off': 0x0068,
+              'label': 'Extended header count'},
+
+            { 'key': 'feature_block_off',
+              'type': 'DWORD',
+              'off': 0x006A,
+              'label': 'Feature block offset'},
+
+            { 'key': 'equipping_feature_ndx',
+              'type': 'WORD',
+              'off': 0x006E,
+              'label': 'First equipping feature index'},
+
+            { 'key': 'equipping_feature_cnt',
+              'type': 'WORD',
+              'off': 0x0070,
+              'label': 'Equipping feature count'},
+
+            # PST only
+            { 'key': 'dialog',
+              'type': 'RESREF',
+              'off': 0x0072,
+              'label': 'Dialog'},
+
+            { 'key': 'talking_item_name',
+              'type': 'STRREF',
+              'off': 0x007A,
+              'label': 'Talking item name'},
+
+            { 'key': 'weapon_color',
+              'type': 'WORD',
+              'off': 0x007E,
+              'label': 'Weapon color'},
+
             )
         
-
-        self.window_record_desc = (
-            { 'key': 'id',
-              'type': 'WORD',
+    extended_header_desc = (
+            { 'key': 'attack_type',
+              'type': 'BYTE',
               'off': 0x0000,
-              'label': 'Window id' },
-            
+              'enum': {0: 'Default', 1: 'Melee', 2: 'Ranged', 3: 'Magical', 4: 'Launcher'},
+              'label': 'Attack type'},
+
+            { 'key': 'id_req',
+              'type': 'BYTE',
+              'off': 0x0001,
+              'label': 'ID req'},
+
+            { 'key': 'location',
+              'type': 'BYTE',
+              'off': 0x0002,
+              'enum': {0: 'unknown', 1: 'Weapon slots', 2: 'unknown', 3: 'Item slots', 4: 'Gem?'},
+              'label': 'Location'},
+
             { 'key': 'unknown1',
-              'type': 'WORD',
+              'type': 'BYTE',
+              'off': 0x0003,
+              'label': 'Unknown1'},
+
+            { 'key': 'use_icon',
+              'type': 'RESREF',
+              'off': 0x0004,
+              'label': 'Use icon'},
+
+            { 'key': 'target',
+              'type': 'BYTE',
+              'off': 0x000C,
+              'enum': {0:'Invalid', 1:'Creature', 2:'Inventory', 3:'Dead character', 4:'Area', 5:'Self', 6:'Unknown', 7:'None'},
+              'label': 'Target'},
+
+            { 'key': 'target_number',
+              'type': 'BYTE',
+              'off': 0x000D,
+              'label': 'Target number'},
+
+            { 'key': 'range',
+              'type': 'WORD',
+              'off': 0x000E,
+              'label': 'Range'},
+
+            { 'key': 'projectile_type',
+              'type': 'WORD',
+              'off': 0x0010,
+              'enum': {0: 'None', 1: 'Bow', 2: 'Crossbow', 3: 'Sling'},
+              'label': 'Projectile type'},
+
+            { 'key': 'speed',
+              'type': 'WORD',
+              'off': 0x0012,
+              'label': 'Speed'},
+
+            { 'key': 'thac0_bonus',
+              'type': 'WORD',
+              'off': 0x0014,
+              'label': 'THAC0 bonus'},
+
+            { 'key': 'dice_sides',
+              'type': 'WORD',
+              'off': 0x0016,
+              'label': 'Dice sides'},
+
+            { 'key': 'dice_thrown',
+              'type': 'WORD',
+              'off': 0x0018,
+              'label': 'Dice thrown'},
+
+            { 'key': 'damage_bonus',
+              'type': 'WORD',
+              'off': 0x001A,
+              'label': 'Damage bonus'},
+
+            { 'key': 'damage_type',
+              'type': 'WORD',
+              'off': 0x001C,
+              'enum': {0: 'None', 1: 'Piercing', 2: 'Crushing', 3: 'Slashing', 4: 'Missile', 5: 'Fists'},
+              'label': 'Damage type'},
+
+            { 'key': 'feature_cnt',
+              'type': 'WORD',
+              'off': 0x001E,
+              'label': 'Feature count'},
+
+            { 'key': 'feature_ndx',
+              'type': 'WORD',
+              'off': 0x0020,
+              'label': 'First feature index'},
+
+            { 'key': 'charges',
+              'type': 'WORD',
+              'off': 0x0022,
+              'label': 'Charges'},
+
+            { 'key': 'charges_depletion',
+              'type': 'WORD',
+              'off': 0x0024,
+              'enum': {0: 'Unknown', 1: 'Item disappears', 2: 'Replace with Used Up', 3: 'Item remains'},
+              'label': 'Charges depletion'},
+
+            { 'key': 'use_strength_bonus',
+              'type': 'BYTE',
+              'off': 0x0026,
+              'label': 'Use strength bonus'},
+
+            { 'key': 'recharge',
+              'type': 'BYTE',
+              'off': 0x0027,
+              'enum': {0: 'unknown', 1: 'No', 8: 'After resting'},
+              'label': 'Recharge'},
+
+            { 'key': 'unknown2',
+              'type': 'WORD',
+              'off': 0x0028,
+              'label': 'Unknown2'},
+
+            { 'key': 'projectile_animation',
+              'type': 'WORD',
+              'off': 0x002A,
+              'label': 'Projectile animation'},
+
+            { 'key': 'melee_animation_0',
+              'type': 'WORD',
+              'off': 0x002C,
+              'label': 'Melee animation 0'},
+
+            { 'key': 'melee_animation_1',
+              'type': 'WORD',
+              'off': 0x002E,
+              'label': 'Melee animation 1'},
+
+            { 'key': 'melee_animation_2',
+              'type': 'WORD',
+              'off': 0x0030,
+              'label': 'Melee animation 2'},
+
+            { 'key': 'bow_arrow_qualifier',
+              'type': 'WORD',
+              'off': 0x0032,
+              'label': 'Bow/Arrow qualifier'},
+
+            { 'key': 'crossbow_bolt_qualifier',
+              'type': 'WORD',
+              'off': 0x0034,
+              'label': 'CrossBow/Bolt qualifier'},
+
+            { 'key': 'misc_projectile_qualifier',
+              'type': 'WORD',
+              'off': 0x0036,
+              'label': 'Misc projectile qualifier'},
+
+            )
+
+    feature_desc = (
+            { 'key': 'opcode_number',
+              'type': 'WORD',
+              'off': 0x0000,
+              'enum': 'effects',
+              'label': 'Opcode number'},
+
+            { 'key': 'target',
+              'type': 'BYTE',
               'off': 0x0002,
-              'label': 'Unknown 1' },
-            
-            { 'key': 'xpos',
-              'type': 'WORD',
+              'enum': {0:'None', 1:'Self', 2:'Pre-Target', 3:'Party', 4:'Global', 5:'Non-Party', 6:'Party?', 7:'Unknown1', 8:'Except-Self', 9:'Original-Caster', 10:'Unknown2', 11:'Unknown3', 12:'Unknown4', 13:'Unknown5'},
+              'label': 'Target'},
+
+            { 'key': 'power',
+              'type': 'BYTE',
+              'off': 0x0003,
+              'label': 'Power'},
+
+            { 'key': 'parameter1',
+              'type': 'DWORD',
               'off': 0x0004,
-              'label': 'X position' },
-            
-            { 'key': 'ypos',
-              'type': 'WORD',
-              'off': 0x0006,
-              'label': 'Y position' },
-            
-            { 'key': 'width',
-              'type': 'WORD',
+              'label': 'Parameter1'},
+
+            { 'key': 'parameter2',
+              'type': 'DWORD',
               'off': 0x0008,
-              'label': 'Width' },
-            
-            { 'key': 'height',
-              'type': 'WORD',
-              'off': 0x000A,
-              'label': 'Height' },
-            
-            { 'key': 'bg_flag',
-              'type': 'WORD',
+              'label': 'Parameter2'},
+
+            { 'key': 'timing_mode',
+              'type': 'BYTE',
               'off': 0x000C,
-              'label': 'Background flag' },
-            
-            { 'key': 'num_of_controls',
-              'type': 'WORD',
+              'enum': {0:'Duration', 1:'Permanent', 2:'While equipped', 3:'Delayed duration', 4:'Delayed2?', 5:'Delayed3?', 6:'Duration2?', 7:'Permanent2?', 8:'Permanent3?', 9:'Permanent? (after Death)', 10:'Trigger'},
+              'label': 'Timing mode'},
+
+            { 'key': 'resistance',
+              'type': 'BYTE',
+              'off': 0x000D,
+              'enum': {0:'Nonmagical', 1:'Dispell/Not bypass', 2:'Not dispell/Not bypass', 3:'Dispell/Bypass'},
+              'label': 'Resistance'},
+
+            { 'key': 'duration',
+              'type': 'DWORD',
               'off': 0x000E,
-              'label': '# of controls' },
-            
-            { 'key': 'bg_name',
-              'type': 'RESREF',
-              'off': 0x0010,
-              'label': 'Background filename' },
-
-            { 'key': 'control_ndx',
-              'type': 'WORD',
-              'off': 0x0018,
-              'label': 'Index of first control' },
-
-            { 'key': 'unknown2',
-              'type': 'WORD',
-              'off': 0x001A,
-              'label': 'Unknown 2' },
-            
+              'label': 'Duration'},
+
+            { 'key': 'probability1',
+              'type': 'BYTE',
+              'off': 0x0012,
+              'label': 'Probability1'},
+
+            { 'key': 'probability2',
+              'type': 'BYTE',
+              'off': 0x0013,
+              'label': 'Probability2'},
+
+            { 'key': 'resource',
+              'type': 'RESREF',
+              'off': 0x0014,
+              'label': 'Resource'},
+
+            { 'key': 'dice_thrown',
+              'type': 'DWORD',
+              'off': 0x001C,
+              'label': 'Dice thrown'},
+
+            { 'key': 'dice_sides',
+              'type': 'DWORD',
+              'off': 0x0020,
+              'label': 'Dice sides'},
+
+            { 'key': 'saving_throw_type',
+              'type': 'DWORD',
+              'off': 0x0024,
+              'mask': {0:'None', 1:'Spells', 2:'Breathe', 4:'Death', 8:'Wands', 16:'Polymorph'},
+              'label': 'Saving throw type'},
+
+            { 'key': 'saving_throw_bonus',
+              'type': 'DWORD',
+              'off': 0x0028,
+              'label': 'Saving throw bonus'},
+
+            { 'key': 'unknown',
+              'type': 'DWORD',
+              'off': 0x002C,
+              'label': 'Unknown'},
+
             )
 
-        self.control_table_record_desc = (
-            { 'key': 'control_offset',
-              'type': 'DWORD',
-              'off': 0x0000,
-              'label': 'Offset of control'},
-
-            { 'key': 'control_len',
-              'type': 'DWORD',
-              'off': 0x0004,
-              'label': 'Length of control structure'},
-            )
-
-        self.control_common_record_desc = (
-            { 'key': 'id',
-              'type': 'CTLID',
-              'off': 0x0000,
-              'label': 'Control ID' },
-            
-            { 'key': 'xpos',
-              'type': 'WORD',
-              'off': 0x0004,
-              'label': 'X position in window' },
-            
-            { 'key': 'ypos',
-              'type': 'WORD',
-              'off': 0x0006,
-              'label': 'Y position in window' },
-            
-            { 'key': 'width',
-              'type': 'WORD',
-              'off': 0x0008,
-              'label': 'Width' },
-            
-            { 'key': 'height',
-              'type': 'WORD',
-              'off': 0x000A,
-              'label': 'Height' },
-            
-            { 'key': 'type',
-              'type': 'CTLTYPE',
-              'off': 0x000C,
-              'label': 'Control type' },
-
-            { 'key': 'unknown',
-              'type': 'BYTE',
-              'off': 0x000D,
-              'label': 'Unknown' },
-
-            )
-
-
-        self.control_button_record_desc = (
-            { 'key': 'bam_file',
-              'type': 'RESREF',
-              'off': 0x000E,
-              'label': 'Name of BAM file w/ pixmap' },
-            
-            { 'key': 'bam_cycle',
-              'type': 'BYTE',
-              'off': 0x0016,
-              'label': 'Cycle in BAM file' },
-            
-            { 'key': 'justification',
-              'type': 'BYTE',
-              'off': 0x0017,
-              'label': 'Button text justification' },
-            
-            { 'key': 'frame_unpressed',
-              'type': 'WORD',
-              'off': 0x0018,
-              'label': 'BAM frame: button unpressed' },
-            
-            { 'key': 'frame_pressed',
-              'type': 'WORD',
-              'off': 0x001A,
-              'label': 'BAM frame: button pressed' },
-            
-            { 'key': 'frame_selected',
-              'type': 'WORD',
-              'off': 0x001C,
-              'label': 'BAM frame: button selected' },
-            
-            { 'key': 'frame_disabled',
-              'type': 'WORD',
-              'off': 0x001E,
-              'label': 'BAM frame: button disabled' },
-            
-            )
-
-        self.control_slider_record_desc = (
-            { 'key': 'mos_file',
-              'type': 'RESREF',
-              'off': 0x000E,
-              'label': 'Name of MOS file w/ background' },
-            
-            { 'key': 'bam_file',
-              'type': 'RESREF',
-              'off': 0x0016,
-              'label': 'Name of BAM file w/ pixmap' },
-            
-            { 'key': 'bam_cycle',
-              'type': 'BYTE',
-              'off': 0x001E,
-              'label': 'Cycle in BAM file' },
-            
-            { 'key': 'frame_ungrabbed',
-              'type': 'WORD',
-              'off': 0x0020,
-              'label': 'BAM frame: slider ungrabbed' },
-            
-            { 'key': 'frame_grabbed',
-              'type': 'WORD',
-              'off': 0x0022,
-              'label': 'BAM frame: slider grabbed' },
-            
-            { 'key': 'knob_x',
-              'type': 'WORD',
-              'off': 0x0024,
-              'label': 'Knob X offset' },
-            
-            { 'key': 'knob_y',
-              'type': 'WORD',
-              'off': 0x0026,
-              'label': 'Knob Y offset' },
-            
-            { 'key': 'knob_jump_width',
-              'type': 'WORD',
-              'off': 0x0028,
-              'label': 'Knob jump width' },
-            
-            { 'key': 'knob_jump_count',
-              'type': 'WORD',
-              'off': 0x002A,
-              'label': 'Knob jump count' },
-            
-            { 'key': 'unknown2',
-              'type': 'WORD',
-              'off': 0x002C,
-              'label': 'Unknown 2' },
-            
-            { 'key': 'unknown3',
-              'type': 'WORD',
-              'off': 0x002E,
-              'label': 'Unknown 3' },
-            
-            { 'key': 'unknown4',
-              'type': 'WORD',
-              'off': 0x0030,
-              'label': 'Unknown 4' },
-            
-            { 'key': 'unknown5',
-              'type': 'WORD',
-              'off': 0x0032,
-              'label': 'Unknown 5' },
-            
-            )
-
-        self.control_textedit_record_desc = (
-            { 'key': 'mos_file_1',
-              'type': 'RESREF',
-              'off': 0x000E,
-              'label': 'MOS file 1' },
-            
-            { 'key': 'mos_file_2',
-              'type': 'RESREF',
-              'off': 0x0016,
-              'label': 'MOS file 2' },
-            
-            { 'key': 'mos_file_3',
-              'type': 'RESREF',
-              'off': 0x001E,
-              'label': 'MOS file 3' },
-            
-            { 'key': 'cursor_file',
-              'type': 'RESREF',
-              'off': 0x0026,
-              'label': 'Name of BAM file w/ cursor' },
-            
-            { 'key': 'unknown2',
-              'type': 'BYTES',
-              'off': 0x002E,
-              'size': 12,
-              'label': 'Unknown 2' },
-            
-            { 'key': 'font_file',
-              'type': 'RESREF',
-              'off': 0x003A,
-              'label': 'Name of BAM file w/ font' },
-            
-            { 'key': 'unknown3',
-              'type': 'BYTES',
-              'off': 0x0042,
-              'size': 34,
-              'label': 'Unknown 3' },
-
-            { 'key': 'input_len',
-              'type': 'WORD',
-              'off': 0x0064,
-              'label': 'Max. input length' },
-            
-            { 'key': 'unknown4',
-              'type': 'DWORD',
-              'off': 0x0066,
-              'label': 'Unknown 4' },
-            
-            )
-
-        self.control_textarea_record_desc = (
-            { 'key': 'font_file_1',
-              'type': 'RESREF',
-              'off': 0x000E,
-              'label': 'Name of BAM file w/ font 1' },
-            
-            { 'key': 'font_file_2',
-              'type': 'RESREF',
-              'off': 0x0016,
-              'label': 'Name of BAM file w/ font 2' },
-            
-            { 'key': 'color_1',
-              'type': 'RGBA',
-              'off': 0x001E,
-              'label': 'Color 1' },
-            
-            { 'key': 'color_2',
-              'type': 'RGBA',
-              'off': 0x0022,
-              'label': 'Color 2' },
-            
-            { 'key': 'color_3',
-              'type': 'RGBA',
-              'off': 0x0026,
-              'label': 'Color 3' },
-            
-            { 'key': 'scrollbar',
-              'type': 'DWORD',
-              'off': 0x002A,
-              'label': 'Scrollbar ID' },
-            
-            )            
-
-        self.control_label_record_desc = (
-            { 'key': 'text',
-              'type': 'STRREF',
-              'off': 0x000E,
-              'label': 'Initial text' },
-
-            { 'key': 'font_file',
-              'type': 'RESREF',
-              'off': 0x0012,
-              'label': 'Name of BAM file w/ font' },
-            
-            { 'key': 'color_1',
-              'type': 'RGBA',
-              'off': 0x001A,
-              'label': 'Color 1' },
-            
-            { 'key': 'color_2',
-              'type': 'RGBA',
-              'off': 0x001E,
-              'label': 'Color 2' },
-            
-            { 'key': 'justification',
-              'type': 'WORD',
-              'off': 0x0022,
-              'label': 'Justification' },
-            
-            )
-
-
-        self.control_scrollbar_record_desc = (
-            { 'key': 'bam_file',
-              'type': 'RESREF',
-              'off': 0x000E,
-              'label': 'Name of BAM file w/ pixmap' },
-
-            { 'key': 'bam_cycle',
-              'type': 'WORD',
-              'off': 0x0016,
-              'label': 'Cycle in BAM file' },
-            
-            { 'key': 'frame_up_unpressed',
-              'type': 'WORD',
-              'off': 0x0018,
-              'label': 'BAM frame: up-arrow unpressed' },
-            
-            { 'key': 'frame_up_pressed',
-              'type': 'WORD',
-              'off': 0x001A,
-              'label': 'BAM frame: up-arrow pressed' },
-            
-            { 'key': 'frame_down_unpressed',
-              'type': 'WORD',
-              'off': 0x001C,
-              'label': 'BAM frame: down-arrow unpressed' },
-            
-            { 'key': 'frame_down_pressed',
-              'type': 'WORD',
-              'off': 0x001E,
-              'label': 'BAM frame: down-arrow pressed' },
-            
-            { 'key': 'frame_trough',
-              'type': 'WORD',
-              'off': 0x0020,
-              'label': 'BAM frame: trough' },
-            
-            { 'key': 'frame_slider',
-              'type': 'WORD',
-              'off': 0x0022,
-              'label': 'BAM frame: slider' },
-            
-            { 'key': 'textarea',
-              'type': 'DWORD',
-              'off': 0x0024,
-              'label': 'Textarea ID' },
-            
-            )
-
-
-    def decode_file (self):
-        self.decode_header ()
-
-        off = self.header['window_offset']
-        for i in range (self.header['num_of_windows']):
+    def __init__ (self):
+        Format.__init__ (self)
+        self.expect_signature = 'ITM'
+
+        self.extended_header_list = []
+        self.equipping_feature_list = []
+
+
+    def read (self, stream):
+        self.read_header (stream)
+
+        off = self.header['extended_header_off']
+        for i in range (self.header['extended_header_cnt']):
             obj = {}
-            self.decode_window_record (off, obj)
-            self.window_list.append (obj)
-            off = off + 28
-
-            off2 = self.header['control_table_offset'] + obj['control_ndx'] * 8
-            obj['control_list'] = []
-
-            for j in range (obj['num_of_controls']):
-                obj2 = {}
-                self.decode_control_record (off2, obj2)
-                obj['control_list'].append (obj2)
-                off2 = off2 + 8
-
-
-    def print_file (self):
+            self.read_extended_header (stream, off, obj)
+            self.extended_header_list.append (obj)
+            off = off + 56
+
+        off = self.header['feature_block_off'] + self.header['equipping_feature_ndx'] * 48
+        for i in range (self.header['equipping_feature_cnt']):
+            obj = {}
+            self.read_feature (stream, off, obj)
+            self.equipping_feature_list.append (obj)
+            off = off + 48
+
+    def write (self, stream):
+        size_extended = self.get_struc_size (self.extended_header_desc)
+        size_feature = self.get_struc_size (self.feature_desc)
+        
+        self.header['extended_header_off'] = self.get_struc_size (self.header)
+        self.header['extended_header_cnt'] = len (self.extended_header_list)
+        self.header['feature_block_off'] = len (self.extended_header_list) * size_extended
+        self.header['equipping_feature_ndx'] = 0
+        self.header['equipping_feature_cnt'] = len (self.equipping_feature_list)
+
+        feature_cnt = self.header['equipping_feature_cnt']
+        for obj in self.extended_header_list:
+            obj['feature_ndx'] = feature_cnt
+            obj['feature_cnt'] = len (obj['feature_list'])
+            feature_cnt += obj['feature_cnt']
+
+        self.write_header (stream)
+        
+        offset = self.header['feature_block_off']
+        for obj in self.equipping_feature_list:
+            self.write_feature (stream, offset, obj)
+            offset += size_feature
+
+        offset = self.header['extended_header_off']
+        for obj in self.extended_header_list:
+            # FIXME: recalc feature indices
+            self.write_extended_header (stream, offset, obj)
+            offset += size_extended
+        
+
+    def printme (self):
         self.print_header ()
 
         i = 0
-        for obj in self.window_list:
-            print '#%d' %i
-            self.print_window_record (obj)
+        for obj in self.equipping_feature_list:
+            print 'Equipping feature #%d' %i
+            self.print_feature (obj)
             i = i + 1
 
-
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
-        
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
-        
-
-    def decode_window_record (self, offset, obj):
-        self.decode_by_desc (offset, self.window_record_desc, obj)
-        
-    def print_window_record (self, obj):
-        self.print_by_desc (obj, self.window_record_desc)
+        i = 0
+        for obj in self.extended_header_list:
+            print 'Extended header #%d' %i
+            self.print_extended_header (obj)
+            i = i + 1
+
+
+    def read_extended_header (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.extended_header_desc, obj)
+
+        obj['feature_list'] = []
+        size_feature = self.get_struc_size (self.feature_desc)
+        off2 = self.header['feature_block_off'] + obj['feature_ndx'] * size_feature
+        for j in range (obj['feature_cnt']):
+            obj2 = {}
+            self.read_feature (stream, off2, obj2)
+            obj['feature_list'].append (obj2)
+            off2 += size_feature
+
+    def write_extended_header (self, stream, offset, obj):
+        self.write_struc (stream, offset, obj)
+        size_feature = self.get_struc_size (self.feature_desc)
+        off2 = self.header['feature_block_off'] +obj['feature_ndx'] * size_feature
+        for obj2 in obj['feature_list']:
+            self.write_feature (stream, off2, obj2)
+            off2 += size_feature
+        
+    def print_extended_header (self, obj):
+        self.print_struc (obj, self.extended_header_desc)
 
         j = 0
-        for ctl in obj['control_list']:
-            print 'C #%d' %j
-            self.print_control_record (ctl)
+        for feature in obj['feature_list']:
+            print 'Feature #%d' %j
+            self.print_feature (feature)
             j = j + 1
 
-
-    def control_type_to_desc (self, type):
-        if type == 0: return self.control_button_record_desc
-        elif type == 2: return self.control_slider_record_desc
-        elif type == 3: return self.control_textedit_record_desc
-        elif type == 5: return self.control_textarea_record_desc
-        elif type == 6: return self.control_label_record_desc
-        elif type == 7: return self.control_scrollbar_record_desc
-
-    def decode_control_record (self, offset, obj):
-        self.decode_by_desc (offset, self.control_table_record_desc, obj)
-        off2 = obj['control_offset']
-        self.decode_by_desc (off2, self.control_common_record_desc, obj)
-
-        desc = self.control_type_to_desc (obj['type'])
-        self.decode_by_desc (off2, desc, obj)
-
-    def print_control_record (self, obj):
-        self.print_by_desc (obj, self.control_table_record_desc)
-        self.print_by_desc (obj, self.control_common_record_desc)
-
-        desc = self.control_type_to_desc (obj['type'])
-        self.print_by_desc (obj, desc)
-
-
-
-    def get_file_res_data (self, obj):
-        obj['data'] = self.decode_blob (obj['data_offset'], obj['data_size'])
-
-    def save_file_res (self, filename, obj):
-        self.get_file_res_data (obj)
-        fh = open (filename, 'w')
-        fh.write (obj['data'])
-        fh.close ()
-        
-register_format ('CHUI', 'V1', CHUI_Format)
+    def read_feature (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.feature_desc, obj)
+        
+    def print_feature (self, obj):
+        self.print_struc (obj, self.feature_desc)
+
+        
+register_format ('ITM', 'V1.1', ITM_Format)
ie_shell/formats/cre.py to infinity/formats/chui.py
--- a/ie_shell/formats/cre.py
+++ b/infinity/formats/chui.py
@@ -16,506 +16,492 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: cre.py,v 1.3 2006/07/03 18:15:35 edheldil Exp $
-
-from ie_shell.formats.format import Format, register_format
-
-# FIXME: incomplete!
-
-class CRE_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'CRE'
-
+
+from infinity.format import Format, register_format
+
+class CHUI_Format (Format):
+    header_desc = (
+        { 'key': 'signature',
+          'type': 'STR4',
+          'off': 0x0000,
+          'label': 'Signature' },
+
+        { 'key': 'version',
+          'type': 'STR4',
+          'off':0x0004,
+          'label': 'Version'},
+
+        { 'key': 'num_of_windows',
+          'type': 'DWORD',
+          'off': 0x0008,
+          'label': '# of windows'},
+
+        { 'key': 'control_table_offset',
+          'type': 'DWORD',
+          'off': 0x000C,
+          'label': 'Control table offset'},
+
+        { 'key': 'window_offset',
+          'type': 'DWORD',
+          'off': 0x0010,
+          'label': 'First window offset'},
+        )
+
+
+    window_record_desc = (
+        { 'key': 'id',
+          'type': 'WORD',
+          'off': 0x0000,
+          'label': 'Window id' },
+
+        { 'key': 'unknown1',
+          'type': 'WORD',
+          'off': 0x0002,
+          'label': 'Unknown 1' },
+
+        { 'key': 'xpos',
+          'type': 'WORD',
+          'off': 0x0004,
+          'label': 'X position' },
+
+        { 'key': 'ypos',
+          'type': 'WORD',
+          'off': 0x0006,
+          'label': 'Y position' },
+
+        { 'key': 'width',
+          'type': 'WORD',
+          'off': 0x0008,
+          'label': 'Width' },
+
+        { 'key': 'height',
+          'type': 'WORD',
+          'off': 0x000A,
+          'label': 'Height' },
+
+        { 'key': 'bg_flag',
+          'type': 'WORD',
+          'off': 0x000C,
+          'label': 'Background flag' },
+
+        { 'key': 'num_of_controls',
+          'type': 'WORD',
+          'off': 0x000E,
+          'label': '# of controls' },
+
+        { 'key': 'bg_name',
+          'type': 'RESREF',
+          'off': 0x0010,
+          'label': 'Background filename' },
+
+        { 'key': 'control_ndx',
+          'type': 'WORD',
+          'off': 0x0018,
+          'label': 'Index of first control' },
+
+        { 'key': 'unknown2',
+          'type': 'WORD',
+          'off': 0x001A,
+          'label': 'Unknown 2' },
+
+        )
+
+    control_table_record_desc = (
+        { 'key': 'control_offset',
+          'type': 'DWORD',
+          'off': 0x0000,
+          'label': 'Offset of control'},
+
+        { 'key': 'control_len',
+          'type': 'DWORD',
+          'off': 0x0004,
+          'label': 'Length of control structure'},
+        )
+
+    control_common_record_desc = (
+        { 'key': 'id',
+          'type': 'CTLID',
+          'off': 0x0000,
+          'label': 'Control ID' },
+
+        { 'key': 'xpos',
+          'type': 'WORD',
+          'off': 0x0004,
+          'label': 'X position in window' },
+
+        { 'key': 'ypos',
+          'type': 'WORD',
+          'off': 0x0006,
+          'label': 'Y position in window' },
+
+        { 'key': 'width',
+          'type': 'WORD',
+          'off': 0x0008,
+          'label': 'Width' },
+
+        { 'key': 'height',
+          'type': 'WORD',
+          'off': 0x000A,
+          'label': 'Height' },
+
+        { 'key': 'type',
+          'type': 'CTLTYPE',
+          'off': 0x000C,
+          'label': 'Control type' },
+
+        { 'key': 'unknown',
+          'type': 'BYTE',
+          'off': 0x000D,
+          'label': 'Unknown' },
+
+        )
+
+
+    control_button_record_desc = (
+        { 'key': 'bam_file',
+          'type': 'RESREF',
+          'off': 0x000E,
+          'label': 'Name of BAM file w/ pixmap' },
+
+        { 'key': 'bam_cycle',
+          'type': 'BYTE',
+          'off': 0x0016,
+          'label': 'Cycle in BAM file' },
+
+        { 'key': 'justification',
+          'type': 'BYTE',
+          'off': 0x0017,
+          'label': 'Button text justification' },
+
+        { 'key': 'frame_unpressed',
+          'type': 'WORD',
+          'off': 0x0018,
+          'label': 'BAM frame: button unpressed' },
+
+        { 'key': 'frame_pressed',
+          'type': 'WORD',
+          'off': 0x001A,
+          'label': 'BAM frame: button pressed' },
+
+        { 'key': 'frame_selected',
+          'type': 'WORD',
+          'off': 0x001C,
+          'label': 'BAM frame: button selected' },
+
+        { 'key': 'frame_disabled',
+          'type': 'WORD',
+          'off': 0x001E,
+          'label': 'BAM frame: button disabled' },
+
+        )
+
+    control_slider_record_desc = (
+        { 'key': 'mos_file',
+          'type': 'RESREF',
+          'off': 0x000E,
+          'label': 'Name of MOS file w/ background' },
+
+        { 'key': 'bam_file',
+          'type': 'RESREF',
+          'off': 0x0016,
+          'label': 'Name of BAM file w/ pixmap' },
+
+        { 'key': 'bam_cycle',
+          'type': 'BYTE',
+          'off': 0x001E,
+          'label': 'Cycle in BAM file' },
+
+        { 'key': 'frame_ungrabbed',
+          'type': 'WORD',
+          'off': 0x0020,
+          'label': 'BAM frame: slider ungrabbed' },
+
+        { 'key': 'frame_grabbed',
+          'type': 'WORD',
+          'off': 0x0022,
+          'label': 'BAM frame: slider grabbed' },
+
+        { 'key': 'knob_x',
+          'type': 'WORD',
+          'off': 0x0024,
+          'label': 'Knob X offset' },
+
+        { 'key': 'knob_y',
+          'type': 'WORD',
+          'off': 0x0026,
+          'label': 'Knob Y offset' },
+
+        { 'key': 'knob_jump_width',
+          'type': 'WORD',
+          'off': 0x0028,
+          'label': 'Knob jump width' },
+
+        { 'key': 'knob_jump_count',
+          'type': 'WORD',
+          'off': 0x002A,
+          'label': 'Knob jump count' },
+
+        { 'key': 'unknown2',
+          'type': 'WORD',
+          'off': 0x002C,
+          'label': 'Unknown 2' },
+
+        { 'key': 'unknown3',
+          'type': 'WORD',
+          'off': 0x002E,
+          'label': 'Unknown 3' },
+
+        { 'key': 'unknown4',
+          'type': 'WORD',
+          'off': 0x0030,
+          'label': 'Unknown 4' },
+
+        { 'key': 'unknown5',
+          'type': 'WORD',
+          'off': 0x0032,
+          'label': 'Unknown 5' },
+
+        )
+
+    control_textedit_record_desc = (
+        { 'key': 'mos_file_1',
+          'type': 'RESREF',
+          'off': 0x000E,
+          'label': 'MOS file 1' },
+
+        { 'key': 'mos_file_2',
+          'type': 'RESREF',
+          'off': 0x0016,
+          'label': 'MOS file 2' },
+
+        { 'key': 'mos_file_3',
+          'type': 'RESREF',
+          'off': 0x001E,
+          'label': 'MOS file 3' },
+
+        { 'key': 'cursor_file',
+          'type': 'RESREF',
+          'off': 0x0026,
+          'label': 'Name of BAM file w/ cursor' },
+
+        { 'key': 'unknown2',
+          'type': 'BYTES',
+          'off': 0x002E,
+          'size': 12,
+          'label': 'Unknown 2' },
+
+        { 'key': 'font_file',
+          'type': 'RESREF',
+          'off': 0x003A,
+          'label': 'Name of BAM file w/ font' },
+
+        { 'key': 'unknown3',
+          'type': 'BYTES',
+          'off': 0x0042,
+          'size': 34,
+          'label': 'Unknown 3' },
+
+        { 'key': 'input_len',
+          'type': 'WORD',
+          'off': 0x0064,
+          'label': 'Max. input length' },
+
+        { 'key': 'unknown4',
+          'type': 'DWORD',
+          'off': 0x0066,
+          'label': 'Unknown 4' },
+
+        )
+
+    control_textarea_record_desc = (
+        { 'key': 'font_file_1',
+          'type': 'RESREF',
+          'off': 0x000E,
+          'label': 'Name of BAM file w/ font 1' },
+
+        { 'key': 'font_file_2',
+          'type': 'RESREF',
+          'off': 0x0016,
+          'label': 'Name of BAM file w/ font 2' },
+
+        { 'key': 'color_1',
+          'type': 'RGBA',
+          'off': 0x001E,
+          'label': 'Color 1' },
+
+        { 'key': 'color_2',
+          'type': 'RGBA',
+          'off': 0x0022,
+          'label': 'Color 2' },
+
+        { 'key': 'color_3',
+          'type': 'RGBA',
+          'off': 0x0026,
+          'label': 'Color 3' },
+
+        { 'key': 'scrollbar',
+          'type': 'DWORD',
+          'off': 0x002A,
+          'label': 'Scrollbar ID' },
+
+        )            
+
+    control_label_record_desc = (
+        { 'key': 'text',
+          'type': 'STRREF',
+          'off': 0x000E,
+          'label': 'Initial text' },
+
+        { 'key': 'font_file',
+          'type': 'RESREF',
+          'off': 0x0012,
+          'label': 'Name of BAM file w/ font' },
+
+        { 'key': 'color_1',
+          'type': 'RGBA',
+          'off': 0x001A,
+          'label': 'Color 1' },
+
+        { 'key': 'color_2',
+          'type': 'RGBA',
+          'off': 0x001E,
+          'label': 'Color 2' },
+
+        { 'key': 'justification',
+          'type': 'WORD',
+          'off': 0x0022,
+          'label': 'Justification' },
+
+        )
+
+
+    control_scrollbar_record_desc = (
+        { 'key': 'bam_file',
+          'type': 'RESREF',
+          'off': 0x000E,
+          'label': 'Name of BAM file w/ pixmap' },
+
+        { 'key': 'bam_cycle',
+          'type': 'WORD',
+          'off': 0x0016,
+          'label': 'Cycle in BAM file' },
+
+        { 'key': 'frame_up_unpressed',
+          'type': 'WORD',
+          'off': 0x0018,
+          'label': 'BAM frame: up-arrow unpressed' },
+
+        { 'key': 'frame_up_pressed',
+          'type': 'WORD',
+          'off': 0x001A,
+          'label': 'BAM frame: up-arrow pressed' },
+
+        { 'key': 'frame_down_unpressed',
+          'type': 'WORD',
+          'off': 0x001C,
+          'label': 'BAM frame: down-arrow unpressed' },
+
+        { 'key': 'frame_down_pressed',
+          'type': 'WORD',
+          'off': 0x001E,
+          'label': 'BAM frame: down-arrow pressed' },
+
+        { 'key': 'frame_trough',
+          'type': 'WORD',
+          'off': 0x0020,
+          'label': 'BAM frame: trough' },
+
+        { 'key': 'frame_slider',
+          'type': 'WORD',
+          'off': 0x0022,
+          'label': 'BAM frame: slider' },
+
+        { 'key': 'textarea',
+          'type': 'DWORD',
+          'off': 0x0024,
+          'label': 'Textarea ID' },
+
+        )
+
+
+    def __init__ (self):
+        Format.__init__ (self)
+        
+        self.expect_signature = 'CHUI'
         self.window_list = []
 
 
-        self.header_desc = (
-            { 'key': 'signature',
-              'type': 'STR4',
-              'off': 0x0000,
-              'label': 'Signature' },
+
+    def read (self, stream):
+        self.read_header (stream)
+
+        off = self.header['window_offset']
+        for i in range (self.header['num_of_windows']):
+            obj = {}
+            self.read_window_record (stream, off, obj)
+            self.window_list.append (obj)
+            off = off + 28
+
+            off2 = self.header['control_table_offset'] + obj['control_ndx'] * 8
+            obj['control_list'] = []
+
+            for j in range (obj['num_of_controls']):
+                obj2 = {}
+                self.read_control_record (stream, off2, obj2)
+                obj['control_list'].append (obj2)
+                off2 = off2 + 8
+
+
+    def printme (self):
+        self.print_header ()
+
+        i = 0
+        for obj in self.window_list:
+            print '#%d' %i
+            self.print_window_record (obj)
+            i = i + 1
+
+
+
+    def write (self, stream):
+        self.header['window_offset'] = self.get_struc_size (self.header_desc, self.header)
+        self.header['num_of_windows'] = len (self.window_list)
+
+        offset = self.header['window_offset']
+        num_controls = 0
+        for obj in self.window_list:
+            offset += self.get_struc_size (self.window_record_desc, obj)
+            obj['control_ndx'] = num_controls
+            obj['num_of_controls'] = len (obj['control_list'])
+            num_controls += obj['num_of_controls']
             
-            { 'key': 'version',
-              'type': 'STR4',
-              'off':0x0004,
-              'label': 'Version'},
+
+        self.header['control_table_offset'] = offset
+        
+
+        self.write_struc (stream, 0x0000, self.header_desc, self.header)
+
+        window_offset = self.header['window_offset']
+        table_offset = self.header['control_table_offset']
+        control_offset = self.header['control_table_offset'] + num_controls * 8
+        
+        for obj in self.window_list:
+            self.write_struc (stream, window_offset, self.window_record_desc, obj)
+            window_offset += self.get_struc_size (self.window_record_desc, obj)
+            for obj2 in obj['control_list']:
+                size = self.write_control (stream, control_offset, obj2)
+                obj3 = { 'control_offset': control_offset, 'control_len': size }
+                self.write_struc (stream, table_offset, self.control_table_record_desc, obj2)
+                control_offset += size
+                table_offset += 8  # FIXME
+                #xyx()
             
-            { 'key': 'long_name',
-              'type': 'STRREF',
-              'off': 0x0008,
-              'label': 'Long creature name'},
-            
-            { 'key': 'short_name',
-              'type': 'STRREF',
-              'off': 0x000C,
-              'label': 'Short creature name'},
-
-            { 'key': 'flags',
-              'type': 'DWORD',
-              'off': 0x0010,
-              'label': 'Creature flags'},
-
-            { 'key': 'xp',
-              'type': 'DWORD',
-              'off': 0x0014,
-              'label': 'XP'},
-
-            { 'key': 'power_level',
-              'type': 'DWORD',
-              'off': 0x0018,
-              'label': 'Creature power level'},
-
-            { 'key': 'gold',
-              'type': 'DWORD',
-              'off': 0x001C,
-              'label': 'Gold carried'},
-
-            { 'key': 'permanent_status_flags',
-              'type': 'DWORD',
-              'off': 0x0020,
-              'label': 'Permanent status flags'},  # as per STATE.IDS
-
-            { 'key': 'current_hp',
-              'type': 'WORD',
-              'off': 0x0024,
-              'label': 'Current HP'},
-
-            { 'key': 'max_hp',
-              'type': 'WORD',
-              'off': 0x0026,
-              'label': 'Maximum HP'},
-
-            { 'key': 'animation_id',
-              'type': 'WORD',
-              'off': 0x0028,
-              'enum': 'ANIMATE',
-              'label': 'Animation ID'},  # as per ANIMATE.IDS
-
-            )
-        
-
-        self.window_record_desc = (
-            { 'key': 'id',
-              'type': 'WORD',
-              'off': 0x0000,
-              'label': 'Window id' },
-            
-            { 'key': 'unknown1',
-              'type': 'WORD',
-              'off': 0x0002,
-              'label': 'Unknown 1' },
-            
-            { 'key': 'xpos',
-              'type': 'WORD',
-              'off': 0x0004,
-              'label': 'X position' },
-            
-            { 'key': 'ypos',
-              'type': 'WORD',
-              'off': 0x0006,
-              'label': 'Y position' },
-            
-            { 'key': 'width',
-              'type': 'WORD',
-              'off': 0x0008,
-              'label': 'Width' },
-            
-            { 'key': 'height',
-              'type': 'WORD',
-              'off': 0x000A,
-              'label': 'Height' },
-            
-            { 'key': 'bg_flag',
-              'type': 'WORD',
-              'off': 0x000C,
-              'label': 'Background flag' },
-            
-            { 'key': 'num_of_controls',
-              'type': 'WORD',
-              'off': 0x000E,
-              'label': '# of controls' },
-            
-            { 'key': 'bg_name',
-              'type': 'RESREF',
-              'off': 0x0010,
-              'label': 'Background filename' },
-
-            { 'key': 'control_ndx',
-              'type': 'WORD',
-              'off': 0x0018,
-              'label': 'Index of first control' },
-
-            { 'key': 'unknown2',
-              'type': 'WORD',
-              'off': 0x001A,
-              'label': 'Unknown 2' },
-            
-            )
-
-        self.control_table_record_desc = (
-            { 'key': 'control_offset',
-              'type': 'DWORD',
-              'off': 0x0000,
-              'label': 'Offset of control'},
-
-            { 'key': 'control_len',
-              'type': 'DWORD',
-              'off': 0x0004,
-              'label': 'Length of control structure'},
-            )
-
-        self.control_common_record_desc = (
-            { 'key': 'id',
-              'type': 'CTLID',
-              'off': 0x0000,
-              'label': 'Control ID' },
-            
-            { 'key': 'xpos',
-              'type': 'WORD',
-              'off': 0x0004,
-              'label': 'X position in window' },
-            
-            { 'key': 'ypos',
-              'type': 'WORD',
-              'off': 0x0006,
-              'label': 'Y position in window' },
-            
-            { 'key': 'width',
-              'type': 'WORD',
-              'off': 0x0008,
-              'label': 'Width' },
-            
-            { 'key': 'height',
-              'type': 'WORD',
-              'off': 0x000A,
-              'label': 'Height' },
-            
-            { 'key': 'type',
-              'type': 'CTLTYPE',
-              'off': 0x000C,
-              'label': 'Control type' },
-
-            { 'key': 'unknown',
-              'type': 'BYTE',
-              'off': 0x000D,
-              'label': 'Unknown' },
-
-            )
-
-
-        self.control_button_record_desc = (
-            { 'key': 'bam_file',
-              'type': 'RESREF',
-              'off': 0x000E,
-              'label': 'Name of BAM file w/ pixmap' },
-            
-            { 'key': 'bam_cycle',
-              'type': 'BYTE',
-              'off': 0x0016,
-              'label': 'Cycle in BAM file' },
-            
-            { 'key': 'justification',
-              'type': 'BYTE',
-              'off': 0x0017,
-              'label': 'Button text justification' },
-            
-            { 'key': 'frame_unpressed',
-              'type': 'WORD',
-              'off': 0x0018,
-              'label': 'BAM frame: button unpressed' },
-            
-            { 'key': 'frame_pressed',
-              'type': 'WORD',
-              'off': 0x001A,
-              'label': 'BAM frame: button pressed' },
-            
-            { 'key': 'frame_selected',
-              'type': 'WORD',
-              'off': 0x001C,
-              'label': 'BAM frame: button selected' },
-            
-            { 'key': 'frame_disabled',
-              'type': 'WORD',
-              'off': 0x001E,
-              'label': 'BAM frame: button disabled' },
-            
-            )
-
-        self.control_slider_record_desc = (
-            { 'key': 'mos_file',
-              'type': 'RESREF',
-              'off': 0x000E,
-              'label': 'Name of MOS file w/ background' },
-            
-            { 'key': 'bam_file',
-              'type': 'RESREF',
-              'off': 0x0016,
-              'label': 'Name of BAM file w/ pixmap' },
-            
-            { 'key': 'bam_cycle',
-              'type': 'BYTE',
-              'off': 0x001E,
-              'label': 'Cycle in BAM file' },
-            
-            { 'key': 'frame_ungrabbed',
-              'type': 'WORD',
-              'off': 0x0020,
-              'label': 'BAM frame: slider ungrabbed' },
-            
-            { 'key': 'frame_grabbed',
-              'type': 'WORD',
-              'off': 0x0022,
-              'label': 'BAM frame: slider grabbed' },
-            
-            { 'key': 'knob_x',
-              'type': 'WORD',
-              'off': 0x0024,
-              'label': 'Knob X offset' },
-            
-            { 'key': 'knob_y',
-              'type': 'WORD',
-              'off': 0x0026,
-              'label': 'Knob Y offset' },
-            
-            { 'key': 'knob_jump_width',
-              'type': 'WORD',
-              'off': 0x0028,
-              'label': 'Knob jump width' },
-            
-            { 'key': 'knob_jump_count',
-              'type': 'WORD',
-              'off': 0x002A,
-              'label': 'Knob jump count' },
-            
-            { 'key': 'unknown2',
-              'type': 'WORD',
-              'off': 0x002C,
-              'label': 'Unknown 2' },
-            
-            { 'key': 'unknown3',
-              'type': 'WORD',
-              'off': 0x002E,
-              'label': 'Unknown 3' },
-            
-            { 'key': 'unknown4',
-              'type': 'WORD',
-              'off': 0x0030,
-              'label': 'Unknown 4' },
-            
-            { 'key': 'unknown5',
-              'type': 'WORD',
-              'off': 0x0032,
-              'label': 'Unknown 5' },
-            
-            )
-
-        self.control_textedit_record_desc = (
-            { 'key': 'mos_file_1',
-              'type': 'RESREF',
-              'off': 0x000E,
-              'label': 'MOS file 1' },
-            
-            { 'key': 'mos_file_2',
-              'type': 'RESREF',
-              'off': 0x0016,
-              'label': 'MOS file 2' },
-            
-            { 'key': 'mos_file_3',
-              'type': 'RESREF',
-              'off': 0x001E,
-              'label': 'MOS file 3' },
-            
-            { 'key': 'cursor_file',
-              'type': 'RESREF',
-              'off': 0x0026,
-              'label': 'Name of BAM file w/ cursor' },
-            
-            { 'key': 'unknown2',
-              'type': 'BYTES',
-              'off': 0x002E,
-              'size': 12,
-              'label': 'Unknown 2' },
-            
-            { 'key': 'font_file',
-              'type': 'RESREF',
-              'off': 0x003A,
-              'label': 'Name of BAM file w/ font' },
-            
-            { 'key': 'unknown3',
-              'type': 'BYTES',
-              'off': 0x0042,
-              'size': 34,
-              'label': 'Unknown 3' },
-
-            { 'key': 'input_len',
-              'type': 'WORD',
-              'off': 0x0064,
-              'label': 'Max. input length' },
-            
-            { 'key': 'unknown4',
-              'type': 'DWORD',
-              'off': 0x0066,
-              'label': 'Unknown 4' },
-            
-            )
-
-        self.control_textarea_record_desc = (
-            { 'key': 'font_file_1',
-              'type': 'RESREF',
-              'off': 0x000E,
-              'label': 'Name of BAM file w/ font 1' },
-            
-            { 'key': 'font_file_2',
-              'type': 'RESREF',
-              'off': 0x0016,
-              'label': 'Name of BAM file w/ font 2' },
-            
-            { 'key': 'color_1',
-              'type': 'RGBA',
-              'off': 0x001E,
-              'label': 'Color 1' },
-            
-            { 'key': 'color_2',
-              'type': 'RGBA',
-              'off': 0x0022,
-              'label': 'Color 2' },
-            
-            { 'key': 'color_3',
-              'type': 'RGBA',
-              'off': 0x0026,
-              'label': 'Color 3' },
-            
-            { 'key': 'scrollbar',
-              'type': 'DWORD',
-              'off': 0x002A,
-              'label': 'Scrollbar ID' },
-            
-            )            
-
-        self.control_label_record_desc = (
-            { 'key': 'text',
-              'type': 'STRREF',
-              'off': 0x000E,
-              'label': 'Initial text' },
-
-            { 'key': 'font_file',
-              'type': 'RESREF',
-              'off': 0x0012,
-              'label': 'Name of BAM file w/ font' },
-            
-            { 'key': 'color_1',
-              'type': 'RGBA',
-              'off': 0x001A,
-              'label': 'Color 1' },
-            
-            { 'key': 'color_2',
-              'type': 'RGBA',
-              'off': 0x001E,
-              'label': 'Color 2' },
-            
-            { 'key': 'justification',
-              'type': 'WORD',
-              'off': 0x0022,
-              'label': 'Justification' },
-            
-            )
-
-
-        self.control_scrollbar_record_desc = (
-            { 'key': 'bam_file',
-              'type': 'RESREF',
-              'off': 0x000E,
-              'label': 'Name of BAM file w/ pixmap' },
-
-            { 'key': 'bam_cycle',
-              'type': 'WORD',
-              'off': 0x0016,
-              'label': 'Cycle in BAM file' },
-            
-            { 'key': 'frame_up_unpressed',
-              'type': 'WORD',
-              'off': 0x0018,
-              'label': 'BAM frame: up-arrow unpressed' },
-            
-            { 'key': 'frame_up_pressed',
-              'type': 'WORD',
-              'off': 0x001A,
-              'label': 'BAM frame: up-arrow pressed' },
-            
-            { 'key': 'frame_down_unpressed',
-              'type': 'WORD',
-              'off': 0x001C,
-              'label': 'BAM frame: down-arrow unpressed' },
-            
-            { 'key': 'frame_down_pressed',
-              'type': 'WORD',
-              'off': 0x001E,
-              'label': 'BAM frame: down-arrow pressed' },
-            
-            { 'key': 'frame_trough',
-              'type': 'WORD',
-              'off': 0x0020,
-              'label': 'BAM frame: trough' },
-            
-            { 'key': 'frame_slider',
-              'type': 'WORD',
-              'off': 0x0022,
-              'label': 'BAM frame: slider' },
-            
-            { 'key': 'textarea',
-              'type': 'DWORD',
-              'off': 0x0024,
-              'label': 'Textarea ID' },
-            
-            )
-
-
-    def decode_file (self):
-        self.decode_header ()
-        return self
-
-#         off = self.header['window_offset']
-#         for i in range (self.header['num_of_windows']):
-#             obj = {}
-#             self.decode_window_record (off, obj)
-#             self.window_list.append (obj)
-#             off = off + 28
-
-#             off2 = self.header['control_table_offset'] + obj['control_ndx'] * 8
-#             obj['control_list'] = []
-
-#             for j in range (obj['num_of_controls']):
-#                 obj2 = {}
-#                 self.decode_control_record (off2, obj2)
-#                 obj['control_list'].append (obj2)
-#                 off2 = off2 + 8
-
-
-
-    def print_file (self):
-        self.print_header ()
-
-#         i = 0
-#         for obj in self.window_list:
-#             print '#%d' %i
-#             self.print_window_record (obj)
-#             i = i + 1
-
-
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
-        
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
-        
-
-    def decode_window_record (self, offset, obj):
-        self.decode_by_desc (offset, self.window_record_desc, obj)
+
+    def read_window_record (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.window_record_desc, obj)
         
     def print_window_record (self, obj):
-        self.print_by_desc (obj, self.window_record_desc)
+        self.print_struc (obj, self.window_record_desc)
 
         j = 0
         for ctl in obj['control_list']:
@@ -532,20 +518,35 @@
         elif type == 6: return self.control_label_record_desc
         elif type == 7: return self.control_scrollbar_record_desc
 
-    def decode_control_record (self, offset, obj):
-        self.decode_by_desc (offset, self.control_table_record_desc, obj)
+    def read_control_record (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.control_table_record_desc, obj)
         off2 = obj['control_offset']
-        self.decode_by_desc (off2, self.control_common_record_desc, obj)
+        self.read_struc (stream, off2, self.control_common_record_desc, obj)
 
         desc = self.control_type_to_desc (obj['type'])
-        self.decode_by_desc (off2, desc, obj)
+        self.read_struc (stream, off2, desc, obj)
+
+
+    def write_control (self, stream, offset, obj):
+        #print obj
+        #size = 0
+        #print offset,
+        self.write_struc (stream, offset, self.control_common_record_desc, obj)
+
+        #size += self.get_struc_size (self.control_common_record_desc, obj)
+        desc = self.control_type_to_desc (obj['type'])
+        self.write_struc (stream, offset, desc, obj)
+        size = self.get_struc_size (desc, obj)
+
+        return size
+        
 
     def print_control_record (self, obj):
-        self.print_by_desc (obj, self.control_table_record_desc)
-        self.print_by_desc (obj, self.control_common_record_desc)
+        self.print_struc (obj, self.control_table_record_desc)
+        self.print_struc (obj, self.control_common_record_desc)
 
         desc = self.control_type_to_desc (obj['type'])
-        self.print_by_desc (obj, desc)
+        self.print_struc (obj, desc)
 
 
 
@@ -558,4 +559,4 @@
         fh.write (obj['data'])
         fh.close ()
         
-register_format ('CRE', 'V1.2', CRE_Format)
+register_format ('CHUI', 'V1', CHUI_Format)
ie_shell/formats/dlg.py to infinity/formats/wmap.py
--- a/ie_shell/formats/dlg.py
+++ b/infinity/formats/wmap.py
@@ -1,6 +1,7 @@
 # -*-python-*-
+
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,23 +17,12 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: dlg.py,v 1.1 2006/07/03 18:15:35 edheldil Exp $
-
-from ie_shell.formats.format import Format, register_format, core
-
-class DLG_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'DLG'
-
-        self.state_list = []
-        self.transition_list = []
-        self.state_trigger_list = []
-        self.transition_trigger_list = []
-        self.action_list = []
-
-
-        self.header_desc = (
+
+from infinity.format import Format, register_format
+
+class WMAP_Format (Format):
+
+    header_desc = (
             { 'key': 'signature',
               'type': 'STR4',
               'off': 0x0000,
@@ -43,305 +33,300 @@
               'off':0x0004,
               'label': 'Version'},
             
-            { 'key': 'state_cnt',
+            { 'key': 'num_of_wmaps',
               'type': 'DWORD',
               'off': 0x0008,
-              'label': 'Number of states'},
-
-            { 'key': 'state_off',
+              'label': '# of wmap entries'},
+            
+            { 'key': 'wmap_offset',
               'type': 'DWORD',
               'off': 0x000C,
-              'label': 'State table offset'},
-
-            { 'key': 'transition_cnt',
-              'type': 'DWORD',
-              'off': 0x0010,
-              'label': 'Number of transitions'},
-
-            { 'key': 'transition_off',
-              'type': 'DWORD',
-              'off': 0x0014,
-              'label': 'Transition table offset'},
-
-
-            { 'key': 'state_trigger_off',
-              'type': 'DWORD',
-              'off': 0x0018,
-              'label': 'State trigger table offset'},
-
-            { 'key': 'state_trigger_cnt',
-              'type': 'DWORD',
-              'off': 0x001C,
-              'label': 'Number of state triggers'},
-
-
-            { 'key': 'transition_trigger_off',
-              'type': 'DWORD',
-              'off': 0x0020,
-              'label': 'Transition trigger table offset'},
-
-            { 'key': 'transition_trigger_cnt',
-              'type': 'DWORD',
-              'off': 0x0024,
-              'label': 'Number of transition triggers'},
-
-
-            { 'key': 'action_off',
-              'type': 'DWORD',
-              'off': 0x0028,
-              'label': 'Action table offset'},
-
-            { 'key': 'action_cnt',
-              'type': 'DWORD',
-              'off': 0x002C,
-              'label': 'Number of actions'},
-
-            )
-
-        self.state_desc = (
-            { 'key': 'npc_text',
-              'type': 'STRREF',
-              'off': 0x0000,
-              'label': 'NPC text'},
-
-            { 'key': 'first_transition_index',
-              'type': 'DWORD',
-              'off': 0x0004,
-              'label': 'First transition index'},
-
-            { 'key': 'transition_cnt',
-              'type': 'DWORD',
-              'off': 0x0008,
-              'label': 'Number of transitions'},
-
-            { 'key': 'trigger_index',
-              'type': 'DWORD',
-              'off': 0x000C,
-              'label': 'Trigger'},
-
-            )
-
-
-        self.transition_desc = (
-            { 'key': 'flags',
-              'type': 'DWORD',
-              'mask': {0x01:'has text', 0x02:'has trigger', 0x04:'has action', 0x08:'terminates dlg', 0x10:'journal entry', 0x20:'unknown', 0x40:'add quest journal', 0x80:'del quest journal', 0x100:'add done quest journal'},
-              'off': 0x0000,
-              'label': 'Flags'},
-
-            { 'key': 'pc_text',
-              'type': 'STRREF',
-              'off': 0x0004,
-              'label': 'PC text'},
-
-            { 'key': 'journal_text',
-              'type': 'STRREF',
-              'off': 0x0008,
-              'label': 'Journal text'},
-
-            { 'key': 'trigger_index',
-              'type': 'DWORD',
-              'off': 0x000C,
-              'label': 'Trigger'},
-
-            { 'key': 'action_index',
-              'type': 'DWORD',
-              'off': 0x0010,
-              'label': 'Action'},
-
-            { 'key': 'next_dialog',
-              'type': 'RESREF',
-              'off': 0x0014,
-              'label': 'Next dialog resref'},
-
-            { 'key': 'next_state',
-              'type': 'DWORD',
-              'off': 0x001C,
-              'label': 'Next state'},
-
+              'label': 'First wmap entry offset'},
             )
         
-        self.script_desc = (
-            { 'key': 'script_off',
-              'type': 'DWORD',
+
+    wmap_record_desc = (
+            { 'key': 'map_resref',
+              'type': 'RESREF',
               'off': 0x0000,
-              'label': 'Script offset'},
-
-            { 'key': 'script_len',
-              'type': 'DWORD',
+              'label': 'Map resource name' },
+            
+            { 'key': 'width',
+              'type': 'DWORD',
+              'off': 0x0008,
+              'label': 'Width' },
+            
+            { 'key': 'height',
+              'type': 'DWORD',
+              'off': 0x000C,
+              'label': 'Height' },
+            
+            { 'key': 'map_num',
+              'type': 'DWORD',
+              'off': 0x0010,
+              'label': 'Map number' },
+            
+            { 'key': 'area_name',
+              'type': 'DWORD',
+              'off': 0x0014,
+              'label': 'Area name' },
+            
+            { 'key': 'unknown',
+              'type': 'DWORD',
+              'off': 0x0018,
+              'label': '???' },
+
+            { 'key': 'unknown',
+              'type': 'DWORD',
+              'off': 0x001C,
+              'label': '???' },
+
+            { 'key': 'num_of_areas',
+              'type': 'DWORD',
+              'off': 0x0020,
+              'label': 'Num of area entries' },
+
+            { 'key': 'area_offset',
+              'type': 'DWORD',
+              'off': 0x0024,
+              'label': 'Offset of first area entry' },
+
+            { 'key': 'arealink_offset',
+              'type': 'DWORD',
+              'off': 0x0028,
+              'label': 'Offset of first arealink entry' },
+
+            { 'key': 'num_of_arealinks',
+              'type': 'DWORD',
+              'off': 0x002C,
+              'label': 'Num of arealink entries' },
+
+            { 'key': 'map_icon_resref',
+              'type': 'RESREF',
+              'off': 0x0030,
+              'label': 'Map icons resource name' },
+            
+            { 'key': 'unknown38',
+              'type': 'BYTES',
+              'off': 0x0038,
+              'size': 128,
+              'label': 'Unknown 0x0038' },
+            )
+
+    area_record_desc = (
+            { 'key': 'area_name',
+              'type': 'RESREF',
+              'off': 0x0000,
+              'label': 'Area name' },
+            
+            { 'key': 'area_resref',
+              'type': 'RESREF',
+              'off': 0x0008,
+              'label': 'Area resref' },
+            
+            { 'key': 'area_long_name',
+              'type': 'STR32',
+              'off': 0x0010,
+              'label': 'Area long name' },
+            
+            { 'key': 'area_status',
+              'type': 'DWORD',
+              'off': 0x0030,
+              'label': 'Area status flags' },
+            
+            { 'key': 'icon_seq_ndx',
+              'type': 'DWORD',
+              'off': 0x0034,
+              'label': 'Icon sequence index' },
+            
+            { 'key': 'xpos',
+              'type': 'DWORD',
+              'off': 0x0038,
+              'label': 'X coordinate on world map' },
+            
+            { 'key': 'ypos',
+              'type': 'DWORD',
+              'off': 0x003C,
+              'label': 'Y coordinate on world map' },
+
+            { 'key': 'area_name_caption',
+              'type': 'STRREF',
+              'off': 0x0040,
+              'label': 'Area title for captions' },
+            
+            { 'key': 'area_name_tooltip',
+              'type': 'STRREF',
+              'off': 0x0044,
+              'label': 'Area title for tooltips' },
+            
+            { 'key': 'loading_screen',
+              'type': 'RESREF',
+              'off': 0x0048,
+              'label': 'Loading screen MOS file' },
+            
+
+            { 'key': 'arealink_ndx_north',
+              'type': 'DWORD',
+              'off': 0x0050,
+              'label': 'Index of 1st arealink north' },
+
+            { 'key': 'arealinks_cnt_north',
+              'type': 'DWORD',
+              'off': 0x0054,
+              'label': '# of arealinks north' },
+
+
+            { 'key': 'arealink_ndx_east',
+              'type': 'DWORD',
+              'off': 0x0058,
+              'label': 'Index of 1st arealink east' },
+
+            { 'key': 'arealinks_cnt_east',
+              'type': 'DWORD',
+              'off': 0x005C,
+              'label': '# of arealinks east' },
+
+
+            { 'key': 'arealink_ndx_south',
+              'type': 'DWORD',
+              'off': 0x0060,
+              'label': 'Index of 1st arealink south' },
+
+            { 'key': 'arealinks_cnt_south',
+              'type': 'DWORD',
+              'off': 0x0064,
+              'label': '# of arealinks south' },
+
+
+            { 'key': 'arealink_ndx_west',
+              'type': 'DWORD',
+              'off': 0x0068,
+              'label': 'Index of 1st arealink west' },
+
+            { 'key': 'arealinks_cnt_west',
+              'type': 'DWORD',
+              'off': 0x006C,
+              'label': '# of arealinks west' },
+
+            { 'key': 'unknown70',
+              'type': 'BYTES',
+              'off': 0x0070,
+              'size': 128,
+              'label': 'Unknown 0x0070' },
+
+            )
+
+    arealink_record_desc = (
+            { 'key': 'dest_map_ndx',
+              'type': 'DWORD',
+              'off': 0x0000,
+              'label': 'Destination map index' },
+
+            { 'key': 'dest_map_entry_point',
+              'type': 'STR32',
               'off': 0x0004,
-              'label': 'Script size'},
-            
-            { 'key': 'code',
-              'type': '_STRING',
-              'off': 0x0000,
-              'label': 'Script code'},
-            
+              'label': 'Destination map entry_point name' },
+
+            { 'key': 'distance_scale',
+              'type': 'DWORD',
+              'off': 0x0024,
+              'label': 'Distance scale?' },
+
+            { 'key': 'flags',
+              'type': 'DWORD',
+              'off': 0x0028,
+              'label': 'Flags' },
+
+            { 'key': 'encounter_area0',
+              'type': 'RESREF',
+              'off': 0x002C,
+              'label': 'Area of random encounter 0' },
+
+            { 'key': 'encounter_area1',
+              'type': 'RESREF',
+              'off': 0x0034,
+              'label': 'Area of random encounter 1' },
+
+            { 'key': 'encounter_area2',
+              'type': 'RESREF',
+              'off': 0x003C,
+              'label': 'Area of random encounter 2' },
+
+            { 'key': 'encounter_area3',
+              'type': 'RESREF',
+              'off': 0x0044,
+              'label': 'Area of random encounter 3' },
+
+            { 'key': 'encounter_area4',
+              'type': 'RESREF',
+              'off': 0x004C,
+              'label': 'Area of random encounter 4' },
+
+            { 'key': 'encounter_chance',
+              'type': 'DWORD',
+              'off': 0x0054,
+              'label': 'Random encounter chance' },
+
+            { 'key': 'unknown58',
+              'type': 'BYTES',
+              'off': 0x0058,
+              'size': 128,
+              'label': 'Unknown 0x0058' },
+
             )
 
-
-
-    def decode_file (self):
-        self.decode_header ()
-
-        off = self.header['state_off']
-        for i in range (self.header['state_cnt']):
+    def __init__ (self):
+        Format.__init__ (self)
+        self.expect_signature = 'WMAP'
+
+        self.wmap_list = []
+
+        # FIXME: maybe they should be local to wmap entries
+        self.area_list = []
+        self.arealink_list = []
+
+
+
+
+    def read (self, stream):
+        self.read_header (stream)
+
+        off = self.header['wmap_offset']
+        print self.get_struc_size (self.wmap_record_desc)
+        print self.get_struc_size (self.area_record_desc)
+        print self.get_struc_size (self.arealink_record_desc)
+        
+        for i in range (self.header['num_of_wmaps']):
             obj = {}
-            self.decode_state (off, obj)
-            self.state_list.append (obj)
-            off = off + 16
-
-        off = self.header['transition_off']
-        for i in range (self.header['transition_cnt']):
-            obj = {}
-            self.decode_transition (off, obj)
-            self.transition_list.append (obj)
-            off = off + 32
-
-
-        off = self.header['state_trigger_off']
-        for i in range (self.header['state_trigger_cnt']):
-            obj = {}
-            self.decode_script (off, obj)
-            self.state_trigger_list.append (obj)
-            off = off + 8
-
-        off = self.header['transition_trigger_off']
-        for i in range (self.header['transition_trigger_cnt']):
-            obj = {}
-            self.decode_script (off, obj)
-            self.transition_trigger_list.append (obj)
-            off = off + 8
-
-        off = self.header['action_off']
-        for i in range (self.header['action_cnt']):
-            obj = {}
-            self.decode_script (off, obj)
-            self.action_list.append (obj)
-            off = off + 8
-
-
-#         off = self.header['feature_block_off'] + self.header['casting_feature_block_off'] * 48
-#         for i in range (self.header['casting_feature_block_cnt']):
-#             obj = {}
-#             self.decode_feature_block (off, obj)
-#             self.casting_feature_block_list.append (obj)
-#             off = off + 48
-
-
-    def print_file (self):
+            self.read_wmap_record (stream, off, obj)
+            self.wmap_list.append (obj)
+            off = off + 184#self.get_struc_size (self.wmap_record_desc) # FIXME: compute the size outside the loop
+
+        for wmap in self.wmap_list:
+            off = wmap['area_offset']
+            for i in range (wmap['num_of_areas']):
+                obj = {}
+                self.read_area_record (stream, off, obj)
+                self.area_list.append (obj)
+                off = off + 240#self.get_struc_size (self.area_record_desc) # FIXME: compute the size outside the loop
+
+            off = wmap['arealink_offset']
+            for i in range (wmap['num_of_arealinks']):
+                obj = {}
+                self.read_arealink_record (stream, off, obj)
+                self.arealink_list.append (obj)
+                # FIXME: is the size correct???
+                off = off + 168#self.get_struc_size (self.arealink_record_desc) # FIXME: compute the size outside the loop
+
+    def printme (self):
         self.print_header ()
 
-        i = 0
-        for obj in self.state_list:
-            print 'State #%d' %i
-            self.print_state (obj)
-            i = i + 1
-
-        i = 0
-        for obj in self.transition_list:
-            print 'Transition #%d' %i
-            self.print_transition (obj)
-            i = i + 1
-
-        i = 0
-        for obj in self.state_trigger_list:
-            print 'State trigger#%d' %i
-            self.print_script (obj)
-            i = i + 1
-
-        i = 0
-        for obj in self.transition_trigger_list:
-            print 'Transition trigger #%d' %i
-            self.print_script (obj)
-            i = i + 1
-
-        i = 0
-        for obj in self.action_list:
-            print 'Action #%d' %i
-            self.print_script (obj)
-            i = i + 1
-
-
-
-
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
-        
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
-        
-
-    def decode_state (self, offset, obj):
-        self.decode_by_desc (offset, self.state_desc, obj)
-
-    def decode_transition (self, offset, obj):
-        self.decode_by_desc (offset, self.transition_desc, obj)
-
-    def decode_script (self, offset, obj):
-        self.decode_by_desc (offset, self.script_desc, obj)
-
-        obj['code'] = self.stream.decode_sized_string (obj['script_off'], obj['script_len'])
-        obj['code_raw'] = obj['code']
-        #if core.lang_trans:
-        #    obj['code'] = string.translate (obj['code'], core.lang_trans)
-
-
-#         obj['feature_list'] = []
-#         off2 = self.header['feature_block_off'] + obj['feature_off'] * 48
-#         for j in range (obj['feature_cnt']):
-#             obj2 = {}
-#             self.decode_feature_block (off2, obj2)
-#             obj['feature_list'].append (obj2)
-#             off2 = off2 + 48
-            
-    def print_state (self, obj):
-        self.print_by_desc (obj, self.state_desc)
-
-    def print_transition (self, obj):
-        self.print_by_desc (obj, self.transition_desc)
-
-    def print_script (self, obj):
-        self.print_by_desc (obj, self.script_desc)
-
-
-    def print_flow (self):
-        i = 0
-        for state in self.state_list:
-            print 'State %d:' %i
-            #self.print_state (state)
-            if (state['trigger_index'] != 0xffffffff):
-                print '  Trigger:', self.state_trigger_list[state['trigger_index']]['code']
-            if core.strrefs:
-                print '  Text:', core.strrefs.strref_list[state['npc_text']]['string']
-            else:
-                print '  Text:', state['npc_text']
-
-            j = 0
-            for transition in self.transition_list[state['first_transition_index']:state['first_transition_index']+state['transition_cnt']]:
-                print '\n  Trans %d:' %j
-                if (transition['flags'] & 0x02) and transition['trigger_index'] != 0xffffffff:
-                    print '    Trigger:', self.transition_trigger_list[transition['trigger_index']]['code']
-
-                if (transition['flags'] & 0x01) and transition['pc_text'] != 0xffffffff:
-                    if core.strrefs:
-                        print '    Text:', core.strrefs.strref_list[transition['pc_text']]['string']
-                    else:
-                        print '  Text:', transition['pc_text']
-
-                if (transition['flags'] & 0x04) and transition['action_index'] != 0xffffffff:
-                    print '    Action:', self.action_list[transition['action_index']]['code']
-
-                if not (transition['flags'] & 0x08):
-                    print '    ->', transition['next_dialog'], ":", transition['next_state']
-                else:
-                    print '    -> FIN'
-
-                print "\n"
-                j = j + 1
-                
-            i = i + 1
-
-        
-register_format ('DLG', 'V1.0', DLG_Format)
+        self.print_list ('wmap')
+        self.print_list ('arealink')
+        self.print_list ('area')
+
+
+
+
+
+register_format ('WMAP', 'V1.0', WMAP_Format)
ie_shell/formats/dtypes.py to infinity/dtypes.py
--- a/ie_shell/formats/dtypes.py
+++ b/infinity/dtypes.py
@@ -2,18 +2,35 @@
 
 dtypes = {}
 
-
-class DescItem:
+class IE_Type:
     def __init__ (self):
         pass
 
-    def printme (self, desc):
-        pass
+    def repr (self, value, *args):
+        return str (value)
 
-    def load (self, desc, stream):
-        pass
+    def read (self, stream, offset):
+        return None
 
-    def save (self, desc, stream):
+    def write (self, stream, offset, value):
         pass
     
+    def size (self, value):
+        return 0
+        
+class IE_WORD (IEType):
+    def read (self, stream, offset):
+        stream.read ()
 
+class IE_STRING (IE_Type):
+    pass
+
+class IE_RESREF (IE_STRING):
+    pass
+
+# FIXME: read/write/ etc should be class methods, perhaps?
+
+dtypes = {
+    'STRING': IE_STRING (),
+    'RESREF': IE_RESREF,
+    }
ie_shell/formats/format.py to infinity/formats/tis.py
--- a/ie_shell/formats/format.py
+++ b/infinity/formats/tis.py
@@ -1,6 +1,6 @@
 # -*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,223 +16,243 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: format.py,v 1.5 2006/07/08 14:29:26 edheldil Exp $
-
-import os.path
-import re
-import string
 import struct
-import types
-
-from ie_shell import core
-from ie_shell.formats.stream import Stream, FileStream, ResourceStream
- 
-PAGE_SIZE = 4096
-
-TICK_SIZE = 100
-TACK_SIZE = 5000
-
-def ResolveFilePath (filename):
-    if os.path.isfile (filename):
-        return filename
-
+import sys
+
+from infinity import core
+from infinity.format import Format, register_format
+
+class TIS_Format (Format):
+
+    header_desc = (
+            { 'key': 'signature',
+              'type': 'STR4',
+              'off': 0x0000,
+              'label': 'Signature' },
+            
+            { 'key': 'version',
+              'type': 'STR4',
+              'off':0x0004,
+              'label': 'Version'},
+            
+            { 'key': 'tile_cnt',
+              'type': 'DWORD',
+              'off': 0x0008,
+              'label': 'Count of tiles' },
+
+            { 'key': 'length',
+              'type': 'DWORD',
+              'off': 0x000C,
+              'label': 'Tile size (bytes)' },
+
+
+            { 'key': 'tile_off',
+              'type': 'DWORD',
+              'off': 0x0010,
+              'label': 'Tiles offset'},
+
+            { 'key': 'size',
+              'type': 'DWORD',
+              'off': 0x0014,
+              'label': 'Tile width (or height)'},
+
+            )
+
+    palette_entry_desc = (
+            { 'key': 'b',
+              'type': 'BYTE',
+              'off': 0x0000,
+              'label': 'B'},
+
+            { 'key': 'g',
+              'type': 'BYTE',
+              'off': 0x0001,
+              'label': 'G'},
+
+            { 'key': 'r',
+              'type': 'BYTE',
+              'off': 0x0002,
+              'label': 'R'},
+
+            { 'key': 'a',
+              'type': 'BYTE',
+              'off': 0x0003,
+              'label': 'A'},
+            )
+
+
+    def __init__ (self):
+        Format.__init__ (self)
+        self.expect_signature = 'TIS'
+
+        self.tile_list = []
+
+
+    def read (self, stream):
+        self.read_header (stream)
+
+        palette_size = 4 * 256
+        tile_size = self.header['size'] ** 2
+        
+        off = self.header['tile_off']
+        for i in range (self.header['tile_cnt']):
+            obj = []
+            self.read_palette (stream,  off,  obj)
+            off += palette_size
+            bin_data = stream.read_blob (off, tile_size)
+            tile_data = struct.unpack ('%dB' %tile_size, bin_data)
+            off += tile_size
+            
+            self.tile_list.append ({'palette': obj,  'tile_data': tile_data})
+
+        return self
+
+
+    def write (self,  stream):
+        self.header['tile_cnt'] = len (self.tile_list)
+        self.header['tile_off'] = self.get_struc_size (self.header_desc)
+        self.write_header (stream)
+
+        tile_size = self.header['size'] ** 2
+        palette_size = 4 * 256
+        
+        off = self.header['tile_off'] 
+        
+        for tile in self.tile_list:
+            self.write_palette (stream, off, tile['palette'])
+            off += palette_size
+            bin_data = struct.pack ('%dB' %tile_size, *tile['tile_data'])
+            stream.write_blob (bin_data, off)
+            
+            off += tile_size
+
+
+    def printme (self):
+        self.print_header ()
+
+        if self.get_option ('format.tis.print_palettes'):
+            i = 0
+            for obj in self.tile_list:
+                print 'Palette #%d' %i
+                self.print_palette (obj['palette'])
+                i = i + 1
+
+        if self.get_option ('format.tis.print_tiles'):
+            i = 0
+            for obj in self.tile_list:
+                print 'Tile #%d' %i
+                #print obj['offset']
+                self.print_tile (obj)
+                i = i + 1
+
+
+    def read_palette (self, stream, offset, obj):
+        for i in range (256):
+            obj2 = {}
+            self.read_struc (stream, offset, self.palette_entry_desc, obj2)
+            obj.append (obj2)
+            offset = offset + 4
+
+        return obj
+
+    def write_palette (self, stream, offset, obj):
+        for i in range (256):
+            self.write_struc (stream, offset, self.palette_entry_desc, obj[i])
+            offset = offset + 4
+
+
+    def print_palette (self, palette):
+        i = 0
+        for obj in palette:
+            print "%3d: %3d %3d %3d %3d (#%02x%02x%02x%02x)" %(i, obj['r'], obj['g'], obj['b'], obj['a'], obj['r'], obj['g'], obj['b'], obj['a'])
+            i = i + 1
+
+
+#    def read_tile (self, stream, obj):
+#        size = obj['width'] * obj['height']
+#        bin_data = stream.read_blob (obj['offset'], size)
+#        obj['tile_data'] = struct.unpack ('%dB' %size, bin_data)
+#
+#
+    def print_tile (self, obj):
+        gray = ' #*+:.'
+        grsz = len (gray) - 1
+        ndx = 0
+
+        for i in range (self.header['size']):
+            for j in range (self.header['size']):
+                pix = obj['tile_data'][ndx]
+
+                p = obj['palette'][pix]
+                gr = 1 + (p['r'] + p['g'] + p['b']) / (3 * (255 / grsz))
+                if gr >= grsz:
+                    gr = grsz - 1
+                sys.stdout.write (gray[gr])
+                #sys.stdout.write (gray[gr])
+                #print gray[gr],
+                ndx = ndx + 1
+            print
+        print
     
-
-
-class Format:
-    
-    default_options = {}
-    
-    def __init__ (self, source):
-        self.header_size = 0
-        self.bitmask_cache = {}
-        self.options = {}
-
-        if not isinstance (source, Stream):
-            source = FileStream (source)
-
-        self.stream = source
-        self.stream.open ()
-
-    def get_masked_bits (self, value, mask, bl):
-        return (value & mask) >> bl
-
-    # FIXME: cache it globally ...
-    def bits_to_mask (self, bits):
-        try:
-            return self.bitmask_cache[bits]
-        except:
-            bh, bl = map (int, string.split (bits, '-'))
-            if bl > bh:
-                print "warning: bh < bl:", bits
-                bh, bl = bl, bh
-
-            mask = 0L
-            for i in range (bl, bh + 1):
-                mask = mask | (1L << i)
-
-            #print "MASK: 0x%08x\n" %mask
-
-            self.bitmask_cache[bits] = (mask, bl)
-            return mask, bl
-
-
-#            { 'key': '', 'type': '', 'off': 0x00, 'label': '' },
-
-    def decode_by_desc (self, offset, desc, obj):
-        for d in desc:
-            key = d['key']
-            type = d['type']
-            local_offset = d['off']
-
-            stream = self.stream
-
-            if self.get_option ('debug_decode'):
-                print d
-
-            if type == 'BYTE':
-                value = ord (stream.get_char (offset + local_offset))
-            elif type == 'CTLTYPE':
-                value = ord (stream.get_char (offset + local_offset))
-            elif type == 'WORD':
-                value = stream.decode_word (offset + local_offset)
-            elif type == 'DWORD':
-                value = stream.decode_dword (offset + local_offset)
-            elif type == 'CTLID':
-                value = stream.decode_dword (offset + local_offset)
-            elif type == 'RGBA':
-                value = stream.decode_dword (offset + local_offset)
-            elif type == 'STR2':
-                value = stream.decode_sized_string (offset + local_offset, 2)
-            elif type == 'STR4':
-                value = stream.decode_sized_string (offset + local_offset, 4)
-            elif type == 'STR8':
-                value = stream.decode_sized_string (offset + local_offset, 8)
-            elif type == 'STR32':
-                value = stream.decode_sized_string (offset + local_offset, 32)
-            elif type == 'RESREF':
-                value = stream.decode_resref (offset + local_offset)
-                value = string.translate (value, core.slash_trans, '\x00')
-            elif type == 'STRREF':
-                value = stream.decode_dword (offset + local_offset)
-            elif type == 'RESTYPE':
-                value = stream.decode_word (offset + local_offset)
-            elif type == 'STROFF':
-                stroff = stream.decode_dword (offset + local_offset)
-                value = stream.decode_asciiz_string (stroff)
-                value = string.translate (value, core.slash_trans, '\x00')
-            elif type == 'STRSIZED':
-                length = stream.decode_dword (offset + local_offset)
-                # FIXME: asciiz or sized???
-                value = stream.decode_asciiz_string (offset + local_offset + 4)
-            elif type == 'BYTES':
-                value = stream.decode_blob (offset + local_offset, d['size'])
-            elif type == '_STRING':
-                value = ''
-            elif type == '_BYTE':
-                value = '?'
+#    # FIXME: use stream instead of fh?
+#    def write_ppm (self, fh):
+#        fh.write ("P6\n")
+#        fh.write ("# ie_shell\n");
+#        fh.write ("%d %d\n" %(self.header['width'], self.header['height']));
+#        fh.write ("255\n");
+#
+#        for line in range (self.header['height']):
+#            row = line / self.header['block_size']
+#            scanline = line % self.header['block_size']
+#            
+#            for i in range (self.header['columns']):
+#                tile = self.tile_list[(row * self.header['columns']) + i]
+#                pal = tile['palette']
+#            
+#                o = scanline * tile['width']
+#                for x in range (tile['width']):
+#                    pix = tile['tile_data'][o + x]
+#                    col = pal[pix]
+#                    fh.write ('%c%c%c' %(col['r'], col['g'], col['b']))
+
+
+    def from_mos (self, mos):
+        self.tile_list = []
+        size = self.header['size'] = mos.header['block_size']
+        
+        for obj in mos.tile_list:
+            # FIXME: this destroys original tiles in the MOS_Format object
+            width = obj['width']
+            height = obj['height']
+            
+            if width < size:
+                pad = [ obj['tile_data'][size - 1] ] * (size - width)
             else:
-                raise "Unknown data type `%s'" %type
-                #value = ''
-
-            if d.has_key ('bits'):
-                mask, bl = self.bits_to_mask (d['bits'])
-                value = self.get_masked_bits (value, mask, bl)
-
-            obj[key] = value
-
-            if self.get_option ('debug_decode'):
-                self.print_date_by_desc (obj, d)
-
+                pad = []
+            
+            #print 'Pad:', pad
+            start = 0
+            data = []
+            for i in range (obj['height']):
+                row = list (obj['tile_data'][start:start+width]) + pad
+                data.extend (row)
                 
-    def print_by_desc (self, obj, desc):
-        for d in desc:
-            self.print_date_by_desc (obj, d)
-        print
-
-    def print_date_by_desc (self, obj, d):
-        key = d['key']
-        rec_type = d['type']
-        label = d['label']
-
-        try: enum = d['enum']
-        except: enum = None
-
-        try: mask = d['mask']
-        except: mask = None
-
-        value = obj[key]
-        value2 = ''
-
-        if rec_type == 'RESTYPE':
-            try: value2 = '(' + core.restype_hash[value] + ')'
-            except: pass
-        elif rec_type == 'CTLTYPE':
-            try: value2 = '(' + ctltype_hash[value] + ')'
-            except: pass
-        elif rec_type == 'CTLID':
-            try: value2 = '(' + '0x%08x' %value + ')'
-            except: pass
-        elif rec_type == 'STRREF' and core.strrefs:
-            try: value2 = '(' + core.strrefs.strref_list[value]['string'] + ')'
-            except: pass
-        elif rec_type == 'RGBA':
-            try: value2 = '(' + '%08x' %value + ')'
-            except: pass
-        elif enum != None:
-            if type (enum) == types.DictType:
-                try: value2 = '(' + enum[value] + ')'
-                except: pass
-
-            elif type (enum) == types.StringType:
-                if not core.ids.has_key (enum):
-                    try:
-                        # FIXME: ugly & should use 'IDS' instead of 0x3F0
-                        ids = ResourceStream (enum, 0x03F0).load_object ()
-                        ids.decode_file ()
-                        core.ids[enum] = ids
-                    except:
-                        pass
-
-                try: value2 = '(' + core.ids[enum].ids[value] + ')'
-                except: pass
-                    
-                
-        elif mask != None:
-            value2 = '(' + string.join (map (lambda m, mask=mask: mask[m], filter (lambda m, v=value: (m & v) == m, mask.keys ())), '|') + ')'
-
-        print label + ':', value, value2
-
-
-    def set_option_default (key, value):
-        Format.default_options[key] = value
-
-    def set_option (self, key, value):
-        self.options[key] = value
-
-    def get_option (self, key):
-        if self.options.has_key (key):
-            return self.options[key]
-        elif core.options.has_key (key):
-            return core.options[key]
-        else:
-            return Format.default_options[key]
-
-
-Format.default_options['debug_decode'] = 0
-
-
-
-ctltype_hash = {
-    0 : 'button/pixmap',
-    2 : 'slider',
-    3 : 'textedit',
-    5 : 'textarea',
-    6 : 'label?',
-    7 : 'scrollbar',
-    }
-
-
-
-def register_format (signature, version, klass):
-    core.register_format (signature, version, klass)
+                start += width
+
+
+            if height < size:
+                pad = data[-size:] * (size - height)
+            else:
+                pad = []
+            
+            #print 'Pad:', pad
+            data.extend (pad)
+
+            self.tile_list.append ({ 'palette': obj['palette'], 'tile_data': data })
+
+        self.header['tile_cnt'] = len (self.tile_list)
+        self.header['tile_off'] = self.get_struc_size (self.header_desc)
+        self.header['length'] = 4 * 256 + self.header['size'] ** 2
+
+
+register_format ('TIS', 'V1', TIS_Format)
ie_shell/formats/ids.py to infinity/formats/ids.py
--- a/ie_shell/formats/ids.py
+++ b/infinity/formats/ids.py
@@ -1,6 +1,6 @@
 # -*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,37 +16,36 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: ids.py,v 1.2 2006/07/08 14:29:26 edheldil Exp $
 
 import re
 import sys
-from ie_shell.formats.format import Format, register_format
+from infinity.format import Format, register_format
 
 class IDS_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        #self.expect_signature = 'KEY'
+    def __init__ (self):
+        Format.__init__ (self)
+        #self.expect_signature = 'IDS'
 
         self.ids = {}
         self.ids_re = {}
         self.ids_list = []
 
-    def decode_file (self):
+    def read (self, stream):
         s = ""
         line_no = 0
-        
-        while s != None:
-            s = self.stream.get_line ()
-            if s == None:
+
+        while s is not None:
+            s = stream.get_line ()
+            if s is None:
                 break
-            
+
             line_no = line_no + 1
 
             s = s.strip ()
             if s == '':
                 continue
 
-            if line_no == 1 and (re.match ("^[0-9]+$", s) or re.match ("^IDS", s)):
+            if line_no == 1 and (re.match ("^-?[0-9]+$", s) or re.match ("^IDS", s)):
                 continue
 
             key, value = s.split (None, 1)
@@ -63,37 +62,22 @@
             # FIXME: if keys are duplicate, which one wins?
             #   The first one or the last one?
             if self.ids.has_key (ikey):
-                print "Warning: %s: Duplicate key %s" %(self.stream, key)
+                print "Warning: %s: Duplicate key %s" %(stream, key)
             else:
                 self.ids[ikey] = value
 
             if self.ids_re.has_key (value):
-                print "Warning: %s: Duplicate value %s" %(self.stream, value)
+                print "Warning: %s: Duplicate value %s" %(stream, value)
             else:
                 self.ids_re[value] = ikey
 
                 
 
-    def print_file (self):
+    def printme (self):
         for key, value in self.ids_list:
 
             print "%s\t%s" %(key, value)
             
 
 
-#     def get_bif_by_name_re (self, name):
-#         rx = re.compile (name)
-#         return filter (lambda s, rx=rx: rx.search (s['file_name']), self.bif_list)
-
-
-#     def get_resref_by_file_index (self, index):
-#         return filter (lambda s, i=index: s['locator_src_ndx'] == i, self.resref_list)
-
-#     def get_resref_by_name_re (self, name):
-#         rx = re.compile (name)
-#         return filter (lambda s, rx=rx: rx.search (s['resref_name']), self.resref_list)
-
-#     def get_resref_by_name (self, name):
-#         return filter (lambda s, name=name: s['resref_name'] == name, self.resref_list)
-
 register_format ('IDS', '', IDS_Format)
ie_shell/formats/itm.py to infinity/formats/spl.py
--- a/ie_shell/formats/itm.py
+++ b/infinity/formats/spl.py
@@ -1,6 +1,6 @@
 # -*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,20 +16,11 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: itm.py,v 1.1 2005/03/02 20:44:23 edheldil Exp $
-
-from ie_shell.formats.format import Format, register_format
-
-class ITM_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'ITM'
-
-        self.extended_header_list = []
-        self.equipping_feature_block_list = []
-
-
-        self.header_desc = (
+
+from infinity.format import Format, register_format
+
+class SPL_Format (Format):
+    header_desc = [
             { 'key': 'signature',
               'type': 'STR4',
               'off': 0x0000,
@@ -40,133 +31,137 @@
               'off':0x0004,
               'label': 'Version'},
             
-            { 'key': 'item_name',
+            { 'key': 'spell_name',
               'type': 'STRREF',
               'off': 0x0008,
-              'label': 'Item name'},
+              'label': 'Spell name'},
             
-            { 'key': 'item_name_identified',
-              'type': 'STRREF',
+            { 'key': 'unknown1',
+              'type': 'DWORD',
               'off': 0x000C,
-              'label': 'Item Name Identified'},
-
-            { 'key': 'drop_sound',
+              'label': 'Unknown1'},
+
+            { 'key': 'completion_sound',
               'type': 'RESREF',
               'off': 0x0010,
-              'label': 'Drop sound'},
+              'label': 'Completion sound'},
 
             { 'key': 'flags',
               'type': 'DWORD',
               'off': 0x0018,
-              'mask': {0x0001: 'Critical', 0x0002: 'TwoHanded', 0x0004: 'Movable', 0x0008: 'Displayable', 0x0010: 'Cursed', 0x0020: 'NotCopyable', 0x0040: 'Magical', 0x0080: 'Bow', 0x0100: 'Silver', 0x0200: 'ColdIron', 0x0400: 'Stolen', 0x0800: 'Conversable', 0x1000: 'Pulsating'},
               'label': 'Flags'},
 
-            { 'key': 'item_type',
+            { 'key': 'spell_type',
               'type': 'WORD',
               'off': 0x001C,
-              'enum': {0:'', 1:''},
-              'label': 'Item type'},
-
-            { 'key': 'usability_mask',
-              'type': 'DWORD',
+              'enum': {0:'Special', 1:'Wizard', 2:'Cleric', 3:'Unknown', 4:'Innate'},
+              'label': 'Spell type'},
+
+            { 'key': 'exclusion_school',
+              'type': 'WORD',
               'off': 0x001E,
-              'label': 'Usability mask'},
-
-            { 'key': 'inventory_icon_type',
-              'type': 'STR2',
+              'label': 'Exclusion school'},
+
+            { 'key': 'priest_type',
+              'type': 'WORD',
+              'off': 0x0020,
+              'mask': {0:'General', 0x4000:'Druid/Ranger', 0x8000:'Cleric/Paladin'},
+              'label': 'Priest type'},
+
+            { 'key': 'spell_school',
+              'type': 'BYTE',
               'off': 0x0022,
-              'enum': {'  ': 'Fists/Nothing', 'AX': 'Axe', 'DD': 'Dagger', 'CL': 'Club', 'WH': 'Hammer', 'S1': 'Karach', 'CB': 'Crossbow'},
-              'label': 'Inventory icon type'},
-
-            { 'key': 'min_level',
-              'type': 'WORD',
-              'off': 0x0024,
-              'label': 'Min level'},
-
-            { 'key': 'min_str',
-              'type': 'WORD',
-              'off': 0x0026,
-              'label': 'Min strength'},
-
-            { 'key': 'min_str_bonus',
-              'type': 'WORD',
+              'label': 'Spell school'},
+
+            { 'key': 'unknown2',
+              'type': 'WORD',
+              'off': 0x0023,
+              'label': 'Unknown2'},
+
+            { 'key': 'primary_type',     # school.ids
+              'type': 'WORD',
+              'off': 0x0025,
+              'label': 'Primary type'},
+
+            { 'key': 'secondary_type',     # secondary.ids
+              'type': 'BYTE',
+              'off': 0x0027,
+              'label': 'Secondary type'},
+
+            { 'key': 'unknown3',
+              'type': 'DWORD',
               'off': 0x0028,
-              'label': 'Min strength bonus'},
-
-            { 'key': 'min_int',
-              'type': 'WORD',
-              'off': 0x002A,
-              'label': 'Min intelligence'},
-
-            { 'key': 'min_dex',
-              'type': 'WORD',
+              'label': 'Unknown3'},
+
+            { 'key': 'unknown4',
+              'type': 'DWORD',
               'off': 0x002C,
-              'label': 'Min dexterity'},
-
-            { 'key': 'min_wis',
-              'type': 'WORD',
-              'off': 0x002E,
-              'label': 'Min wisdom'},
-
-            { 'key': 'min_con',
-              'type': 'WORD',
+              'label': 'Unknown4'},
+
+            { 'key': 'unknown5',
+              'type': 'DWORD',
               'off': 0x0030,
-              'label': 'Min constitution'},
-
-            { 'key': 'min_cha',
-              'type': 'WORD',
-              'off': 0x0032,
-              'label': 'Min charisma'},
-
-            { 'key': 'price',
+              'label': 'Unknown5'},
+
+            { 'key': 'spell_level',
               'type': 'DWORD',
               'off': 0x0034,
-              'label': 'Price'},
-
-            { 'key': 'stack_amount',
+              'label': 'Spell level'},
+
+            { 'key': 'unknown6',
               'type': 'WORD',
               'off': 0x0038,
-              'label': 'Stack amount'},
-
-            { 'key': 'item_icon',
+              'label': 'Unknown6'},
+
+            { 'key': 'spellbook_icon',
               'type': 'RESREF',
               'off': 0x003A,
-              'label': 'Item icon'},
-
-            { 'key': 'lore_to_id',
+              'label': 'Spellbook icon'},
+
+            { 'key': 'unknown7',
               'type': 'WORD',
               'off': 0x0042,
-              'label': 'Lore to identify'},
-
-            { 'key': 'ground_icon',
-              'type': 'RESREF',
+              'label': 'Unknown7'},
+
+            { 'key': 'unknown8',
+              'type': 'DWORD',
               'off': 0x0044,
-              'label': 'Ground icon'},
-
-            { 'key': 'weight',
+              'label': 'Unknown8'},
+
+            { 'key': 'unknown9',
+              'type': 'DWORD',
+              'off': 0x0048,
+              'label': 'Unknown9'},
+
+            { 'key': 'unknown10',
               'type': 'DWORD',
               'off': 0x004C,
-              'label': 'Weight'},
-
-            { 'key': 'item_desc',
+              'label': 'Unknown10'},
+
+            { 'key': 'spell_description',
               'type': 'STRREF',
               'off': 0x0050,
-              'label': 'Item description'},
-
-            { 'key': 'item_desc_identified',
-              'type': 'STRREF',
+              'label': 'Spell description'},
+
+            { 'key': 'unknown11',
+              'type': 'DWORD',
               'off': 0x0054,
-              'label': 'Item description identified'},
-
-            { 'key': 'pick_up_sound',
-              'type': 'RESREF',
+              'label': 'Unknown11'},
+
+            { 'key': 'unknown12',
+              'type': 'DWORD',
               'off': 0x0058,
-              'label': 'Pick up sound'},
-
-            { 'key': 'enchantment',
+              'label': 'Unknown12'},
+
+            { 'key': 'unknown13',
+              'type': 'DWORD',
+              'off': 0x005C,
+              'label': 'Unknown13'},
+
+            { 'key': 'unknown14',
               'type': 'DWORD',
               'off': 0x0060,
-              'label': 'Enchantment'},
+              'label': 'Unknown14'},
 
             { 'key': 'extended_header_off',
               'type': 'DWORD',
@@ -183,61 +178,43 @@
               'off': 0x006A,
               'label': 'Feature block offset'},
 
-            { 'key': 'equipping_feature_block_off',
+            { 'key': 'casting_feature_ndx',
               'type': 'WORD',
               'off': 0x006E,
-              'label': 'Equipping feature block offset'},
-
-            { 'key': 'equipping_feature_block_cnt',
+              'label': 'First casting feature index'},
+
+            { 'key': 'casting_feature_cnt',
               'type': 'WORD',
               'off': 0x0070,
-              'label': 'Equipping feature block count'},
-
-            # PST only
-            { 'key': 'dialog',
-              'type': 'RESREF',
-              'off': 0x0072,
-              'label': 'Dialog'},
-
-            { 'key': 'talking_item_name',
-              'type': 'STRREF',
-              'off': 0x007A,
-              'label': 'Talking item name'},
-
-            { 'key': 'weapon_color',
-              'type': 'WORD',
-              'off': 0x007E,
-              'label': 'Weapon color'},
-
-            )
+              'label': 'Casting feature count'},
+
+            ]
         
-        self.extended_header_desc = (
-            { 'key': 'attack_type',
+    extended_header_desc = (
+            { 'key': 'spell_form',
               'type': 'BYTE',
               'off': 0x0000,
-              'enum': {0: 'Default', 1: 'Melee', 2: 'Ranged', 3: 'Magical', 4: 'Launcher'},
-              'label': 'Attack type'},
-
-            { 'key': 'id_req',
+              'label': 'Spell form'},
+
+            { 'key': 'unknown1',
               'type': 'BYTE',
               'off': 0x0001,
-              'label': 'ID req'},
+              'label': 'Unknown1'},
 
             { 'key': 'location',
               'type': 'BYTE',
               'off': 0x0002,
-              'enum': {0: 'unknown', 1: 'Weapon slots', 2: 'unknown', 3: 'Item slots', 4: 'Gem?'},
               'label': 'Location'},
 
-            { 'key': 'unknown1',
+            { 'key': 'unknown2',
               'type': 'BYTE',
               'off': 0x0003,
-              'label': 'Unknown1'},
-
-            { 'key': 'use_icon',
+              'label': 'Unknown2'},
+
+            { 'key': 'memorised_icon',
               'type': 'RESREF',
               'off': 0x0004,
-              'label': 'Use icon'},
+              'label': 'Memorised icon'},
 
             { 'key': 'target',
               'type': 'BYTE',
@@ -255,121 +232,58 @@
               'off': 0x000E,
               'label': 'Range'},
 
-            { 'key': 'projectile_type',
+            { 'key': 'level_required',
               'type': 'WORD',
               'off': 0x0010,
-              'enum': {0: 'None', 1: 'Bow', 2: 'Crossbow', 3: 'Sling'},
-              'label': 'Projectile type'},
-
-            { 'key': 'speed',
-              'type': 'WORD',
+              'label': 'Level required'},
+
+            { 'key': 'casting_time',
+              'type': 'DWORD',
               'off': 0x0012,
-              'label': 'Speed'},
-
-            { 'key': 'thac0_bonus',
-              'type': 'WORD',
-              'off': 0x0014,
-              'label': 'THAC0 bonus'},
-
-            { 'key': 'dice_sides',
-              'type': 'WORD',
+              'label': 'Casting time'},
+
+            { 'key': 'unknown3',
+              'type': 'DWORD',
               'off': 0x0016,
-              'label': 'Dice sides'},
-
-            { 'key': 'dice_thrown',
-              'type': 'WORD',
-              'off': 0x0018,
-              'label': 'Dice thrown'},
-
-            { 'key': 'damage_bonus',
-              'type': 'WORD',
+              'label': 'Unknown3'},
+
+            { 'key': 'unknown4',
+              'type': 'DWORD',
               'off': 0x001A,
-              'label': 'Damage bonus'},
-
-            { 'key': 'damage_type',
-              'type': 'WORD',
-              'off': 0x001C,
-              'enum': {0: 'None', 1: 'Piercing', 2: 'Crushing', 3: 'Slashing', 4: 'Missile', 5: 'Fists'},
-              'label': 'Damage type'},
+              'label': 'Unknown4'},
 
             { 'key': 'feature_cnt',
               'type': 'WORD',
               'off': 0x001E,
               'label': 'Feature count'},
 
-            { 'key': 'feature_off',
+            { 'key': 'feature_ndx',
               'type': 'WORD',
               'off': 0x0020,
-              'label': 'Feature offset'},
-
-            { 'key': 'charges',
+              'label': 'First feature index'},
+
+            { 'key': 'projectile',
               'type': 'WORD',
               'off': 0x0022,
-              'label': 'Charges'},
-
-            { 'key': 'charges_depletion',
+              'label': 'Projectile'},
+
+            { 'key': 'unknown5',
               'type': 'WORD',
               'off': 0x0024,
-              'enum': {0: 'Unknown', 1: 'Item disappears', 2: 'Replace with Used Up', 3: 'Item remains'},
-              'label': 'Charges depletion'},
-
-            { 'key': 'use_strength_bonus',
-              'type': 'BYTE',
+              'label': 'Unknown5'},
+
+            { 'key': 'animation',
+              'type': 'WORD',
               'off': 0x0026,
-              'label': 'Use strength bonus'},
-
-            { 'key': 'recharge',
-              'type': 'BYTE',
-              'off': 0x0027,
-              'enum': {0: 'unknown', 1: 'No', 8: 'After resting'},
-              'label': 'Recharge'},
-
-            { 'key': 'unknown2',
-              'type': 'WORD',
-              'off': 0x0028,
-              'label': 'Unknown2'},
-
-            { 'key': 'projectile_animation',
-              'type': 'WORD',
-              'off': 0x002A,
-              'label': 'Projectile animation'},
-
-            { 'key': 'melee_animation_0',
-              'type': 'WORD',
-              'off': 0x002C,
-              'label': 'Melee animation 0'},
-
-            { 'key': 'melee_animation_1',
-              'type': 'WORD',
-              'off': 0x002E,
-              'label': 'Melee animation 1'},
-
-            { 'key': 'melee_animation_2',
-              'type': 'WORD',
-              'off': 0x0030,
-              'label': 'Melee animation 2'},
-
-            { 'key': 'bow_arrow_qualifier',
-              'type': 'WORD',
-              'off': 0x0032,
-              'label': 'Bow/Arrow qualifier'},
-
-            { 'key': 'crossbow_bolt_qualifier',
-              'type': 'WORD',
-              'off': 0x0034,
-              'label': 'CrossBow/Bolt qualifier'},
-
-            { 'key': 'misc_projectile_qualifier',
-              'type': 'WORD',
-              'off': 0x0036,
-              'label': 'Misc projectile qualifier'},
+              'label': 'Animation'},
 
             )
 
-        self.feature_block_desc = (
+    feature_desc = (
             { 'key': 'opcode_number',
               'type': 'WORD',
               'off': 0x0000,
+              'enum': 'effects',
               'label': 'Opcode number'},
 
             { 'key': 'target',
@@ -453,74 +367,100 @@
 
             )
 
-
-    def decode_file (self):
-        self.decode_header ()
+    def __init__ (self):
+        Format.__init__ (self)
+        self.expect_signature = 'SPL'
+
+        self.extended_header_list = []
+        self.casting_feature_list = []
+
+
+    def read (self, stream):
+        self.read_header (stream)
 
         off = self.header['extended_header_off']
         for i in range (self.header['extended_header_cnt']):
             obj = {}
-            self.decode_extended_header (off, obj)
+            self.read_extended_header (stream, off, obj)
             self.extended_header_list.append (obj)
-            off = off + 56
-
-        off = self.header['feature_block_off'] + self.header['equipping_feature_block_off'] * 48
-        for i in range (self.header['equipping_feature_block_cnt']):
+            off = off + 40
+
+        off = self.header['feature_block_off'] + self.header['casting_feature_ndx'] * 48
+        for i in range (self.header['casting_feature_cnt']):
             obj = {}
-            self.decode_feature_block (off, obj)
-            self.equipping_feature_block_list.append (obj)
+            self.read_feature_block (stream, off, obj)
+            self.casting_feature_block_list.append (obj)
             off = off + 48
 
 
-    def print_file (self):
+    def printme (self):
         self.print_header ()
+
+        i = 0
+        for obj in self.casting_feature_list:
+            print 'Casting feature #%d' %i
+            self.print_feature (obj)
+            i = i + 1
 
         i = 0
         for obj in self.extended_header_list:
-            print '#%d' %i
+            print 'Extended header #%d' %i
             self.print_extended_header (obj)
             i = i + 1
 
-        i = 0
-        for obj in self.equipping_feature_block_list:
-            print '#%d' %i
-            self.print_feature_block (obj)
-            i = i + 1
-
-
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
-        
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
-        
-
-    def decode_extended_header (self, offset, obj):
-        self.decode_by_desc (offset, self.extended_header_desc, obj)
+
+    def read_extended_header (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.extended_header_desc, obj)
 
         obj['feature_list'] = []
-        off2 = self.header['feature_block_off'] + obj['feature_off'] * 48
+        off2 = self.header['feature_block_off'] + obj['feature_ndx'] * 48
         for j in range (obj['feature_cnt']):
             obj2 = {}
-            self.decode_feature_block (off2, obj2)
+            self.read_feature (stream, off2, obj2)
             obj['feature_list'].append (obj2)
             off2 = off2 + 48
             
     def print_extended_header (self, obj):
-        self.print_by_desc (obj, self.extended_header_desc)
+        self.print_struc (obj, self.extended_header_desc)
 
         j = 0
         for feature in obj['feature_list']:
-            print 'F #%d' %j
-            self.print_feature_block (feature)
+            print 'Feature #%d' %j
+            self.print_feature (feature)
             j = j + 1
 
-    def decode_feature_block (self, offset, obj):
-        self.decode_by_desc (offset, self.feature_block_desc, obj)
+    def read_feature (self, stream, offset, obj):
+        self.read_struc (stream,offset, self.feature_desc, obj)
         
-    def print_feature_block (self, obj):
-        self.print_by_desc (obj, self.feature_block_desc)
-
-        
-register_format ('ITM', 'V1.1', ITM_Format)
+    def print_feature (self, obj):
+        self.print_struc (obj, self.feature_desc)
+
+
+
+class SPL_V20_Format (SPL_Format):
+    SPL_Format.header_desc.extend ([
+            { 'key': 'duration_modifier_level',
+              'type': 'BYTE',
+              'off': 0x0072,
+              'label': 'Duration modifier (level)' },
+
+            { 'key': 'duration_modifier_rounds',
+              'type': 'BYTE',
+              'off': 0x0073,
+              'label': 'Duration modifier (rounds)' }, 
+
+            { 'key': 'unknown_73',
+              'type': 'BYTES',
+              'off': 0x0074,
+              'size': 14, 
+              'label': 'Unknown 0x73' },
+    ])
+
+    ###Format.get_struc_field (None,  SPL_Format.extended_header_desc, 'off',  0x24)['enum'] = { 0: 'None' }
+
+    def __init__ (self):
+        SPL_Format.__init__ (self)
+
+
+register_format ('SPL', 'V1', SPL_Format,  "Should be done except of enums")
+register_format ('SPL', 'V2.0', SPL_V20_Format,  "Should be done except of enums")
ie_shell/formats/key.py to infinity/formats/key.py
--- a/ie_shell/formats/key.py
+++ b/infinity/formats/key.py
@@ -1,6 +1,6 @@
 # -*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,29 +16,15 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: key.py,v 1.4 2006/07/08 14:29:26 edheldil Exp $
 
 import re
 import sys
 
-from ie_shell.formats.format import Format, register_format, TICK_SIZE, TACK_SIZE
+from infinity import core
+from infinity.format import Format, register_format
 
 class KEY_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'KEY'
-
-        self.bif_list = []
-        self.bif_hash = {}
-
-        self.resref_list = []
-        self.resref_hash = {}
-
-        # when set to some number, read that number of resources at most
-        #self.options['max_read_bifs'] = None
-        self.options['max_read_resrefs'] = None
-
-        self.header_desc = (
+    header_desc = (
             { 'key': 'signature',
               'type': 'STR4',
               'off': 0x0000,
@@ -71,7 +57,7 @@
             )
         
 
-        self.bif_record_desc = (
+    bif_record_desc = (
             { 'key': 'file_len',
               'type': 'DWORD',
               'off': 0x0000,
@@ -98,7 +84,7 @@
               'label': 'BIF location' },
             )
 
-        self.resref_record_desc = (
+    resref_record_desc = (
             { 'key': 'resref_name',
               'type': 'RESREF',
               'off': 0x0000,
@@ -133,41 +119,83 @@
               'label': 'resref locator (non-tileset index)' },
             )
 
-    def decode_file (self):
-        self.decode_header ()
+    def __init__ (self):
+        Format.__init__ (self)
+        self.expect_signature = 'KEY'
+
+        self.bif_list = []
+        self.bif_hash = {}
+
+        self.resref_list = []
+        self.resref_hash = {}
+
+        # when set to some number, read that number of resources at most
+        #self.options['max_read_bifs'] = None
+
+
+    def read (self, stream):
+        self.read_header (stream)
 
         off = self.header['bif_offset']
+        bif_record_size = self.get_struc_size (self.bif_record_desc)
         for i in range (self.header['num_of_bifs']):
             obj = {}
-            self.decode_bif_record (off, obj)
+            self.read_bif_record (stream, off, obj)
             self.bif_list.append (obj)
             self.bif_hash[obj['file_name']] = obj
-            off = off + 12
+            off = off + bif_record_size
 
         off = self.header['resref_offset']
         max_read_resrefs = self.header['num_of_resrefs']
-        if self.options['max_read_resrefs']:
-            max_read_resrefs = min (max_read_resrefs, self.options['max_read_resrefs'])
-            
+        if self.get_option ('format.key.max_read_resrefs'):
+            max_read_resrefs = min (max_read_resrefs, self.get_option ('format.key.max_read_resrefs'))
+        
+        resref_record_size = self.get_struc_size (self.resref_record_desc)
+        tick_size = self.get_option ('format.key.tick_size')
+        tack_size = self.get_option ('format.key.tack_size')
         for i in range (max_read_resrefs):
             #if i == 1000:
             #    break
             
             obj = {}
-            self.decode_resref_record (off, obj)
+            self.read_resref_record (stream, off, obj)
             self.resref_list.append (obj)
             obj['file_name'] = self.bif_list[obj['locator_src_ndx']]
             self.resref_hash[obj['resref_name']] = obj
-            off = off + 14
-
-            if not (i % TICK_SIZE):
+            off = off + resref_record_size
+
+            if not (i % tick_size):
                 sys.stdout.write('.')
-                if not (i % TACK_SIZE):
+                if not (i % tack_size):
                     sys.stdout.write('%d' %i)
                 sys.stdout.flush ()
         print
 
-    def print_file (self):
+    def write (self, stream):
+        # FIXME: STROFF is missing
+        header_size = self.get_struc_size (self.header_desc)
+        bif_record_size = self.get_struc_size (self.bif_record_desc)
+        resref_record_size = self.get_struc_size (self.resref_record_desc)
+        
+        self.header['num_of_bifs'] = len (self.bif_list)
+        self.header['num_of_resrefs'] = len (self.resref_list)
+        self.header['bif_offset'] = header_size
+        self.header['resref_offset'] = header_size + len (self.bif_list) * bif_record_size
+        
+        self.write_struc (stream, 0x0000, self.header_desc, self.header)
+
+        off2 = self.header['bif_offset']
+        for obj in self.bif_list:
+            self.write_struc (stream, off2, self.bif_record_desc, obj)
+            off2 += bif_record_size
+            
+        off2 = self.header['resref_offset']
+        for obj in self.resref_list:
+            self.write_struc (stream, off2, self.resref_record_desc, obj)
+            off2 += resref_record_size
+            
+
+    def printme (self):
         self.print_header ()
 
         i = 0
@@ -183,26 +211,18 @@
             i = i + 1
 
 
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
-        
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
-
-        
-    def decode_bif_record (self, offset, obj):
-        self.decode_by_desc (offset, self.bif_record_desc, obj)
+    def read_bif_record (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.bif_record_desc, obj)
         
     def print_bif_record (self, obj):
-        self.print_by_desc (obj, self.bif_record_desc)
-
-
-    def decode_resref_record (self, offset, obj):
-        self.decode_by_desc (offset, self.resref_record_desc, obj)
+        self.print_struc (obj, self.bif_record_desc)
+
+
+    def read_resref_record (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.resref_record_desc, obj)
         
     def print_resref_record (self, obj):
-        self.print_by_desc (obj, self.resref_record_desc)
+        self.print_struc (obj, self.resref_record_desc)
 
 
 
ie_shell/formats/pro.py to infinity/formats/pro.py
--- a/ie_shell/formats/pro.py
+++ b/infinity/formats/pro.py
@@ -1,6 +1,6 @@
 # -*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,17 +16,12 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: pro.py,v 1.1 2006/07/08 14:29:26 edheldil Exp $
-
-from ie_shell.formats.format import Format, register_format, core
+
+from infinity import core
+from infinity.format import Format, register_format
 
 class PRO_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'PRO'
-
-
-        self.header_desc = (
+    header_desc = (
             { 'key': 'signature',
               'type': 'STR4',
               'off': 0x0000,
@@ -216,7 +211,7 @@
 
             )
 
-        self.area_header_desc = (
+    area_header_desc = (
             { 'key': 'aoe_target',
               'type': 'WORD',
               'off': 0x0200,
@@ -319,34 +314,29 @@
 
             )
 
-
-
-    def decode_file (self):
-        self.decode_header ()
+    def __init__ (self):
+        Format.__init__ (self)
+        self.expect_signature = 'PRO'
+
+
+    def read (self, stream):
+        self.read_header (stream)
         if self.header['projectile_type'] == 3:
-            self.decode_area_header ()
-
-
-    def print_file (self):
+            self.read_area_header ()
+
+
+    def printme (self):
         self.print_header ()
         if self.header['projectile_type'] == 3:
             self.print_area_header ()
-
-
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
         
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
-        
-
-    def decode_area_header (self):
+
+    def read_area_header (self, stream):
         self.area_header = {}
-        self.decode_by_desc (0x0000, self.area_header_desc, self.area_header)
+        self.read_struc (stream, 0x0000, self.area_header_desc, self.area_header)
         
     def print_area_header (self):
-        self.print_by_desc (self.area_header, self.area_header_desc)
+        self.print_struc (self.area_header, self.area_header_desc)
 
         
 register_format ('PRO', 'V1.0', PRO_Format)
ie_shell/formats/spl.py to infinity/formats/wed.py
--- a/ie_shell/formats/spl.py
+++ b/infinity/formats/wed.py
@@ -1,6 +1,6 @@
 # -*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,20 +16,12 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: spl.py,v 1.1 2005/03/02 20:44:23 edheldil Exp $
-
-from ie_shell.formats.format import Format, register_format
-
-class SPL_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'SPL'
-
-        self.extended_header_list = []
-        self.casting_feature_block_list = []
-
-
-        self.header_desc = (
+
+from infinity import core
+from infinity.format import Format, register_format
+
+class WED_Format (Format):
+    header_desc = (
             { 'key': 'signature',
               'type': 'STR4',
               'off': 0x0000,
@@ -40,409 +32,423 @@
               'off':0x0004,
               'label': 'Version'},
             
-            { 'key': 'spell_name',
-              'type': 'STRREF',
+            { 'key': 'overlay_cnt',
+              'type': 'DWORD',
               'off': 0x0008,
-              'label': 'Spell name'},
-            
-            { 'key': 'unknown1',
+              'label': '# of overlays' },
+
+            { 'key': 'door_cnt',
               'type': 'DWORD',
               'off': 0x000C,
-              'label': 'Unknown1'},
-
-            { 'key': 'completion_sound',
-              'type': 'RESREF',
+              'label': '# of doors' },
+
+            { 'key': 'overlay_off',
+              'type': 'DWORD',
               'off': 0x0010,
-              'label': 'Completion sound'},
-
-            { 'key': 'flags',
+              'label': 'Overlay offset' },
+
+            { 'key': 'secondary_header_off',
+              'type': 'DWORD',
+              'off': 0x0014,
+              'label': 'Secondary headers offset' },
+
+            { 'key': 'door_off',
               'type': 'DWORD',
               'off': 0x0018,
-              'label': 'Flags'},
-
-            { 'key': 'spell_type',
-              'type': 'WORD',
+              'label': 'Doors offset' },
+
+            { 'key': 'door_tile_cell_indices_off',
+              'type': 'DWORD',
               'off': 0x001C,
-              'enum': {0:'Special', 1:'Wizard', 2:'Cleric', 3:'Unknown', 4:'Innate'},
-              'label': 'Spell type'},
-
-            { 'key': 'exclusion_school',
-              'type': 'WORD',
-              'off': 0x001E,
-              'label': 'Exclusion school'},
-
-            { 'key': 'priest_type',
-              'type': 'WORD',
-              'off': 0x0020,
-              'mask': {0:'General', 0x4000:'Druid/Ranger', 0x8000:'Cleric/Paladin'},
-              'label': 'Priest type'},
-
-            { 'key': 'spell_school',
-              'type': 'BYTE',
-              'off': 0x0022,
-              'label': 'Spell school'},
-
-            { 'key': 'unknown2',
-              'type': 'WORD',
-              'off': 0x0023,
-              'label': 'Unknown2'},
-
-            { 'key': 'primary_type',     # school.ids
-              'type': 'WORD',
-              'off': 0x0025,
-              'label': 'Primary type'},
-
-            { 'key': 'secondary_type',     # secondary.ids
-              'type': 'BYTE',
-              'off': 0x0027,
-              'label': 'Secondary type'},
-
-            { 'key': 'unknown3',
-              'type': 'DWORD',
-              'off': 0x0028,
-              'label': 'Unknown3'},
-
-            { 'key': 'unknown4',
-              'type': 'DWORD',
-              'off': 0x002C,
-              'label': 'Unknown4'},
-
-            { 'key': 'unknown5',
-              'type': 'DWORD',
-              'off': 0x0030,
-              'label': 'Unknown5'},
-
-            { 'key': 'spell_level',
-              'type': 'DWORD',
-              'off': 0x0034,
-              'label': 'Spell level'},
-
-            { 'key': 'unknown6',
-              'type': 'WORD',
-              'off': 0x0038,
-              'label': 'Unknown6'},
-
-            { 'key': 'spellbook_icon',
-              'type': 'RESREF',
-              'off': 0x003A,
-              'label': 'Spellbook icon'},
-
-            { 'key': 'unknown7',
-              'type': 'WORD',
-              'off': 0x0042,
-              'label': 'Unknown7'},
-
-            { 'key': 'unknown8',
-              'type': 'DWORD',
-              'off': 0x0044,
-              'label': 'Unknown8'},
-
-            { 'key': 'unknown9',
-              'type': 'DWORD',
-              'off': 0x0048,
-              'label': 'Unknown9'},
-
-            { 'key': 'unknown10',
-              'type': 'DWORD',
-              'off': 0x004C,
-              'label': 'Unknown10'},
-
-            { 'key': 'spell_description',
-              'type': 'STRREF',
-              'off': 0x0050,
-              'label': 'Spell description'},
-
-            { 'key': 'unknown11',
-              'type': 'DWORD',
-              'off': 0x0054,
-              'label': 'Unknown11'},
-
-            { 'key': 'unknown12',
-              'type': 'DWORD',
-              'off': 0x0058,
-              'label': 'Unknown12'},
-
-            { 'key': 'unknown13',
-              'type': 'DWORD',
-              'off': 0x005C,
-              'label': 'Unknown13'},
-
-            { 'key': 'unknown14',
-              'type': 'DWORD',
-              'off': 0x0060,
-              'label': 'Unknown14'},
-
-            { 'key': 'extended_header_off',
-              'type': 'DWORD',
-              'off': 0x0064,
-              'label': 'Extended header offset'},
-
-            { 'key': 'extended_header_cnt',
-              'type': 'WORD',
-              'off': 0x0068,
-              'label': 'Extended header count'},
-
-            { 'key': 'feature_block_off',
-              'type': 'DWORD',
-              'off': 0x006A,
-              'label': 'Feature block offset'},
-
-            { 'key': 'casting_feature_block_off',
-              'type': 'WORD',
-              'off': 0x006E,
-              'label': 'Casting feature block offset'},
-
-            { 'key': 'casting_feature_block_cnt',
-              'type': 'WORD',
-              'off': 0x0070,
-              'label': 'Casting feature block count'},
-
-            )
-        
-        self.extended_header_desc = (
-            { 'key': 'spell_form',
-              'type': 'BYTE',
-              'off': 0x0000,
-              'label': 'Spell form'},
-
-            { 'key': 'unknown1',
-              'type': 'BYTE',
-              'off': 0x0001,
-              'label': 'Unknown1'},
-
-            { 'key': 'location',
-              'type': 'BYTE',
+              'label': 'Door tile cell indices offset' },
+
+            )
+
+    overlay_desc = (
+            { 'key': 'width',
+              'type': 'WORD',
+              'off': 0x0000,
+              'label': 'Width'},
+
+            { 'key': 'height',
+              'type': 'WORD',
               'off': 0x0002,
-              'label': 'Location'},
-
-            { 'key': 'unknown2',
-              'type': 'BYTE',
-              'off': 0x0003,
-              'label': 'Unknown2'},
-
-            { 'key': 'memorised_icon',
+              'label': 'Height'},
+
+            { 'key': 'tileset',
               'type': 'RESREF',
               'off': 0x0004,
-              'label': 'Memorised icon'},
-
-            { 'key': 'target',
+              'label': 'Tileset'},
+
+            { 'key': 'unknown_0C',
+              'type': 'DWORD',
+              'off': 0x000C,
+              'label': 'Unknown 0C'},
+
+            { 'key': 'tilemap_off',
+              'type': 'DWORD',
+              'off': 0x0010,
+              'label': 'Offset to tilemap'},
+
+            { 'key': 'tile_index_lookup_off',
+              'type': 'DWORD',
+              'off': 0x0014,
+              'label': 'Offset to tile index lookup'},
+
+            )
+        
+    secondary_header_desc = (
+            { 'key': 'polygon_cnt',
+              'type': 'DWORD',
+              'off': 0x0000,
+              'label': '# of polygons'},
+
+            { 'key': 'polygon_off',
+              'type': 'DWORD',
+              'off': 0x0004,
+              'label': 'Polygons offset'},
+
+            { 'key': 'vertex_off',
+              'type': 'DWORD',
+              'off': 0x0008,
+              'label': 'Vertices offset'},
+
+            { 'key': 'wallgrp_off',
+              'type': 'DWORD',
+              'off': 0x000C,
+              'label': 'Wall groups offset'},
+
+            { 'key': 'polygon_lut_off',
+              'type': 'DWORD',
+              'off': 0x0010,
+              'label': 'Polygon indices LUT offset'},
+
+            )
+
+    door_desc = (
+            { 'key': 'door_name',
+              'type': 'STR8',
+              'off': 0x0000,
+              'label': 'Door name'},
+
+            { 'key': 'unknown_08',
+              'type': 'WORD',
+              'off': 0x0008,
+              'label': 'Unknown 08'},
+
+            { 'key': 'door_tile_cell_ndx',
+              'type': 'WORD',
+              'off': 0x000A,
+              'label': 'First door tile cell index'},
+
+            { 'key': 'door_tile_cell_cnt',
+              'type': 'WORD',
+              'off': 0x000C,
+              'label': 'Door tile cell count'},
+
+            { 'key': 'open_door_poly_cnt',
+              'type': 'WORD',
+              'off': 0x000E,
+              'label': '# of open door polygons'},
+
+            { 'key': 'closed_door_poly_cnt',
+              'type': 'WORD',
+              'off': 0x0010,
+              'label': '# of closed door polygons'},
+
+            { 'key': 'open_door_poly_off',
+              'type': 'DWORD',
+              'off': 0x0012,
+              'label': 'Offset of open door polygons'},
+
+            { 'key': 'closed_door_poly_off',
+              'type': 'DWORD',
+              'off': 0x0016,
+              'label': 'Offset of closed door polygons'},
+
+            )
+
+    tilemap_desc = (
+            { 'key': 'tile_index_lut_ndx',
+              'type': 'WORD',
+              'off': 0x0000,
+              'label': 'Primary tile index LUT start index'},
+
+            { 'key': 'tile_index_lut_cnt',
+              'type': 'WORD',
+              'off': 0x0002,
+              'label': '# of tiles in primary tile index LUT'},
+
+            { 'key': 'secondary_tis_index',
+              'type': 'WORD',
+              'off': 0x0004,
+              'label': 'Index from TIS (secondary)'},
+
+            { 'key': 'overlay_mask',
               'type': 'BYTE',
+              'off': 0x0006,
+              'label': 'Mask of drawn overlays'},
+
+            { 'key': 'unknown_07',
+              'type': 'BYTES',
+              'off': 0x0007,
+              'size': 3,
+              'label': 'Unknown 07'},
+
+            )
+
+        # Door tile cells desc
+
+        # Tile index lookup table desc
+
+    wallgroup_desc = (
+            { 'key': 'polygon_ndx',
+              'type': 'WORD',
+              'off': 0x0000,
+              'label': 'Start polygon index'},
+
+            { 'key': 'polygon_cnt',
+              'type': 'WORD',
+              'off': 0x0002,
+              'label': '# of Polygon'},
+
+            )
+
+    polygon_desc = (
+            { 'key': 'vertex_ndx',
+              'type': 'DWORD',
+              'off': 0x0000,
+              'label': 'Start vertex index'},
+
+            { 'key': 'vertex_cnt',
+              'type': 'DWORD',
+              'off': 0x0004,
+              'label': '# of vertices'},
+
+            { 'key': 'flags',
+              'type': 'BYTE',
+              'off': 0x0008,
+              'mask': {
+                  0x01: 'shade wall',
+                  0x02: 'hovering',
+                  0x04: 'cover anims',
+                  0x08: 'cover anims 2',
+                  0x10: 'unknown 4',
+                  0x20: 'unknown 5',
+                  0x40: 'unknown 6',
+                  0x80: 'door?',
+                  },
+              'label': 'Passability flags'},
+
+            { 'key': 'unknown_09',
+              'type': 'BYTE',
+              'off': 0x0009,
+              'label': 'Unknown 09'},
+
+            { 'key': 'bbox_x1',
+              'type': 'WORD',
+              'off': 0x000A,
+              'label': 'Bounding box X min'},
+
+            { 'key': 'bbox_x2',
+              'type': 'WORD',
               'off': 0x000C,
-              'enum': {0:'Invalid', 1:'Creature', 2:'Inventory', 3:'Dead character', 4:'Area', 5:'Self', 6:'Unknown', 7:'None'},
-              'label': 'Target'},
-
-            { 'key': 'target_number',
-              'type': 'BYTE',
-              'off': 0x000D,
-              'label': 'Target number'},
-
-            { 'key': 'range',
+              'label': 'Bounding box X max'},
+
+            { 'key': 'bbox_y1',
               'type': 'WORD',
               'off': 0x000E,
-              'label': 'Range'},
-
-            { 'key': 'level_required',
+              'label': 'Bounding box Y min'},
+
+            { 'key': 'bbox_y2',
               'type': 'WORD',
               'off': 0x0010,
-              'label': 'Level required'},
-
-            { 'key': 'casting_time',
-              'type': 'DWORD',
-              'off': 0x0012,
-              'label': 'Casting time'},
-
-            { 'key': 'unknown3',
-              'type': 'DWORD',
-              'off': 0x0016,
-              'label': 'Unknown3'},
-
-            { 'key': 'unknown4',
-              'type': 'DWORD',
-              'off': 0x001A,
-              'label': 'Unknown4'},
-
-            { 'key': 'feature_cnt',
-              'type': 'WORD',
-              'off': 0x001E,
-              'label': 'Feature count'},
-
-            { 'key': 'feature_off',
-              'type': 'WORD',
-              'off': 0x0020,
-              'label': 'Feature offset'},
-
-            { 'key': 'projectile',
-              'type': 'WORD',
-              'off': 0x0022,
-              'label': 'Projectile'},
-
-            { 'key': 'unknown5',
-              'type': 'WORD',
-              'off': 0x0024,
-              'label': 'Unknown5'},
-
-            { 'key': 'animation',
-              'type': 'WORD',
-              'off': 0x0026,
-              'label': 'Animation'},
-
-            )
-
-        self.feature_block_desc = (
-            { 'key': 'opcode_number',
-              'type': 'WORD',
-              'off': 0x0000,
-              'label': 'Opcode number'},
-
-            { 'key': 'target',
-              'type': 'BYTE',
+              'label': 'Bounding box Y max'},
+            
+            )
+
+        # Polygon index LUT desc
+
+    vertex_desc = (
+            { 'key': 'x',
+              'type': 'WORD',
+              'off': 0x0000,
+              'label': 'X'},
+
+            { 'key': 'y',
+              'type': 'WORD',
               'off': 0x0002,
-              'enum': {0:'None', 1:'Self', 2:'Pre-Target', 3:'Party', 4:'Global', 5:'Non-Party', 6:'Party?', 7:'Unknown1', 8:'Except-Self', 9:'Original-Caster', 10:'Unknown2', 11:'Unknown3', 12:'Unknown4', 13:'Unknown5'},
-              'label': 'Target'},
-
-            { 'key': 'power',
-              'type': 'BYTE',
-              'off': 0x0003,
-              'label': 'Power'},
-
-            { 'key': 'parameter1',
-              'type': 'DWORD',
-              'off': 0x0004,
-              'label': 'Parameter1'},
-
-            { 'key': 'parameter2',
-              'type': 'DWORD',
-              'off': 0x0008,
-              'label': 'Parameter2'},
-
-            { 'key': 'timing_mode',
-              'type': 'BYTE',
-              'off': 0x000C,
-              'enum': {0:'Duration', 1:'Permanent', 2:'While equipped', 3:'Delayed duration', 4:'Delayed2?', 5:'Delayed3?', 6:'Duration2?', 7:'Permanent2?', 8:'Permanent3?', 9:'Permanent? (after Death)', 10:'Trigger'},
-              'label': 'Timing mode'},
-
-            { 'key': 'resistance',
-              'type': 'BYTE',
-              'off': 0x000D,
-              'enum': {0:'Nonmagical', 1:'Dispell/Not bypass', 2:'Not dispell/Not bypass', 3:'Dispell/Bypass'},
-              'label': 'Resistance'},
-
-            { 'key': 'duration',
-              'type': 'DWORD',
-              'off': 0x000E,
-              'label': 'Duration'},
-
-            { 'key': 'probability1',
-              'type': 'BYTE',
-              'off': 0x0012,
-              'label': 'Probability1'},
-
-            { 'key': 'probability2',
-              'type': 'BYTE',
-              'off': 0x0013,
-              'label': 'Probability2'},
-
-            { 'key': 'resource',
-              'type': 'RESREF',
-              'off': 0x0014,
-              'label': 'Resource'},
-
-            { 'key': 'dice_thrown',
-              'type': 'DWORD',
-              'off': 0x001C,
-              'label': 'Dice thrown'},
-
-            { 'key': 'dice_sides',
-              'type': 'DWORD',
-              'off': 0x0020,
-              'label': 'Dice sides'},
-
-            { 'key': 'saving_throw_type',
-              'type': 'DWORD',
-              'off': 0x0024,
-              'mask': {0:'None', 1:'Spells', 2:'Breathe', 4:'Death', 8:'Wands', 16:'Polymorph'},
-              'label': 'Saving throw type'},
-
-            { 'key': 'saving_throw_bonus',
-              'type': 'DWORD',
-              'off': 0x0028,
-              'label': 'Saving throw bonus'},
-
-            { 'key': 'unknown',
-              'type': 'DWORD',
-              'off': 0x002C,
-              'label': 'Unknown'},
-
-            )
-
-
-    def decode_file (self):
-        self.decode_header ()
-
-        off = self.header['extended_header_off']
-        for i in range (self.header['extended_header_cnt']):
+              'label': 'Y'},
+            
+            )
+
+    def __init__ (self):
+        Format.__init__ (self)
+        self.expect_signature = 'WED'
+
+        self.secondary_header = {}
+        self.overlay_list = []
+        self.door_list = []
+        self.polygon_list = []
+
+
+    def read (self, stream):
+        self.read_header (stream)
+        self.read_secondary_header (stream, self.header['secondary_header_off'], self.secondary_header)
+
+
+        off = self.header['overlay_off']
+        for i in range (self.header['overlay_cnt']):
             obj = {}
-            self.decode_extended_header (off, obj)
-            self.extended_header_list.append (obj)
-            off = off + 40
-
-        off = self.header['feature_block_off'] + self.header['casting_feature_block_off'] * 48
-        for i in range (self.header['casting_feature_block_cnt']):
+            self.read_overlay (stream, off, obj)
+            self.overlay_list.append (obj)
+            off = off + 24
+
+        # NOTE: door is nested, so we can't use read_list () here
+        off = self.header['door_off']
+        for i in range (self.header['door_cnt']):
             obj = {}
-            self.decode_feature_block (off, obj)
-            self.casting_feature_block_list.append (obj)
-            off = off + 48
-
-
-    def print_file (self):
+            self.read_door (stream, off, obj)
+            self.door_list.append (obj)
+            off = off + 26
+
+
+
+    def printme (self):
         self.print_header ()
+        self.print_secondary_header ()
 
         i = 0
-        for obj in self.extended_header_list:
-            print '#%d' %i
-            self.print_extended_header (obj)
+        for obj in self.overlay_list:
+            print 'Overlay #%d' %i
+            self.print_overlay (obj)
             i = i + 1
 
         i = 0
-        for obj in self.casting_feature_block_list:
-            print '#%d' %i
-            self.print_feature_block (obj)
+        for obj in self.door_list:
+            print 'Door #%d' %i
+            self.print_door (obj)
             i = i + 1
 
 
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
-        
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
-        
-
-    def decode_extended_header (self, offset, obj):
-        self.decode_by_desc (offset, self.extended_header_desc, obj)
-
-        obj['feature_list'] = []
-        off2 = self.header['feature_block_off'] + obj['feature_off'] * 48
-        for j in range (obj['feature_cnt']):
+    def read_secondary_header (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.secondary_header_desc, obj)
+        
+        self.read_list (stream, 'polygon',  self.secondary_header,)
+        # vertices
+        # wallgroups
+        # polygon indices LUT
+
+    def print_secondary_header (self):
+        self.print_struc (self.secondary_header, self.secondary_header_desc)
+        self.print_list ('polygon')
+        # vertices
+        # wallgroups
+        # polygon indices LUT
+
+        
+    def read_overlay (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.overlay_desc, obj)
+        obj['tilemap_list'] = []
+        cnt = obj['width'] * obj['height']
+        size = self.get_struc_size (self.tilemap_desc)
+
+        off2 = obj['tilemap_off']
+        tile_index_cnt = 0
+        
+        for i in range (cnt):
             obj2 = {}
-            self.decode_feature_block (off2, obj2)
-            obj['feature_list'].append (obj2)
-            off2 = off2 + 48
-            
-    def print_extended_header (self, obj):
-        self.print_by_desc (obj, self.extended_header_desc)
-
-        j = 0
-        for feature in obj['feature_list']:
-            print 'F #%d' %j
-            self.print_feature_block (feature)
-            j = j + 1
-
-    def decode_feature_block (self, offset, obj):
-        self.decode_by_desc (offset, self.feature_block_desc, obj)
-        
-    def print_feature_block (self, obj):
-        self.print_by_desc (obj, self.feature_block_desc)
-
-        
-register_format ('SPL', 'V1', SPL_Format)
+            self.read_tilemap (stream, off2, obj2,  obj)
+            obj['tilemap_list'].append (obj2)
+            off2 = off2 + size
+            tile_index_cnt +=  obj2['tile_index_lut_cnt']
+
+        obj['tile_index_list'] = []
+        size = 2 # FIXME: don't hardwire the size
+        off2 = obj['tile_index_lookup_off']
+        
+        for i in range (tile_index_cnt):
+            tile_index = stream.read_word (off2)
+            obj['tile_index_list'].append (tile_index)
+            off2 += size
+
+
+    def print_overlay (self, obj):
+        self.print_struc (obj, self.overlay_desc)
+
+        i = 0
+        for obj2 in obj['tilemap_list']:
+            print 'Tilemap #%d' %i
+            self.print_tilemap (obj2)
+            i = i + 1
+
+        print "Tile indices:",  obj['tile_index_list']
+        print
+
+
+    def read_door (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.door_desc, obj)
+
+        obj['open_door_poly_list'] = []
+        off = obj['open_door_poly_off']
+        for i in range (obj['open_door_poly_cnt']):
+            obj2 = {}
+            self.read_polygon (stream, off, obj2)
+            obj['open_door_poly_list'].append (obj2)
+            off = off + 34
+
+        obj['closed_door_poly_list'] = []
+        off = obj['closed_door_poly_off']
+        for i in range (obj['closed_door_poly_cnt']):
+            obj2 = {}
+            self.read_polygon (stream, off, obj2)
+            obj['closed_door_poly_list'].append (obj2)
+            off = off + 34
+
+        
+    def print_door (self, obj):
+        self.print_struc (obj, self.door_desc)
+
+        i = 0
+        for obj2 in obj['open_door_poly_list']:
+            print 'Open Poly #%d' %i
+            self.print_polygon (obj2)
+            i = i + 1
+
+        i = 0
+        for obj2 in obj['closed_door_poly_list']:
+            print 'Closed Poly #%d' %i
+            self.print_polygon (obj2)
+            i = i + 1
+
+        
+    def read_tilemap (self, stream, offset, obj, overlay):
+        self.read_struc (stream, offset, self.tilemap_desc, obj)
+        
+    def print_tilemap (self, obj):
+        self.print_struc (obj, self.tilemap_desc)
+        
+    def read_wallgroup (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.wallgroup_desc, obj)
+        
+    def print_wallgroup (self, obj):
+        self.print_struc (obj, self.wallgroup_desc)
+
+
+    def read_polygon (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.polygon_desc, obj)
+        
+    def print_polygon (self, obj):
+        self.print_struc (obj, self.polygon_desc)
+
+        
+    def read_vertex (self, stream, offset, obj):
+        self.read_struc (stream, offset, self.vertex_desc, obj)
+        
+    def print_vertex (self, obj):
+        self.print_struc (obj, self.vertex_desc)
+
+        
+        
+
+register_format ('WED', 'V1.3', WED_Format)
ie_shell/formats/stream.py to infinity/formats/d2a.py
--- a/ie_shell/formats/stream.py
+++ b/infinity/formats/d2a.py
@@ -1,6 +1,6 @@
 # -*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,242 +16,166 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: stream.py,v 1.3 2006/07/08 14:29:27 edheldil Exp $
 
-import gzip
-import os.path
 import re
-import string
-import struct
+import sys
+from infinity.format import Format, register_format
 
-from ie_shell import core
+class D2A_Format (Format):
+    def __init__ (self):
+        Format.__init__ (self)
+        #self.expect_signature = '2DA'
 
-class Stream:
-    def __init__ (self):
-        self.is_open = False
+        self.rows = []
+        self.cols = []
+        self.cells = {}
+
+
+    def read (self, stream):
+        s = ""
+        line_no = 0
+
+        self.signature = stream.get_line ().strip ()
+        # FIXME: check the header
         
-    def open (self):
-        pass
-    
-    def close (self):
-        pass
-    
-    def seek (self, offset):
-        pass
+        self.default_value = stream.get_line ().strip ()
+        
+        s = stream.get_line ()
+        s = s.strip ()
+        # FIXME: canonize the column names
+        self.cols = map (lambda s: s.strip (), s.split (None))
+        line_no = 3
+        
+        while s is not None:
+            s = stream.get_line ()
+            if s is None:
+                break
+            
+            line_no = line_no + 1
 
-    def read (self, count = None):
-        pass
+            s = s.strip ()
+            if s == '':
+                continue
 
-    def readline (self):
+##            if line_no == 1 and (re.match ("^[0-9]+$", s) or re.match ("^IDS", s)):
+##                continue
+##            if line_no == 2 and (re.match ("^[0-9]+$", s) or re.match ("^IDS", s)):
+##                continue
+##            if line_no == 3 and (re.match ("^[0-9]+$", s) or re.match ("^IDS", s)):
+##                continue
+
+            values = s.split (None)
+            key = values[0]
+            values = values[1:]
+            key = key.strip ()
+            values = map (lambda v: v.strip (), values)
+            self.rows.append (key)
+
+            for col in self.cols:
+                try: 
+                    value = values[0]
+                    values.pop (0)
+                except: 
+                    value = None
+                
+                self.cells[(key, col)] = value
+
+    def get (self, row, col):
+        row = self.get_row_name (row)
+        col = self.get_col_name (col)
+
+        if row != '' and col != '':
+            return self.cells[(row, col)]
+        elif row != '':
+            return row
+        elif col != '':
+            return col
+        else:
+            return ''
+
+
+    def get_row_name (self, row):
+        # FIXME: canonize
+        if row == -1 or row == '':
+            return ''
+        elif type (row) == type (''):
+            if self.rows.index (row) >= 0:
+                return row
+        else:
+            return self.rows[row]
+
+
+    def get_col_name (self, col):
+        # FIXME: canonize
+        if col == -1 or col == '':
+            return ''
+        elif type (col) == type (''):
+            if self.cols.index (col) >= 0:
+                return col
+        else:
+            return self.cols[col]
+
+
+    def get_row (self, row, include_heading = False):
+        res = []
+        row = self.get_row_name (row)
+        
+        if include_heading:
+            res.append (self.get (row, ''))
+            
+        for col in self.cols:
+            res.append (self.get (row, col))
+
+        return res
+
+
+    def get_col (self, col, include_heading = False):
+        res = []
+        col = self.get_col_name (col)
+        
+        if include_heading:
+            res.append (self.get ('', col))
+            
+        for row in self.rows:
+            res.append (self.get (row, col))
+
+        return res
+
+
+    def trim (self):
+        # TODO: replaces values equal to default with implicit default
         pass
 
 
-    def get_char (self, offset):
-        # offset == None means "current offset" here
-        if offset != None:
-            self.seek (offset)
-            
-        return self.read (1)
-        #v = self.read (1)
-        #return struct.unpack ('c', v)[0]
-
-    def get_line (self):
-        #return self.readline ()
-        return self.decode_line_string ()
-
-    def decode_word (self, offset):
-        # offset == None means "current offset" here
-        if offset != None:
-            self.seek (offset)
-        v = self.read (2)
-        return struct.unpack ('<H', v)[0]
-
-    def decode_dword (self, offset):
-        # offset == None means "current offset" here
-        if offset != None:
-            self.seek (offset)
-        v = self.read (4)
-        return struct.unpack ('<I', v)[0]
-
-    def decode_sized_string (self, offset, size):
-        # offset == None means "current offset" here
-        if offset != None:
-            self.seek (offset)
-        v = self.read (size)
-        
-        return struct.unpack ('%ds' %size, v)[0]
-
-    def decode_asciiz_string (self, off):
-        s = ''
-        
-        while 1:
-            c = self.get_char (off)
-            if c == '\0': break
-            s = s + c
-            off = off + 1
-            
-        return s
-
-    def decode_line_string (self):
-        s = ''
-        
-        while 1:
-            c = self.get_char (None)
-            if c == '\n': break
-            if c == '':
-                if s == '':
-                    s = None
-                break
-            
-            s = s + c
-            
-        return s
-
-    def decode_resref (self, off):
-        return self.decode_sized_string (off, 8)
-
-    def decode_blob (self, offset, size = None):
-        # offset == None means "current offset" here
-        # size == None means "till the end of stream"
-        if offset != None:
-            self.seek (offset)
-
-        return self.read (size)
-        
-
-    def get_signature (self):
-        was_open = self.is_open
-        if not self.is_open:
-            self.open ()
-
-        s = self.decode_sized_string (0, 8)
-
-        if not was_open:
-            self.close ()
-        else:
-            self.seek (0)
+    def get_col_width (self, col):
+        cols = self.get_col (col, include_heading = True)
+        return reduce (lambda m, n: max (m, len (n)), cols, 0)
 
 
-        if re.match ("[0-9]{1,4}[\r\n ]", s) or re.match ("0[xX][0-9A-Fa-f]{1,4} ", s) or re.match ("-1[\r\n]", s):
-            signature = "IDS"
-            version = ""
-        else:
-            signature = s[0:4].strip ()
-            version = s[4:8].strip ()
+    # FIXME: options for same column width, minimal width, using tabs, left alignment, ...
+    def printme (self):
+        print 'Signature:', self.signature
+        print 'Default value:', self.default_value
+        print self.cols
+        for row in self.rows:
+            values = self.get_row (row)
+            print row + ':', 
+            for v in values:
+                if v is None:
+                    break
+                print v,
+            print
+        
+        hsize = self.get_col_width (-1) 
+        sizes = map (self.get_col_width, self.get_row (-1))
+        
+        for row_name in self.get_col (-1, include_heading = True):
+            values = self.get_row (row_name, include_heading = False)
+            
+            print row_name.ljust (hsize),
+            for i in range (len (values)):
+                value = values[i]
+                print value.rjust (sizes[i]),
+            print
 
-        return (signature, version)
-
-    def get_format (self, type = 0):
-        signature, version = self.get_signature ()
-        fmt = core.get_format (signature, version)
-        
-        if fmt == None and type != 0:
-            fmt = core.get_format_by_type (type)
-
-        return fmt
-
-
-    def load_object (self, type = 0):
-        fmt = self.get_format ()
-        return fmt (self)
-
-
-
-class FileStream (Stream):
-    def __init__ (self, filename):
-        Stream.__init__ (self)
-
-        self.filename = filename
-
-    def open (self):
-        self.fh = open (self.filename, "r")
-        self.is_open = True
-
-    def close (self):
-        self.fh.close ()
-        self.is_open = False
-
-    def seek (self, offset):
-        self.fh.seek (offset)
-
-    def read (self, size = None):
-        if size != None:
-            return self.fh.read (size)
-        else:
-            return self.fh.read ()
-        
-
-    def __repr__ (self):
-        return "<FileStream: %s>" %self.filename
-
-
-class MemoryStream (Stream):
-    def __init__ (self, membuffer):
-        Stream.__init__ (self)
-
-        self.buffer = membuffer
-
-    def open (self):
-        self.offset = 0
-        self.is_open = True
-
-    def decrypt (self):
-        for i in range (len (self.buffer)):
-            print chr (ord (self.buffer[i]) ^ ord (core.xor_key[i]))
-
-    def close (self):
-        self.is_open = False
-    
-    def seek (self, offset):
-        self.offset = offset
-
-    def read (self, count = None):
-        if count != None:
-            data = self.buffer[self.offset:self.offset+count]
-        else:
-            data = self.buffer[self.offset:]
-            
-        self.offset = self.offset + len (data)
-        return data
-
-
-
-class ResourceStream (MemoryStream):
-    def __init__ (self, name, type = None):
-        MemoryStream.__init__ (self, '')
-        self.resref = name
-        self.type = type
-
-    def open (self):
-        if core.keys == None:
-            raise RuntimeError, "Core game files are not loaded. See load_game ()."
-
-        oo = core.keys.get_resref_by_name_re (self.resref)
-        if self.type != None:
-            oo = filter (lambda o: o['type'] == self.type, oo)
-
-        if len (oo) > 1 and self.type == None:
-            raise RuntimeError, "More than one result"
-
-        o = oo[0]
-     
-        src_file = core.keys.bif_list[o['locator_src_ndx']]
-        b = core.formats['BIFF'] (os.path.join (core.game_dir, src_file['file_name']))
-        b.decode_file ()
-        obj = b.file_list[o['locator_ntset_ndx']]
-        b.get_file_data (obj)
-
-        self.buffer = obj['data']
-        MemoryStream.open (self)
-
-    def load_object (self):
-        return Stream.load_object (self, self.type)
-
-
-    def __repr__ (self):
-        return "<ResStream: %s>" %self.resref
-
-
-class CompressedStream (MemoryStream):
-    def __init__ (self, membuffer):
-        MemoryStream.__init__ (self, gzip.zlib.decompress (membuffer))
+register_format ('2DA', '', D2A_Format)
ie_shell/formats/tis.py to infinity/formats/wfx.py
--- a/ie_shell/formats/tis.py
+++ b/infinity/formats/wfx.py
@@ -1,6 +1,6 @@
 # -*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,17 +16,12 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: tis.py,v 1.1 2006/07/08 14:29:27 edheldil Exp $
 
-from ie_shell.formats.format import Format, register_format, core
+from infinity import core
+from infinity.format import Format, register_format
 
-class TIS_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'TIS'
-
-
-        self.header_desc = (
+class WFX_Format (Format):
+    header_desc = (
             { 'key': 'signature',
               'type': 'STR4',
               'off': 0x0000,
@@ -37,43 +32,43 @@
               'off':0x0004,
               'label': 'Version'},
             
-            { 'key': 'count',
+            { 'key': 'unknown_08',
               'type': 'DWORD',
               'off': 0x0008,
-              'label': 'Count' },
+              'label': 'Unknown 08' },
 
-            { 'key': 'length',
+            { 'key': 'flags',
               'type': 'DWORD',
               'off': 0x000C,
-              'label': 'Length' },
+              'mask': { 0x1: 'unknown 0', 0x2: 'unknown 1', 0x4: 'enabled' },
+              'label': 'Flags' },
 
 
-            { 'key': 'size',
+            { 'key': 'deviation',
               'type': 'DWORD',
               'off': 0x0010,
-              'label': 'Size'},
+              'label': 'Deviation'},
 
-            { 'key': 'offset',
-              'type': 'DWORD',
+            { 'key': 'unknown 14',
+              'type': 'BYTES',
               'off': 0x0014,
-              'label': 'Offset'},
+              'size': 244,
+              'label': 'Unknown 14'},
 
             )
 
 
-    def decode_file (self):
-        self.decode_header ()
+    def __init__ (self):
+        Format.__init__ (self)
+        self.expect_signature = 'WFX'
 
-    def print_file (self):
+
+    def read (self, stream):
+        self.read_header (stream)
+
+    def printme (self):
         self.print_header ()
-
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
-        
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
         
 
         
-register_format ('TIS', 'V1  ', TIS_Format)
+register_format ('WFX', 'V1.0', WFX_Format)
ie_shell/formats/tlk.py to infinity/builtins.py
--- a/ie_shell/formats/tlk.py
+++ b/infinity/builtins.py
@@ -1,6 +1,6 @@
-# -*-python-*-
+#-*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,149 +16,221 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: tlk.py,v 1.6 2006/07/08 14:29:27 edheldil Exp $
-
-import re
-import string
-import sys
-
-from ie_shell import core
-from ie_shell.formats.format import Format, register_format, TICK_SIZE, TACK_SIZE
-
-class TLK_Format (Format):
-    
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
+"""Built-in functions for the iesh shell. 
+
+Functions which are ``built-into'' the iesh shell, meaning that this
+module contents are automatically imported directly into iesh's 
+namespace on startup."""
+
+import os.path
+import traceback
+
+from infinity import core
+from infinity.stream import ResourceStream, FileStream
+
+###################################################
+def load_game (game_dir, chitin_file = None, dialog_file = None):
+    """Load key and dialog files from the 'game_dir' directory.
+
+    The `game_dir' parameter is mandatory, the others are optional.
+    Many commands assume that these two files are
+    already loaded. The loaded objects are stored in core.keys and
+    core.strrefs."""
+
+    if chitin_file is None:
+        chitin_file = core.get_option ('core.chitin_file')
+
+    if dialog_file is None:
+        dialog_file = core.get_option ('core.dialog_file')
+
+    core.game_dir = game_dir
+    core.chitin_file = chitin_file
+    core.dialog_file = dialog_file
+    
+    # Load RESREF index file (CHITIN.KEY)
+    stream = FileStream ().open (os.path.join (game_dir, chitin_file))
+    core.keys = core.get_format ('KEY') ()
+    core.keys.read_header (stream)
+    print "Loading %d file refs and %d RESREFs. This may take ages" %(core.keys.header['num_of_bifs'], core.keys.header['num_of_resrefs'])
+    core.keys.read (stream)
+    stream.close ()
+
+    # LOAD STRREF index file (DIALOG.TLK)
+    stream = FileStream ().open (os.path.join (game_dir, dialog_file))
+    core.strrefs = core.get_format ('TLK') ()
+    core.strrefs.read_header (stream)
+    print "Loading %d STRREFs. This may take eternity" %(core.strrefs.header['num_of_strrefs'])
+    core.strrefs.read (stream)
+    stream.close ()
+
+###################################################
+def load_object (name, type = None,  index = 0):
+    """Load named object from a file located in filesystem or in game's data.
+
+    Load file or resref `name'  and return Format object of appropriate type. 
+    If `name' is not unique, specify resource type with `type' and eventually 
+    `index' if there's still more than one."""
+
+    try:
+        stream = FileStream().open (name)
+    except:
+        stream = ResourceStream().open (name, type,  index)
+    
+    res = stream.load_object ()
+    stream.close ()
+    return res
+
+###################################################
+def print_object (name, type = None,  index = 0):
+    """Load and print named object. See `load_object()' for details."""
+    
+    obj = load_object (name,  type,  index)
+    obj.printme ()
+
+###################################################
+def export_object (name, filename, type = None, index = 0):
+    """Export resource `name' into file `filename'. 
+
+    If the `name' is not
+    unique, specify resource type with `type' and eventually `index' if
+    there's still more than one"""
+
+    stream = ResourceStream ().open (name,  type,  index)
+    fh = open (filename,  "w")
+    fh.write (stream.buffer)
+    fh.close ()
+    stream.close ()
+    
+###################################################
+def iterate_objects (fn, filter_fn = None, sort_fn = None, error_fn = None):
+    """Execute function for each object of specified type."""
+    
+    # FIXME: this function opens and decodes a bif file EACH time some
+    #   object from it is accessed, so it's slow as hell. It should use
+    #   some caching
+
+    #def resref_to_obj (res):
+    #    print res['resref_name']
+    #    return ResourceStream (res['resref_name'], type).load_object ()
+
+    resrefs = []
+    resrefs.extend (core.keys.resref_list)
+    if callable (filter_fn):
+        resrefs = filter (filter_fn, resrefs)
+    
+    if callable (sort_fn):
+        resrefs.sort (sort_fn)
+
+    for res in resrefs:
+        print res['resref_name']
+        obj = None
+
+        if error_fn in ['msg', 'traceback'] or callable (error_fn):
+            try:
+                obj = ResourceStream ().open (res['resref_name'], res['type']).load_object ()
+                fn (res, obj)
+            except Exception, e:
+                if callable (error_fn):
+                    error_fn (res, obj, e)
+                elif error_fn == 'msg':
+                    print e
+                elif error_fn == 'traceback':
+                    traceback.print_exc ()
+        else:
+            obj = ResourceStream ().open (res['resref_name'], res['type']).load_object ()
+            fn (res, obj)
+
+
+###################################################
+def iterate_objects_by_type (type, fn, filter_fn = None, sort_fn = None, error_fn = None):
+    """Execute function for each object of specified type."""
+    return iterate_objects (fn, filter_fn = lambda r: r['type'] == type, sort_fn = sort_fn, error_fn = error_fn)
+    
+###################################################
+def find_str (regexp):
+    """Find all strings in core.strrefs matching regular expression.
+    
+    Find all strings in loaded DIALOG.TLK file matching regular
+    expression 'regexp' and prints the STRREFs and strings to stdout."""
+    
+    for o in core.strrefs.get_strref_by_str_re(text):
+        print core.strrefs.strref_list.index(o), o['string']
+
+
+###################################################
+def sprintf (format_str, *params):
+    return  format_str %(params)
+
+def printf (format_str, *params):
+    print sprintf (format_str, *params)
+
+
+###################################################
+def load_ids ():
+    """Load (or try to) all IDS files."""
+
+    def p (obj):
+        obj.read ()
+        print obj.stream.resref
+        #obj.print_file ()
         
-        self.expect_signature = 'TLK'
-
-        self.strref_list = []
-        
-        self.header_desc = (
-            { 'key': 'signature',
-              'type': 'STR4',
-              'off': 0x0000,
-              'label': 'Signature' },
+    iterate_objects_by_type (0x03f0, p)
+
+
+###################################################
+def print_restype_stats ():
+    """Print list of RESREFs with count of objects of each type."""
+
+    stats = {}
+    
+    for o in core.keys.resref_list:
+        if not stats.has_key (o['type']):
+            stats[o['type']] = 1
+        else:
+            stats[o['type']] = stats[o['type']] + 1
+
+    for s in stats.keys ():
+        if core.restype_hash.has_key (s):
+            type = core.restype_hash[s]
+        else:
+            type = '??'
+        print "0x%04x (%s):\t%5d" %(s, type, stats[s])
+
+    return stats
+
+###################################################
+def print_formats ():
+    """List recognized/implemented IE file formats"""
+    
+    flist = filter (lambda a: a[0][1] is not None,  core.formats.items ())
+    flist.sort (lambda a,  b: 2 * cmp (a[0][0],  b[0][0]) + cmp (a[0][1],  b[0][1]))
+
+    print "sign vers  class                 desc & status"
+    print "-----------------------------------------------"
+    for key,  value in flist:
+        version = ''
+        desc = ''
+        if key[1] is not None:
+            version = key[1]
+        if value[1] is not None:
+            desc = '- ' + value[1]
+        klass_name = str (value[0])
+        klass_name = klass_name.replace ('infinity.formats.',  '')
             
-            { 'key': 'version',
-              'type': 'STR4',
-              'off':0x0004,
-              'label': 'Version'},
-            
-            { 'key': 'unknown',
-              'type': 'WORD',
-              'off': 0x0008,
-              'label': '???' },
-
-            { 'key': 'num_of_strrefs',
-              'type': 'DWORD',
-              'off': 0x000A,
-              'label': '# of strref entries'},
-            
-            { 'key': 'string_offset',
-              'type': 'DWORD',
-              'off': 0x000E,
-              'label': 'First string data offset'},
-            )
-        
-
-        self.strref_record_desc = (
-            { 'key': 'content_type',
-              'type': 'WORD',
-              'off': 0x0000,
-              'label': 'Content of this entry' },
-            
-            { 'key': 'sound_resref',
-              'type': 'RESREF',
-              'off': 0x0002,
-              'label': 'Sound resref' },
-            
-            { 'key': 'volume_variance',
-              'type': 'DWORD',
-              'off': 0x000A,
-              'label': 'Volume variance' },
-            
-            { 'key': 'pitch_variance',
-              'type': 'DWORD',
-              'off': 0x000E,
-              'label': 'Pitch variance' },
-            
-            { 'key': 'string_offset',
-              'type': 'DWORD',
-              'off': 0x0012,
-              'label': 'String data rel offset' },
-
-            { 'key': 'string_len',
-              'type': 'DWORD',
-              'off': 0x0016,
-              'label': 'String data size' },
-
-            { 'key': 'string',
-              'type': '_STRING',
-              'off': 0x0000,
-              'label': 'String' },
-
-            )
-
-
-    def decode_file (self):
-        self.decode_header ()
-
-        off = 0x0012
-            
-        if not self.get_option ('tlk_decode_strrefs'):
-            return
-        
-        for i in range (self.header['num_of_strrefs']):
-            
-            obj = {}
-            self.decode_strref_record (off, obj)
-            self.strref_list.append (obj)
-            off = off + 26
-
-            if not (i % TICK_SIZE):
-                sys.stdout.write('.')
-                if not (i % TACK_SIZE):
-                    sys.stdout.write('%d' %i)
-                sys.stdout.flush ()
-        print
-
-
-    def print_file (self):
-        self.print_header ()
-
-        i = 0
-        for obj in self.strref_list:
-            print '#%d' %i
-            self.print_strref_record (obj)
-            i = i + 1
-
-
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
-        
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
-        
-
-    def decode_strref_record (self, offset, obj):
-        self.decode_by_desc (offset, self.strref_record_desc, obj)
-        obj['string'] = self.stream.decode_sized_string (self.header['string_offset'] + obj['string_offset'], obj['string_len'])
-        obj['string_raw'] = obj['string']
-        if core.lang_trans:
-            obj['string'] = string.translate (obj['string'], core.lang_trans)
-        
-    def print_strref_record (self, obj):
-        self.print_by_desc (obj, self.strref_record_desc)
-
-
-
-    def get_strref_by_str_re (self, text):
-        rx = re.compile (text)
-        return filter (lambda s, rx=rx: rx.search (s['string']), self.strref_list)
-
-
-TLK_Format.default_options['tlk_decode_strrefs'] = 1
-        
-register_format ('TLK', 'V1', TLK_Format)
+        print "%-4s %-4s  %-20s  %s" %(key[0],  version,  klass_name,  desc)
+
+
+###################################################
+def print_options (desc = True):
+    """List known options with their values. If `desc' is True, list their descriptions as well."""
+
+    options = core.options.items ()
+    options.sort ()
+    for key,  opt in options:
+        if desc:
+            print "%-30s - %s [%s]" %(key,  opt[1],  repr (opt[0]))
+        else:
+            print "%-30s - %s" %(key,  repr (opt[0]))
+
+###################################################
+# End of file builtins.py
ie_shell/formats/vvc.py to infinity/formats/vvc.py
--- a/ie_shell/formats/vvc.py
+++ b/infinity/formats/vvc.py
@@ -1,6 +1,6 @@
 # -*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,17 +16,13 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: vvc.py,v 1.1 2006/07/08 14:29:27 edheldil Exp $
-
-from ie_shell.formats.format import Format, register_format, core
+
+from infinity import core
+from infinity.format import Format, register_format
 
 class VVC_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'VVC'
-
-
-        self.header_desc = (
+
+    header_desc = (
             { 'key': 'signature',
               'type': 'STR4',
               'off': 0x0000,
@@ -251,21 +247,16 @@
 
             )
 
-
-
-    def decode_file (self):
-        self.decode_header ()
-
-    def print_file (self):
+    def __init__ (self):
+        Format.__init__ (self)
+        self.expect_signature = 'VVC'
+
+    def read (self, stream):
+        self.read_header (stream)
+
+    def printme (self):
         self.print_header ()
 
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
-        
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
-        
 
         
 register_format ('VVC', 'V1.0', VVC_Format)
ie_shell/formats/wed.py to infinity/formats/stor.py
--- a/ie_shell/formats/wed.py
+++ b/infinity/formats/stor.py
@@ -1,6 +1,6 @@
 # -*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,403 +16,301 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: wed.py,v 1.1 2006/07/08 14:29:27 edheldil Exp $
-
-from ie_shell.formats.format import Format, register_format, core
-
-class WED_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'WED'
-
-        self.secondary_header = {}
-        self.overlay_list = []
-        self.door_list = []
-
-        self.header_desc = (
+
+from infinity.format import Format, register_format
+
+
+class STOR_Format (Format):
+
+    header_desc = (
             { 'key': 'signature',
-              'type': 'STR4',
-              'off': 0x0000,
-              'label': 'Signature' },
+                'type': 'STR4',
+                'off': 0x0000,
+                'label': 'Signature' },
             
             { 'key': 'version',
-              'type': 'STR4',
-              'off':0x0004,
-              'label': 'Version'},
-            
-            { 'key': 'overlay_cnt',
-              'type': 'DWORD',
-              'off': 0x0008,
-              'label': '# of overlays' },
-
-            { 'key': 'door_cnt',
-              'type': 'DWORD',
-              'off': 0x000C,
-              'label': '# of doors' },
-
-            { 'key': 'overlay_off',
-              'type': 'DWORD',
-              'off': 0x0010,
-              'label': 'Overlay offset' },
-
-            { 'key': 'secondary_header_off',
-              'type': 'DWORD',
-              'off': 0x0014,
-              'label': 'Secondary headers offset' },
-
-            { 'key': 'door_off',
-              'type': 'DWORD',
-              'off': 0x0018,
-              'label': 'Doors offset' },
-
-            { 'key': 'door_tile_cell_indices_off',
-              'type': 'DWORD',
-              'off': 0x001C,
-              'label': 'Door tile cell indices offset' },
-
+                'type': 'STR4',
+                'off':0x0004,
+                'label': 'Version'},
+
+            { 'key': 'type',
+                'type': 'DWORD',
+                'off': 0x0008,
+                'enum': { 0: 'Store', 1: 'Tavern', 2: 'Inn', 3: 'Temple' },
+                'label': 'Type' },
+
+            { 'key': 'name',
+                'type': 'STRREF',
+                'off': 0x000C,
+                'label': 'Name strref' },
+
+            { 'key': 'flags',
+                'type': 'DWORD',
+                'off': 0x0010,
+                'mask':  { 0x0001: 'Sells items', 0x0002: 'Buys items', 0x0004: 'Identify', 0x0008: 'Steal', 0x0010: 'Donate', 0x0020: 'Cures', 0x00040: 'Drinks', 0x0080: 'Unknown bit7', 0x0100: 'Unknown bit8', 0x0200: 'Unknown bit9', 0x0400: 'Unknown/Recharge?', 0x0800: 'Unknown bit11', 0x1000: 'Buy fenced goods' },
+                'label': 'Flags' },
+
+            { 'key': 'sell_price_markup',
+                'type': 'DWORD',
+                'off': 0x0014,
+                'label': 'Sell price markup (% of base price when store sells)' },
+
+            { 'key': 'buy_price_markup',
+                'type': 'DWORD',
+                'off': 0x0018,
+                'label': 'Buy price markup (% of base price when store buys)' },
+
+
+            { 'key': 'depreciation_rate',
+                'type': 'DWORD',
+                'off': 0x001C,
+                'label': 'Depreciation rate' },
+
+            { 'key': 'steal_failure_chance',
+                'type': 'WORD',  #FIXME: IESDP has DWORD here
+                'off': 0x0020,
+                'label': 'Steal failure chance' },
+
+            { 'key': 'capacity',
+                'type': 'WORD',  #FIXME: IESDP has DWORD here
+                'off': 0x0022,
+                'label': 'Capacity' },
+
+            { 'key': 'unknown_24',
+                'type': 'BYTES',
+                'off': 0x0024,
+                'size': 8,
+                'label': 'Unknown 24' },
+
+            { 'key': 'bought_item_off',
+                'type': 'DWORD',
+                'off': 0x002C,
+                'label': 'Offset of items bought here' },
+
+            { 'key': 'bought_item_cnt',
+                'type': 'DWORD',
+                'off': 0x0030,
+                'label': 'Count of items bought here' },
+  
+              { 'key': 'sold_item_off',
+                'type': 'DWORD',
+                'off': 0x0034,
+                'label': 'Offset of items for sale' },
+  
+            { 'key': 'sold_item_cnt',
+                'type': 'DWORD',
+                'off': 0x0038,
+                'label': 'Count of items for sale' },
+  
+            { 'key': 'lore',
+                'type': 'DWORD',
+                'off': 0x003C,
+                'label': 'Lore' },
+  
+            { 'key': 'id_price',
+                'type': 'DWORD',
+                'off': 0x0040,
+                'label': 'ID price' },
+  
+            { 'key': 'rumours_tavern',
+                'type': 'RESREF',
+                'off': 0x0044,
+                'label': 'Rumours (tavern)' },
+  
+            { 'key': 'drink_off',
+                'type': 'DWORD',
+                'off': 0x004C,
+                'label': 'Offset to drinks' },
+  
+            { 'key': 'drink_cnt',
+                'type': 'DWORD',
+                'off': 0x0050,
+                'label': 'Count of drinks' },
+  
+            { 'key': 'rumours_temple',
+                'type': 'RESREF',
+                'off': 0x0054,
+                'label': 'Rumours (temple)' },
+  
+            { 'key': 'room_flags',
+                'type': 'DWORD',
+                'off': 0x005C,
+                'mask': { 0x01: 'Peasant', 0x02: 'Merchant', 0x04: 'Noble', 0x08: 'Royal' },
+                'label': 'Room type flags' },
+  
+            { 'key': 'price_peasant_room',
+                'type': 'DWORD',
+                'off': 0x0060,
+                'label': 'Price of a peasant room' },
+  
+            { 'key': 'price_merchant_room',
+                'type': 'DWORD',
+                'off': 0x0064,
+                'label': 'Price of a merchant room' },
+  
+            { 'key': 'price_noble_room',
+                'type': 'DWORD',
+                'off': 0x0068,
+                'label': 'Price of a noble room' },
+  
+            { 'key': 'price_royal_room',
+                'type': 'DWORD',
+                'off': 0x006C,
+                'label': 'Price of a royal room' },
+  
+            { 'key': 'cure_off',
+                'type': 'DWORD',
+                'off': 0x0070,
+                'label': 'Offset to cures' },
+  
+            { 'key': 'cure_cnt',
+                'type': 'DWORD',
+                'off': 0x0074,
+                'label': 'Count of cures' },
+  
+            { 'key': 'unknown_78',
+                'type': 'BYTES',
+                'off': 0x0078,
+                'size': 36,
+                'label': 'Unknown 78' },
             )
 
-        self.overlay_desc = (
-            { 'key': 'width',
-              'type': 'WORD',
-              'off': 0x0000,
-              'label': 'Width'},
-
-            { 'key': 'height',
-              'type': 'WORD',
-              'off': 0x0002,
-              'label': 'Height'},
-
-            { 'key': 'tileset',
-              'type': 'RESREF',
-              'off': 0x0004,
-              'label': 'Tileset'},
-
-            { 'key': 'unknown_0C',
-              'type': 'DWORD',
-              'off': 0x000C,
-              'label': 'Unknown 0C'},
-
-            { 'key': 'tilemap_off',
-              'type': 'DWORD',
-              'off': 0x0010,
-              'label': 'Offset to tilemap'},
-
-            { 'key': 'tile_index_lookup_off',
-              'type': 'DWORD',
-              'off': 0x0014,
-              'label': 'Offset to tile index lookup'},
-
-            )
+    bought_item_desc = (
+            { 'key': 'type',
+                'type': 'DWORD',
+                'off': 0x0000,
+                'enum': 'itemtypes',
+                'label': 'Type' },
+    )
+
+
+    sold_item_desc = (
+            { 'key': 'itm_resref',
+                'type': 'RESREF',
+                'off': 0x0000,
+                'label': 'ITM resref' },
+
+            { 'key': 'unknown_08',
+                'type': 'WORD',
+                'off': 0x0008,
+                'label': 'Unknown 08' },
+
+            { 'key': 'usage_1',
+                'type': 'WORD',
+                'off': 0x000A,
+                'label': 'Usage 1/Stock amount' },
+
+            { 'key': 'usage_2',
+                'type': 'WORD',
+                'off': 0x000C,
+                'label': 'Usage 2' },
+
+            { 'key': 'usage_3',
+                'type': 'WORD',
+                'off': 0x000E,
+                'label': 'Usage 3' },
+
+            { 'key': 'flags',
+                'type': 'DWORD',
+                'off': 0x0010,
+                'mask': { 0x01: 'Identified', 0x02: 'Unstealable', 0x04: 'Stolen', 0x08: 'Undroppable' },
+                'label': 'Flags' },
+
+            { 'key': 'amount_in_stock',
+                'type': 'DWORD',
+                'off': 0x0014,
+                'label': 'Amount in stock' },
+
+            { 'key': 'infinite_supply_flag',
+                'type': 'DWORD',
+                'off': 0x0018,
+                'label': 'Infinite supply flag (1=infinite stock)' },
+
+            { 'key': 'trigger_strref',
+                'type': 'STRREF',
+                'off': 0x001C,
+                'label': 'Trigger strref' },
+
+            { 'key': 'unknown_20',
+                'type': 'BYTES',
+                'off': 0x0020,
+                'size': 56,
+                'label': 'Unknown 20' },
+    )
+
+
+    drink_desc = (
+            { 'key': 'rumour_resref',
+                'type': 'RESREF',
+                'off': 0x0000,
+                'label': 'Rumour resref' },
+
+            { 'key': 'name',
+                'type': 'STRREF',
+                'off': 0x0008,
+                'label': 'Drink name' },
+
+            { 'key': 'price',
+                'type': 'DWORD',
+                'off': 0x000C,
+                'label': 'Drink price' },
+
+            { 'key': 'strength',
+                'type': 'DWORD',
+                'off': 0x0010,
+                'label': 'Alcoholic strength' },
+    )
+
+
+    cure_desc = (
+            { 'key': 'spl_resref',
+                'type': 'RESREF',
+                'off': 0x0000,
+                'label': 'SPL resref' },
+
+            { 'key': 'price',
+                'type': 'DWORD',
+                'off': 0x0008,
+                'label': 'Price of this cure' },
+    )
+
+
+    def __init__ (self):
+        Format.__init__ (self)
+        self.expect_signature = 'STOR'
+
+        self.bought_item_list = []
+        self.sold_item_list = []
+        self.drink_list = []
+        self.cure_list = []
+
+
+    def read (self, stream):
+        self.read_header (stream)
         
-        self.secondary_header_desc = (
-            { 'key': 'polygon_cnt',
-              'type': 'DWORD',
-              'off': 0x0000,
-              'label': '# of polygons'},
-
-            { 'key': 'polygon_off',
-              'type': 'DWORD',
-              'off': 0x0004,
-              'label': 'Polygons offset'},
-
-            { 'key': 'vertex_off',
-              'type': 'DWORD',
-              'off': 0x0008,
-              'label': 'Vertices offset'},
-
-            { 'key': 'wallgrp_off',
-              'type': 'DWORD',
-              'off': 0x000C,
-              'label': 'Wall groups offset'},
-
-            { 'key': 'polygon_lut_off',
-              'type': 'DWORD',
-              'off': 0x0010,
-              'label': 'Polygon indices LUT offset'},
-
-            )
-
-        self.door_desc = (
-            { 'key': 'door_name',
-              'type': 'STR8',
-              'off': 0x0000,
-              'label': 'Door name'},
-
-            { 'key': 'unknown_08',
-              'type': 'WORD',
-              'off': 0x0008,
-              'label': 'Unknown 08'},
-
-            { 'key': 'door_tile_cell_ndx',
-              'type': 'WORD',
-              'off': 0x000A,
-              'label': 'First door tile cell index'},
-
-            { 'key': 'door_tile_cell_cnt',
-              'type': 'WORD',
-              'off': 0x000C,
-              'label': 'Door tile cell count'},
-
-            { 'key': 'open_door_poly_cnt',
-              'type': 'WORD',
-              'off': 0x000E,
-              'label': '# of open door polygons'},
-
-            { 'key': 'closed_door_poly_cnt',
-              'type': 'WORD',
-              'off': 0x0010,
-              'label': '# of closed door polygons'},
-
-            { 'key': 'open_door_poly_off',
-              'type': 'DWORD',
-              'off': 0x0012,
-              'label': 'Offset of open door polygons'},
-
-            { 'key': 'closed_door_poly_off',
-              'type': 'DWORD',
-              'off': 0x0016,
-              'label': 'Offset of closed door polygons'},
-
-            )
-
-        self.tilemap_desc = (
-            { 'key': 'tile_index_lut_ndx',
-              'type': 'WORD',
-              'off': 0x0000,
-              'label': 'Primary tile index LUT start index'},
-
-            { 'key': 'tile_index_lut_cnt',
-              'type': 'WORD',
-              'off': 0x0002,
-              'label': '# of tiles in primary tile index LUT'},
-
-            { 'key': 'secondary_tis_index',
-              'type': 'WORD',
-              'off': 0x0004,
-              'label': 'Index from TIS (secondary)'},
-
-            { 'key': 'overlay_mask',
-              'type': 'BYTE',
-              'off': 0x0006,
-              'label': 'Mask of drawn overlays'},
-
-            { 'key': 'unknown_07',
-              'type': 'BYTES',
-              'off': 0x0007,
-              'size': 3,
-              'label': 'Unknown 07'},
-
-            )
-
-        # Door tile cells desc
-
-        # Tile index lookup table desc
-
-        self.wallgroup_desc = (
-            { 'key': 'polygon_ndx',
-              'type': 'WORD',
-              'off': 0x0000,
-              'label': 'Start polygon index'},
-
-            { 'key': 'polygon_cnt',
-              'type': 'WORD',
-              'off': 0x0002,
-              'label': '# of Polygon'},
-
-            )
-
-        self.polygon_desc = (
-            { 'key': 'vertex_ndx',
-              'type': 'DWORD',
-              'off': 0x0000,
-              'label': 'Start vertex index'},
-
-            { 'key': 'vertex_cnt',
-              'type': 'DWORD',
-              'off': 0x0004,
-              'label': '# of vertices'},
-
-            { 'key': 'flags',
-              'type': 'BYTE',
-              'off': 0x0008,
-              'mask': {
-                  0x01: 'shade wall',
-                  0x02: 'hovering',
-                  0x04: 'cover anims',
-                  0x08: 'cover anims 2',
-                  0x10: 'unknown 4',
-                  0x20: 'unknown 5',
-                  0x40: 'unknown 6',
-                  0x80: 'door?',
-                  },
-              'label': 'Passability flags'},
-
-            { 'key': 'unknown_09',
-              'type': 'BYTE',
-              'off': 0x0009,
-              'label': 'Unknown 09'},
-
-            { 'key': 'bbox_x1',
-              'type': 'WORD',
-              'off': 0x000A,
-              'label': 'Bounding box X min'},
-
-            { 'key': 'bbox_x2',
-              'type': 'WORD',
-              'off': 0x000C,
-              'label': 'Bounding box X max'},
-
-            { 'key': 'bbox_y1',
-              'type': 'WORD',
-              'off': 0x000E,
-              'label': 'Bounding box Y min'},
-
-            { 'key': 'bbox_y2',
-              'type': 'WORD',
-              'off': 0x0010,
-              'label': 'Bounding box Y max'},
-            
-            )
-
-        # Polygon index LUT desc
-
-        self.vertex_desc = (
-            { 'key': 'x',
-              'type': 'WORD',
-              'off': 0x0000,
-              'label': 'X'},
-
-            { 'key': 'y',
-              'type': 'WORD',
-              'off': 0x0002,
-              'label': 'Y'},
-            
-            )
-
-    def decode_file (self):
-        self.decode_header ()
-        self.decode_secondary_header (self.header['secondary_header_off'], self.secondary_header)
-
-        off = self.header['overlay_off']
-        for i in range (self.header['overlay_cnt']):
-            obj = {}
-            self.decode_overlay (off, obj)
-            self.overlay_list.append (obj)
-            off = off + 24
-
-        off = self.header['door_off']
-        for i in range (self.header['door_cnt']):
-            obj = {}
-            self.decode_door (off, obj)
-            self.door_list.append (obj)
-            off = off + 26
-
-
-
-    def print_file (self):
+        self.read_list (stream, 'bought_item')
+        self.read_list (stream, 'sold_item')
+        self.read_list (stream, 'drink')
+        self.read_list (stream, 'cure')
+
+    def write (self, stream):
+        off = self.get_struc_size (self.header_desc)
+        off = self.write_list (stream, off, 'sold_item')
+        off = self.write_list (stream, off, 'drink')
+        off = self.write_list (stream, off, 'cure')
+        off = self.write_list (stream, off, 'bought_item')
+
+        self.write_header (stream)
+
+
+    def printme (self):
         self.print_header ()
-        self.print_secondary_header ()
-
-        i = 0
-        for obj in self.overlay_list:
-            print 'Overlay #%d' %i
-            self.print_overlay (obj)
-            i = i + 1
-            
-        i = 0
-        for obj in self.door_list:
-            print 'Door #%d' %i
-            self.print_door (obj)
-            i = i + 1
-            
-
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
+
+        self.print_list ('bought_item')
+        self.print_list ('sold_item')
+        self.print_list ('drink')
+        self.print_list ('cure')
+
+
         
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
-
-        
-    def decode_secondary_header (self, offset, obj):
-        self.decode_by_desc (offset, self.secondary_header_desc, obj)
-        
-    def print_secondary_header (self):
-        self.print_by_desc (self.secondary_header, self.secondary_header_desc)
-        
-
-    def decode_overlay (self, offset, obj):
-        self.decode_by_desc (offset, self.overlay_desc, obj)
-        
-    def print_overlay (self, obj):
-        self.print_by_desc (obj, self.overlay_desc)
-
-        
-    def decode_door (self, offset, obj):
-        self.decode_by_desc (offset, self.door_desc, obj)
-
-        obj['open_door_poly_list'] = []
-        off = obj['open_door_poly_off']
-        for i in range (obj['open_door_poly_cnt']):
-            obj2 = {}
-            self.decode_polygon (off, obj2)
-            obj['open_door_poly_list'].append (obj2)
-            off = off + 34
-
-        obj['closed_door_poly_list'] = []
-        off = obj['closed_door_poly_off']
-        for i in range (obj['closed_door_poly_cnt']):
-            obj2 = {}
-            self.decode_polygon (off, obj2)
-            obj['closed_door_poly_list'].append (obj2)
-            off = off + 34
-
-        
-    def print_door (self, obj):
-        self.print_by_desc (obj, self.door_desc)
-
-        i = 0
-        for obj2 in obj['open_door_poly_list']:
-            print 'Open Poly #%d' %i
-            self.print_polygon (obj2)
-            i = i + 1
-
-        i = 0
-        for obj2 in obj['closed_door_poly_list']:
-            print 'Closed Poly #%d' %i
-            self.print_polygon (obj2)
-            i = i + 1
-
-        
-    def decode_tilemap (self, offset, obj):
-        self.decode_by_desc (offset, self.tilemap_desc, obj)
-        
-    def print_tilemap (self, obj):
-        self.print_by_desc (obj, self.tilemap_desc)
-
-        
-    def decode_wallgroup (self, offset, obj):
-        self.decode_by_desc (offset, self.wallgroup_desc, obj)
-        
-    def print_wallgroup (self, obj):
-        self.print_by_desc (obj, self.wallgroup_desc)
-
-
-    def decode_polygon (self, offset, obj):
-        self.decode_by_desc (offset, self.polygon_desc, obj)
-        
-    def print_polygon (self, obj):
-        self.print_by_desc (obj, self.polygon_desc)
-
-        
-    def decode_vertex (self, offset, obj):
-        self.decode_by_desc (offset, self.vertex_desc, obj)
-        
-    def print_vertex (self, obj):
-        self.print_by_desc (obj, self.vertex_desc)
-
-        
-        
-
-register_format ('WED', 'V1.3', WED_Format)
+register_format ('STOR', 'V1.1', STOR_Format)
ie_shell/formats/wfx.py to infinity/defaults.py
--- a/ie_shell/formats/wfx.py
+++ b/infinity/defaults.py
@@ -1,6 +1,6 @@
 # -*-python-*-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -16,66 +16,39 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: wfx.py,v 1.1 2006/07/08 14:29:27 edheldil Exp $
 
-from ie_shell.formats.format import Format, register_format, core
+# FIXME: add descriptions to the options
 
-class WFX_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'WFX'
+options = {
+    'core.chitin_file': ['CHITIN.KEY', ""], 
+    'core.dialog_file': ['dialog.tlk', ""],  
+    'pager': ['more', "Program to use for paging command output"],  
+ 
+    'stream.debug_coverage': [False, "On stream close print info on offsets not read or read more than once"], 
+    'format.debug_read': [False, "Print each read op to stdout"], 
+    'format.debug_write': [False, "Print each write op to stdout"], 
 
+    'format.bam.force_rle': [True,  "Assume that frame data is always RLE encoded"], 
+    'format.bam.decode_frame_data': [True, "Decode BAM frame data"], 
+    'format.bam.print_frame_bitmap': [True, "Print BAM frame data"], 
+    'format.bam.print_palette': [True, "Print BAM frame palette" ], 
 
-        self.header_desc = (
-            { 'key': 'signature',
-              'type': 'STR4',
-              'off': 0x0000,
-              'label': 'Signature' },
-            
-            { 'key': 'version',
-              'type': 'STR4',
-              'off':0x0004,
-              'label': 'Version'},
-            
-            { 'key': 'unknown_08',
-              'type': 'DWORD',
-              'off': 0x0008,
-              'label': 'Unknown 08' },
+    'format.biff.read_data': [False,  "When reading BIFF file read its data too"], 
 
-            { 'key': 'flags',
-              'type': 'DWORD',
-              'off': 0x000C,
-              'mask': { 0x1: 'unknown 0', 0x2: 'unknown 1', 0x4: 'enabled' },
-              'label': 'Flags' },
+    'format.key.tick_size': [ 100, "# of RESREFs read to print a dot" ], 
+    'format.key.tack_size': [ 5000, "# of RESREFs read to print a number" ], 
+    'format.key.max_read_resrefs': [None,  "Max # of RESREFs to read from KEY file"], 
 
+    'format.mos.print_tiles': [True,  "Print MOS tiles"], 
+    'format.mos.print_palettes': [False,  "Print MOS palettes"], 
 
-            { 'key': 'deviation',
-              'type': 'DWORD',
-              'off': 0x0010,
-              'label': 'Deviation'},
+    'format.tis.print_tiles': [False,  "Print TIS tiles"], 
+    'format.tis.print_palettes': [False,  "Print TIS palettes"], 
+    
+    'format.tlk.tick_size': [ 100, "# of STRREFs read to print a dot" ], 
+    'format.tlk.tack_size': [ 5000, "# of STRREFs read to print a number" ], 
+    'format.tlk.decode_strrefs': [True,  "Read TLK strrefs, not only header"], 
 
-            { 'key': 'unknown 14',
-              'type': 'BYTES',
-              'off': 0x0014,
-              'size': 244,
-              'label': 'Unknown 14'},
+}
 
-            )
-
-
-    def decode_file (self):
-        self.decode_header ()
-
-    def print_file (self):
-        self.print_header ()
-
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
-        
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
-        
-
-        
-register_format ('WFX', 'V1.0', WFX_Format)
+# End of file defaults.py
ie_shell/formats/wmap.py to infinity/stream.py
--- a/ie_shell/formats/wmap.py
+++ b/infinity/stream.py
@@ -1,7 +1,6 @@
 # -*-python-*-
-
 # ie_shell.py - Simple shell for Infinity Engine-based game files
-# Copyright (C) 2004 by Jaroslav Benkovsky, <edheldil@users.sf.net>
+# Copyright (C) 2004-2008 by Jaroslav Benkovsky, <edheldil@users.sf.net>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -17,351 +16,441 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# RCS: $Id: wmap.py,v 1.1 2005/03/02 20:44:23 edheldil Exp $
-
-from ie_shell.formats.format import Format, register_format
-
-class WMAP_Format (Format):
-    def __init__ (self, filename):
-        Format.__init__ (self, filename)
-        self.expect_signature = 'WMAP'
-
-        self.wmap_list = []
-
-        # FIXME: maybe they should be local to wmap entries
-        self.area_list = []
-        self.arealink_list = []
-
-
-        self.header_desc = (
-            { 'key': 'signature',
-              'type': 'STR4',
-              'off': 0x0000,
-              'label': 'Signature' },
-            
-            { 'key': 'version',
-              'type': 'STR4',
-              'off':0x0004,
-              'label': 'Version'},
-            
-            { 'key': 'num_of_wmaps',
-              'type': 'DWORD',
-              'off': 0x0008,
-              'label': '# of wmap entries'},
-            
-            { 'key': 'wmap_offset',
-              'type': 'DWORD',
-              'off': 0x000C,
-              'label': 'First wmap entry offset'},
-            )
-        
-
-        self.wmap_record_desc = (
-            { 'key': 'map_resref',
-              'type': 'RESREF',
-              'off': 0x0000,
-              'label': 'Map resource name' },
-            
-            { 'key': 'width',
-              'type': 'DWORD',
-              'off': 0x0008,
-              'label': 'Width' },
-            
-            { 'key': 'height',
-              'type': 'DWORD',
-              'off': 0x000C,
-              'label': 'Height' },
-            
-            { 'key': 'map_num',
-              'type': 'DWORD',
-              'off': 0x0010,
-              'label': 'Map number' },
-            
-            { 'key': 'area_name',
-              'type': 'DWORD',
-              'off': 0x0014,
-              'label': 'Area name' },
-            
-            { 'key': 'unknown',
-              'type': 'DWORD',
-              'off': 0x0018,
-              'label': '???' },
-
-            { 'key': 'unknown',
-              'type': 'DWORD',
-              'off': 0x001C,
-              'label': '???' },
-
-            { 'key': 'num_of_areas',
-              'type': 'DWORD',
-              'off': 0x0020,
-              'label': 'Num of area entries' },
-
-            { 'key': 'area_offset',
-              'type': 'DWORD',
-              'off': 0x0024,
-              'label': 'Offset of first area entry' },
-
-            { 'key': 'arealink_offset',
-              'type': 'DWORD',
-              'off': 0x0028,
-              'label': 'Offset of first arealink entry' },
-
-            { 'key': 'num_of_arealinks',
-              'type': 'DWORD',
-              'off': 0x002C,
-              'label': 'Num of arealink entries' },
-
-            { 'key': 'map_icon_resref',
-              'type': 'RESREF',
-              'off': 0x0030,
-              'label': 'Map icons resource name' },
-            
-            )
-
-        self.area_record_desc = (
-            { 'key': 'area_name',
-              'type': 'RESREF',
-              'off': 0x0000,
-              'label': 'Area name' },
-            
-            { 'key': 'area_resref',
-              'type': 'RESREF',
-              'off': 0x0008,
-              'label': 'Area resref' },
-            
-            { 'key': 'area_long_name',
-              'type': 'STR32',
-              'off': 0x0010,
-              'label': 'Area long name' },
-            
-            { 'key': 'area_status',
-              'type': 'DWORD',
-              'off': 0x0030,
-              'label': 'Area status flags' },
-            
-            { 'key': 'icon_seq_ndx',
-              'type': 'DWORD',
-              'off': 0x0034,
-              'label': 'Icon sequence index' },
-            
-            { 'key': 'xpos',
-              'type': 'DWORD',
-              'off': 0x0038,
-              'label': 'X coordinate on world map' },
-            
-            { 'key': 'ypos',
-              'type': 'DWORD',
-              'off': 0x003C,
-              'label': 'Y coordinate on world map' },
-
-            { 'key': 'area_name_caption',
-              'type': 'STRREF',
-              'off': 0x0040,
-              'label': 'Area title for captions' },
-            
-            { 'key': 'area_name_tooltip',
-              'type': 'STRREF',
-              'off': 0x0044,
-              'label': 'Area title for tooltips' },
-            
-            { 'key': 'loading_screen',
-              'type': 'RESREF',
-              'off': 0x0048,
-              'label': 'Loading screen MOS file' },
-            
-
-            { 'key': 'arealink_ndx_north',
-              'type': 'DWORD',
-              'off': 0x0050,
-              'label': 'Index of 1st arealink north' },
-
-            { 'key': 'arealinks_cnt_north',
-              'type': 'DWORD',
-              'off': 0x0054,
-              'label': '# of arealinks north' },
-
-
-            { 'key': 'arealink_ndx_east',
-              'type': 'DWORD',
-              'off': 0x0058,
-              'label': 'Index of 1st arealink east' },
-
-            { 'key': 'arealinks_cnt_east',
-              'type': 'DWORD',
-              'off': 0x005C,
-              'label': '# of arealinks east' },
-
-
-            { 'key': 'arealink_ndx_south',
-              'type': 'DWORD',
-              'off': 0x0060,
-              'label': 'Index of 1st arealink south' },
-
-            { 'key': 'arealinks_cnt_south',
-              'type': 'DWORD',
-              'off': 0x0064,
-              'label': '# of arealinks south' },
-
-
-            { 'key': 'arealink_ndx_west',
-              'type': 'DWORD',
-              'off': 0x0068,
-              'label': 'Index of 1st arealink west' },
-
-            { 'key': 'arealinks_cnt_west',
-              'type': 'DWORD',
-              'off': 0x006C,
-              'label': '# of arealinks west' },
-
-            )
-
-        self.arealink_record_desc = (
-            { 'key': 'dest_map_ndx',
-              'type': 'DWORD',
-              'off': 0x0000,
-              'label': 'Destination map index' },
-
-            { 'key': 'dest_map_entry_point',
-              'type': 'STR32',
-              'off': 0x0004,
-              'label': 'Destination map entry_point name' },
-
-            { 'key': 'distance_scale',
-              'type': 'DWORD',
-              'off': 0x0024,
-              'label': 'Distance scale?' },
-
-            { 'key': 'flags',
-              'type': 'DWORD',
-              'off': 0x0028,
-              'label': 'Flags' },
-
-            { 'key': 'encounter_area0',
-              'type': 'RESREF',
-              'off': 0x002C,
-              'label': 'Area of random encounter 0' },
-
-            { 'key': 'encounter_area1',
-              'type': 'RESREF',
-              'off': 0x0034,
-              'label': 'Area of random encounter 1' },
-
-            { 'key': 'encounter_area2',
-              'type': 'RESREF',
-              'off': 0x003C,
-              'label': 'Area of random encounter 2' },
-
-            { 'key': 'encounter_area3',
-              'type': 'RESREF',
-              'off': 0x0044,
-              'label': 'Area of random encounter 3' },
-
-            { 'key': 'encounter_area4',
-              'type': 'RESREF',
-              'off': 0x004C,
-              'label': 'Area of random encounter 4' },
-
-            { 'key': 'encounter_chance',
-              'type': 'DWORD',
-              'off': 0x0050,
-              'label': 'Random encounter chance' },
-
-            )
-
-
-    def decode_file (self):
-        self.decode_header ()
-
-        off = self.header['wmap_offset']
-        for i in range (self.header['num_of_wmaps']):
-            obj = {}
-            self.decode_wmap_record (off, obj)
-            self.wmap_list.append (obj)
-            off = off + 184
-
-        for wmap in self.wmap_list:
-            off = wmap['area_offset']
-            for i in range (wmap['num_of_areas']):
-                obj = {}
-                self.decode_area_record (off, obj)
-                self.area_list.append (obj)
-                off = off + 240
-
-            off = wmap['arealink_offset']
-            for i in range (wmap['num_of_arealinks']):
-                obj = {}
-                self.decode_arealink_record (off, obj)
-                self.arealink_list.append (obj)
-                off = off + 168
-
-    def print_file (self):
-        self.print_header ()
-
-        i = 0
-        for obj in self.wmap_list:
-            print '#%d' %i
-            self.print_wmap_record (obj)
-            i = i + 1
-
-        i = 0
-        for obj in self.arealink_list:
-            print '#%d' %i
-            self.print_arealink_record (obj)
-            i = i + 1
-            
-        i = 0
-        for obj in self.area_list:
-            print '#%d' %i
-            self.print_area_record (obj)
-            i = i + 1
-            
-        return
+
+"""
+Implement *Stream, classes for reading  IE files.
+
+Implement *Stream classes for reading IE data primitives
+from normal files, memory buffers and from files in IE specific 
+`filesystem' addressed by RESREFs.
+
+Classes:
+  Stream - base class implementing reading of IE primitives
+  FileStream - read from files contained in filesystem
+  MemoryStream - read from memory buffers (strings)
+  ResourceStream - read IE object referenced by RESREF
+  CompressedStream - read compressed buffer
+"""
+
+
+import gzip
+import os.path
+import re
+import string
+import struct
+
+from infinity import core
+
+
+class Stream (object):
+    """
+    Base abstract class for reading and writing IE files. 
     
-        for i in range (self.header['num_of_res']):
-            self.decode_res_record (off, obj)
-            off = off + 14
-            print '#%d' %i
-            self.print_res_record (obj)
-
-
-    def decode_header (self):
-        self.header = {}
-        self.decode_by_desc (0x0000, self.header_desc, self.header)
-        
-    def print_header (self):
-        self.print_by_desc (self.header, self.header_desc)
-        
-
-    def decode_wmap_record (self, offset, obj):
-        self.decode_by_desc (offset, self.wmap_record_desc, obj)
-        
-    def print_wmap_record (self, obj):
-        self.print_by_desc (obj, self.wmap_record_desc)
-
-
-    def decode_area_record (self, offset, obj):
-        self.decode_by_desc (offset, self.area_record_desc, obj)
-        
-    def print_area_record (self, obj):
-        self.print_by_desc (obj, self.area_record_desc)
-
-
-    def decode_arealink_record (self, offset, obj):
-        self.decode_by_desc (offset, self.arealink_record_desc, obj)
-        
-    def print_arealink_record (self, obj):
-        self.print_by_desc (obj, self.arealink_record_desc)
-
-
-
-    def get_file_res_data (self, obj):
-        obj['data'] = self.decode_blob (obj['data_offset'], obj['data_size'])
-
-    def save_file_res (self, filename, obj):
-        self.get_file_res_data (obj)
-        fh = open (filename, 'w')
-        fh.write (obj['data'])
-        fh.close ()
-        
-register_format ('WMAP', 'V1.0', WMAP_Format)
+    It implements the basic open / read / write / seek / close
+    API to be implemented in the subclasses, as well as
+    functions for reading primitive IE data types like RESREF or WORD.
+    """
+    
+    def __init__ (self):
+        self.is_open = False
+        self.options = {}
+
+
+    def __del__ (self):
+        """Destructor"""
+        # FIXME: possibly close() only when autoclose flag is set
+        self.close ()
+
+
+    def open (self, name,  mode = 'r'):
+        # do the real open here
+        self.is_open = True
+        self.name = name
+        self.coverage = []
+        return self
+
+
+    def close (self):
+        if not self.is_open:
+            return
+            
+        if self.get_option ('stream.debug_coverage'):
+            self.print_coverage ()
+        self.is_open = False
+
+
+    def seek (self, offset):
+        """Set position for next read (current offset) to `offset'.
+        Override in subclasses."""
+        pass
+
+    def read (self, count = None):
+        """Read `count' of bytes at the current offset and update the offset.
+        Override in subclasses."""
+        pass
+
+    def write (self, data, count = None):
+        """Write `data' to current offset in file.
+       Override in subclasses."""
+        pass
+
+    # Methods for reading and writing primitive IE data types
+
+    def get_char (self, offset = None):
+        # offset == None means "current offset" here
+        if offset is not None:
+            self.seek (offset)
+            
+        return self.read (1)
+        #v = self.read (1)
+        #return struct.unpack ('c', v)[0]
+
+    def put_char (self, char, offset = None):
+        # offset == None means "current offset" here
+        if offset is not None:
+            self.seek (offset)
+            
+        self.write (char, 1)
+        
+    def get_line (self):
+        #return self.readline ()
+        return self.read_line_string ()
+
+    def put_line (self):
+        #FIXME
+        pass
+
+    def read_word (self, offset):
+        # offset == None means "current offset" here
+        if offset is not None:
+            self.seek (offset)
+        v = self.read (2)
+        return struct.unpack ('H', v)[0]
+
+    def write_word (self, value, offset = None):
+        # offset == None means "current offset" here
+        if offset is not None:
+            self.seek (offset)
+        bytes = struct.pack ('H', value)
+        self.write (bytes)
+
+    def read_dword (self, offset):
+        # offset == None means "current offset" here
+        if offset is not None:
+            self.seek (offset)
+        v = self.read (4)
+        return struct.unpack ('I', v)[0]
+
+    def write_dword (self, value, offset = None):
+        # offset == None means "current offset" here
+        if offset is not None:
+            self.seek (offset)
+        bytes = struct.pack ('I', value)
+        self.write (bytes)
+
+    def read_sized_string (self, offset, size):
+        # offset == None means "current offset" here
+        if offset is not None:
+            self.seek (offset)
+        v = self.read (size)
+        # FIXME: remove the trailing zeros
+        
+        return struct.unpack ('%ds' %size, v)[0]
+
+    def write_sized_string (self, value, offset, size):
+        # offset == None means "current offset" here
+        if offset is not None:
+            self.seek (offset)
+        
+        # FIXME: pad with zeros
+        bytes = struct.pack ('%ds' %size, value)
+        self.write (bytes)
+
+    def read_asciiz_string (self, off):
+        s = ''
+        
+        while 1:
+            c = self.get_char (off)
+            if c == '\0': break
+            s = s + c
+            off = off + 1
+            
+        return s
+
+    def read_line_string (self):
+        s = ''
+        
+        while 1:
+            c = self.get_char (None)
+            if c == '\n': break
+            if c == '':
+                if s == '':
+                    s = None
+                break
+            
+            s = s + c
+            
+        return s
+
+    # FIXME: (read|write)_resref should not be here
+    def read_resref (self, off):
+        return self.read_sized_string (off, 8)
+
+    def write_resref (self, value, off):
+        self.write_sized_string (value, off, 8)
+
+    def read_blob (self, offset, size = None):
+        # offset == None means "current offset" here
+        # size == None means "till the end of stream"
+        if offset is not None:
+            self.seek (offset)
+
+        return self.read (size)
+        
+    def write_blob (self, value, offset, size = None):
+        # offset == None means "current offset" here
+        # size == None means "till the end of stream"
+        if offset is not None:
+            self.seek (offset)
+
+        return self.write (value)
+        
+
+    # Helper methods
+
+    def get_signature (self):
+        was_open = self.is_open
+        #if not self.is_open:
+        #    self.open ()
+
+        s = self.read_sized_string (0x0000, 8)
+
+        #if not was_open:
+        #    self.close ()
+        #else:
+        #    self.seek (0)
+
+
+        if re.match ("[0-9]{1,4}[\r\n ]", s) or re.match ("0[xX][0-9A-Fa-f]{1,4} ", s) or re.match ("-1[\r\n]", s):
+            signature = "IDS"
+            version = ""
+        elif s == "SC\nCR\nCO" or s == "SC\r\nCR\r\n":
+            signature = "BCS"
+            version = ""
+        else:
+            signature = s[0:4].strip ()
+            version = s[4:8].strip ()
+
+        return (signature, version)
+
+    def get_format (self, type = 0):
+        signature, version = self.get_signature ()
+        fmt = core.get_format (signature, version)
+        
+        if fmt is None and type != 0:
+            fmt = core.get_format_by_type (type)
+
+        return fmt
+
+
+    def load_object (self, type = 0):
+        fmt = self.get_format ()
+        obj = fmt ()
+        self.seek (0)
+        obj.read (self)
+        return obj
+
+
+    def coverage_add (self, offset, size, some_info_tbd):
+        d = offset +size - len (self.coverage)
+        if d > 0:
+            self.coverage.extend ([0] * d)
+        for i in range (offset,  offset + size):
+            self.coverage[i] += 1
+
+
+    def print_coverage (self):
+        def print_single (offset,  value):
+            print "  0x%04x: %d" %(offset,  value)
+
+        def print_range (offset1,  offset2,  value):
+            if offset2 == offset1:
+                print_single (offset1,  value)
+            else:
+                print "  0x%04x - 0x%04x: %d" %(offset1,  offset2,  value)
+
+        print "Coverage (%s):" %self.name
+        from_offset = None
+        
+        for i in range (len (self.coverage)):
+            if self.coverage[i] == 0 and from_offset is None:
+                from_offset = i
+            elif self.coverage[i] == 0:
+                pass
+            elif self.coverage[i] > 1:
+                if from_offset is not None:
+                    print_range (from_offset,  i - 1,  0)
+                    from_offset = None
+                print_single (i,  self.coverage[i])
+            else:
+                if from_offset is not None:
+                    print_range (from_offset,  i - 1,  0)
+                    from_offset = None
+                    
+        if from_offset is not None:
+            print_range (from_offset,  i - 1,  0)
+            from_offset = None
+
+
+    def get_option (self, key):
+        if self.options.has_key (key):
+            return self.options[key]
+        else:
+            return core.get_option (key)
+
+
+    def set_option (self, key, value):
+        self.options[key] = value
+
+
+
+
+class FileStream (Stream):
+    """Specialized Stream for working with normal files in filesystem"""
+
+    def __init__ (self):
+        Stream.__init__ (self)
+
+    def open (self, filename, mode = 'r'):
+        # FIXME: reset offset?
+        Stream.open (self, filename,  mode)
+        self.is_open = False
+        self.filename = filename
+        self.fh = open (self.filename, mode)
+        self.is_open = True
+        
+        if self.get_option ('stream.debug_coverage'):
+            size = os.stat (filename)[6]
+            self.coverage = [0] * size
+            
+        return self
+
+    def close (self):
+        if not self.is_open:
+            return
+            
+        self.fh.close ()
+        Stream.close (self)
+
+    def seek (self, offset):
+        self.fh.seek (offset)
+
+    def read (self, size = None):
+        # FIXME: do some caching/buffering here?
+
+        #if size == None:
+        #   raise RuntimeError ()
+
+        if self.get_option ('stream.debug_coverage'):
+            off = self.fh.tell ()
+
+        #if size != None:
+        value = self.fh.read (size)
+        #else:
+        #   value = self.fh.read ()
+
+        if self.get_option ('stream.debug_coverage'):
+            #sz = len (value)
+            self.coverage_add (off,  size,  None)
+        return value
+
+
+    def write (self, bytes, size = None):
+        #if size != None:
+        #    self.fh.write (bytes, size)
+        #else:
+        self.fh.write (bytes)
+
+    def __repr__ (self):
+        return "<FileStream: %s>" %self.filename
+
+
+class MemoryStream (Stream):
+    """Stream for working with memory buffers instead of files."""
+    def __init__ (self):
+        Stream.__init__ (self)
+
+    def open (self, membuffer,  name = '<memory stream>'):
+        Stream.open (self, name,  '')
+        self.buffer = membuffer
+        self.offset = 0
+        return self
+
+    def decrypt (self):
+        for i in range (len (self.buffer)):
+            print chr (ord (self.buffer[i]) ^ ord (core.xor_key[i]))
+
+    def seek (self, offset):
+        self.offset = offset
+
+    def read (self, count = None):
+        if count is not None:
+            data = self.buffer[self.offset:self.offset+count]
+        else:
+            data = self.buffer[self.offset:]
+            
+        self.offset = self.offset + len (data)
+        return data
+
+
+
+class ResourceStream (MemoryStream):
+    """Stream for reading RESREFs (files in the IE data `filesystem').
+    It requires that the KEY file is loaded in core.keys."""
+
+    def __init__ (self):
+        MemoryStream.__init__ (self)
+
+    def open (self, name, type = None,  index = 0):
+        self.resref = name
+        self.type = type
+
+        if core.keys is None:
+            raise RuntimeError, "Core game files are not loaded. See load_game ()."
+
+        oo = core.keys.get_resref_by_name (self.resref)
+        if self.type is not None:
+            oo = filter (lambda o: o['type'] == self.type, oo)
+
+        if len (oo) > 1 and self.type is None:
+            raise RuntimeError, "More than one result"
+
+        o = oo[index]
+     
+        src_file = core.keys.bif_list[o['locator_src_ndx']]
+        bif_stream = FileStream ().open (os.path.join (core.game_dir, src_file['file_name']))
+        b = core.get_format ('BIFF') ()
+        b.read (bif_stream)
+        obj = b.file_list[o['locator_ntset_ndx']]
+        b.get_file_data (bif_stream, obj)
+
+        buffer = obj['data']
+        return MemoryStream.open (self, buffer,  name = name)
+        
+    def load_object (self):
+        return Stream.load_object (self, self.type)
+
+
+    def __repr__ (self):
+        return "<ResStream: %s>" %self.resref
+
+
+class CompressedStream (MemoryStream):
+    """Stream for reading compressed files in memory."""
+    
+    def open (self, membuffer,  name = '<compressed stream>'):
+        return MemoryStream.open (self, gzip.zlib.decompress (membuffer),  name)
+
+        
+# End of file stream.py
+
<< < 1 2 3 (Page 3 of 3)