Rewrote result.py to use functions, not a singleton static class contraption
1 files changed, 84 insertions(+), 77 deletions(-)

M result.py
M result.py +84 -77
@@ 1,5 1,5 @@ 
 """
-Class for parsing RST (result) files.
+Functions for reading and parsing RST (result) files.
 """
 
 import struct

          
@@ 9,82 9,89 @@ class InvalidResultFile(Exception):
     """Exception raised when the RST file is invalid or corrupt"""
     pass
 
-class ResultFile(object):
-    """Class for parsing RST (result) files"""
+def read(f, filesize):
+    """Reads from file and parses RST data"""
+    result = {}
+    pointers = get_sections(f, filesize)
+    # Read standard sections (should always exist, even if 0 items in section)
+    f.seek(pointers['ships'])
+    result['ships'] = Ship.read(f, has_count=True)
+    f.seek(pointers['planets'])
+    result['planets'] = Planet.read(f, has_count=True)
+    f.seek(pointers['bases'])
+    result['bases'] = Base.read(f, has_count=True)
+    return result
+    
+def valid_pointer(pointer, filesize, datasize):
+    """Checks if pointer seek location is valid (smaller than filesize)"""
+    if not filesize:
+        raise InvalidResultFile('RST file size zero or not defined')
+    if pointer < 0:
+        raise InvalidResultFile('Pointer should be positive integer')
+    if pointer + datasize <= filesize:
+        return True
+    return False
+    
+def get_sections(f, filesize):
+    """Get pointers for various sections of the RST file"""
+    result = {}
+    f.seek(0) # Reset position
+    (result['ships'],) = struct.unpack('< i', f.read(4))
+    (result['targets'],) = struct.unpack('< i', f.read(4))
+    (result['planets'],) = struct.unpack('< i', f.read(4))
+    (result['bases'],) = struct.unpack('< i', f.read(4))
+    (result['messages'],) = struct.unpack('< i', f.read(4))
+    (result['shipxy'],) = struct.unpack('< i', f.read(4))
+    (result['general'],) = struct.unpack('< i', f.read(4))
+    (result['vcr'],) = struct.unpack('< i', f.read(4))
+    # Subtract 1 to fix BASIC style pointer (which assumes 1 = start of file)
+    for key, pointer in result.items():
+        result[key] = int(pointer) - 1
+    # Check RST version: is WinPlan data present?
+    (result['winplan'], result['extratargets'],) = get_version(f, filesize)
+    if result['winplan']:
+        f.seek(44) # Position of LEECHx.DAT 
+        (result['leech'],) = struct.unpack('< i', f.read(4))
+        if not result['leech']:
+            result['leech'] = None
+        else:
+            result['leech'] = int(result['leech']) - 1
+        (result['ufo'],) = struct.unpack('< i', f.read(4))
+        if not result['ufo']:
+            result['ufo'] = None
+        else:
+            result['ufo'] = int(result['ufo']) - 1
+    else:
+        result['leech'] = None
+        result['ufo'] = None
+    # Check if pointers are valid
+    for pointer in result.values():
+        if pointer is not None and not valid_pointer(pointer, filesize, 2):
+            raise InvalidResultFile('One or more pointers is invalid')
+    return result
+
+def get_version(f, filesize):
+    """Analyses RST file to determine if it is DOS-style or WinPlan-style"""
     SIGNATURE = 'VER3.5'
     VERSIONS = ('00', '01',)
     TARGETSIG = ('1211', '1120',)
