@@ 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