@@ 33,4 33,55 @@ class Base(PlanetsData):
'build_beamcount',
'build_torptype',
'build_torpcount',
- 'unused')
No newline at end of file
+ 'unused')
+
+class Ship(PlanetsData):
+ """List of player's ships"""
+ # No FILENAME, but typically: SHIPx.DAT or SHIPx.DIS (x=player)
+ # No COUNT, but normally maximum 500 and absolute max of 999
+ PACK_LENGTH = 107
+ PACK_FORMAT = '<hh3shhhhhhhhhhhhhhhhhhh20shhhhhhhhhhhhhhhhhhhhh'
+ FIELDS = ('id',
+ 'owner',
+ 'friendlycode',
+ 'warp',
+ 'delta_x',
+ 'delta_y',
+ 'x',
+ 'y',
+ 'engine',
+ 'hull',
+ 'beamtype',
+ 'beamcount',
+ 'fighterbays',
+ 'torptype',
+ 'ordnance',
+ 'torpcount',
+ 'mission',
+ 'primary_enemy',
+ 'tow_id',
+ 'damage',
+ 'crew',
+ 'cargo_clans',
+ 'name',
+ 'cargo_fuel',
+ 'cargo_tritanium',
+ 'cargo_duranium',
+ 'cargo_molybdenum',
+ 'cargo_supplies',
+ 'unload_fuel',
+ 'unload_tritanium',
+ 'unload_duranium',
+ 'unload_molybdenum',
+ 'unload_clans',
+ 'unload_supplies',
+ 'unload_id',
+ 'transfer_fuel',
+ 'transfer_tritanium',
+ 'transfer_duranium',
+ 'transfer_molybdenum',
+ 'transfer_clans',
+ 'transfer_supplies',
+ 'transfer_id',
+ 'intercept_id',
+ 'money')
No newline at end of file
@@ 0,0 1,88 @@
+"""
+Class for parsing RST (result) files.
+"""
+
+import struct
+from player import Base, Ship
+
+class InvalidResultFile(Exception):
+ """Exception raised when the RST file is invalid or corrupt"""
+ pass
+
+class ResultFile(object):
+ """Class for parsing RST (result) files"""
+ 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['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