-    size = None
-    pointers = {}
-    data = {}
-    
-    @classmethod
-    def setsize(cls, size):
-        """Sets size of RST file, which is important for checking pointers"""
-        cls.size = size
-    
-    @classmethod
-    def validpointer(cls, pointer, datasize):
-        """Checks if pointer seek location is valid (smaller than filesize)"""
-        if cls.size is None:
-            raise ValueError('RST file size not defined')
-        if pointer < 0:
-            raise ValueError('Pointer should be positive integer')
-        if pointer + datasize <= cls.size:
-            return True
-        return False
-    
-    @classmethod
-    def read(cls, f):
-        """Reads from file and parses RST data"""
-        # Reset position
-        f.seek(0)
-        cls.getpointers(f)
-        # Read standard data
-        f.seek(cls.pointers['ships'])
-        cls.data['ships'] = Ship.read(f, has_count=True)
-        f.seek(cls.pointers['planets'])
-        cls.data['planets'] = Planet.read(f, has_count=True)
-        f.seek(cls.pointers['bases'])
-        cls.data['bases'] = Base.read(f, has_count=True)
-        # Return gathered data
-        return cls.data
-    
-    @classmethod
-    def getpointers(cls, f):
-        """Get pointers for various sections of the RST file"""
-        # Get pointers to standard data
-        (cls.pointers['ships'],) = struct.unpack('< i', f.read(4))
-        (cls.pointers['targets'],) = struct.unpack('< i', f.read(4))
-        (cls.pointers['planets'],) = struct.unpack('< i', f.read(4))
-        (cls.pointers['bases'],) = struct.unpack('< i', f.read(4))
-        (cls.pointers['messages'],) = struct.unpack('< i', f.read(4))
-        (cls.pointers['shipxy'],) = struct.unpack('< i', f.read(4))
-        (cls.pointers['general'],) = struct.unpack('< i', f.read(4))
-        (cls.pointers['vcr'],) = struct.unpack('< i', f.read(4))
-        # Subtract 1 to fix BASIC style pointer
-        for key, pointer in cls.pointers.items():
-            cls.pointers[key] = int(pointer) - 1
-        # Check if pointers are valid
-        for pointer in cls.pointers.values():
-            if not cls.validpointer(pointer, 2):
-                raise InvalidResultFile('One or more pointers is invalid')
-        
-    @classmethod
-    def getversion(cls, f):
-        """Analyses RST file to determine if it is DOS-style or WinPlan-style"""
-        # Check first signature (SIGNATURE) at location +32 of RST file
-        f.seek(32)
-        (signature, version, pointer) = struct.unpack('< 6s 2s i', f.read(12))
-        if (signature != cls.SIGNATURE) or (version not in cls.VERSIONS):
-            return False
-        cls.pointers['winplan'] = int(pointer) - 1
-        # Check if location of WinPlan data exists in file
-        if not cls.validpointer(cls.pointers['winplan'], 13281+4):
-            return False
-        # Check second signature (TARGETSIG) at location +13282 of WinPlan data
-        f.seek(cls.pointers['winplan'] + 13282)
-        (targetsig,) = struct.unpack('< 4s', f.read(4))
-        if targetsig not in cls.TARGETSIG:
-            return False
-        return True # True = WinPlan-style; False = otherwise
  No newline at end of file
+    # Check first signature (SIGNATURE) at location +32 of RST file
+    f.seek(32)
+    (signature, version, pointer) = struct.unpack('< 6s 2s i', f.read(12))
+    if (signature != SIGNATURE) or (version not in VERSIONS):
+        return None, None
+    # Fix BASIC style pointer
+    pointer = int(pointer) - 1
+    # Check if location of WinPlan data (and TARGETSIG) exists in file
+    if not valid_pointer(pointer, filesize, 13282+4):
+        return None, None
+    # Check second signature (TARGETSIG) at location +13282 of WinPlan data
+    f.seek(pointer + 13282)
+    (targetsig,) = struct.unpack('< 4s', f.read(4))
+    if targetsig not in TARGETSIG:
+        return None, None
+    # Second TARGETSIG value (1120) indicates whether extra targets follow it
+    if targetsig == TARGETSIG[1]:
+        return pointer, (pointer + 13282 + 4)
+    else:
+        return pointer, None
  No newline at end of file