#!/usr/local/python22/bin/python

# it does not need python22...

#
# What is it?  It is a converter, generating an xml out of an Player<n>.rst.
# Author : Tjabo Kloppenburg <tk@bitsex.de>
# License: GNU GPL
#

# Usage: rst2xml.py Player1.rst [ > player1_rst.xml ]

# HELP-REQUEST:
#  If you know how to read a signed word out
#  of an rst file please send me a patch! -> tk@bitsex.de
#  (see line 71)

# TODO:
#  - proper encoding (what if "<" is in the rst password? -> Booom!)
#  - LeechX.dat
#  - Ufo
#  - VisualCombatRecodings
#  - WinplanData
#  - tools to generate game dat files (planets.nm) to xml
#

version = "0.3"

import sys
from math import *
import copy
import re

argc = len(sys.argv)-1
if argc == 1:
  rst_filename = sys.argv[1]
else:
  print "\n   Usage: rst2xml.py Player1.rst > player1_rst.xml\n\n"
  print 0 / 0   #  is there no exit statement? :-)

#
# default object class
#

class MyBaseClass:

  # new in rst2xml:
  def printTag( self, name, value, type="int", attrs="" ):
    if type == "int":
      type = ""
    else:
      type = " type='" + type + "'"

    if attrs != "":
      attrs = " " + attrs

    print "<" + name + type + attrs + ">" + str(value) + "</" + name + ">"

  def getByteChar(self, data, pos):
    return data[pos]

  def getByteInt(self, data, pos):
    return int(data[pos])

  def getByteOrd(self, data, pos):
    return ord(data[pos])

  def getWORD(self, data, pos):
    return ord(data[pos]) + (256 * ord(data[pos+1]))

  def getSignedWORD(self, data, pos):
    w = self.getWORD(data,pos)
    if w > 32768:
      w = 65535 - w + 1   # 999 I'ld bet this is wrong. Who can help?
      w = -w
    return w

  def getDWORD(self, data, pos):
    return long( (256*256*self.getWORD( data, pos+2)) + self.getWORD( data, pos) )

  def getFriendlyCode(self, data, pos):
    str = data[pos] + data[pos+1] + data[pos+2]
    return str

  def getString(self, data, pos, len):
    str = ""
    for i in range(len):
      str = str + data[pos+i]
    return str

#endclass

# --------------------------------------------------------------------

#
# class for ore (N,T,D,M) / german: Erze
#

class OreClass( MyBaseClass ):
  #n = long(0)
  #t = long(0)
  #d = long(0)
  #m = long(0)

  def __init__(self, n=0,t=0,d=0,m=0):
    self.n = long(n)
    self.t = long(t)
    self.d = long(d)
    self.m = long(m)

  def dump(self):
    print "N T D M: ",self.n,self.t,self.d,self.m

  def printXML(self):
    self.printTag( "n", self.n )
    self.printTag( "t", self.t )
    self.printTag( "d", self.d )
    self.printTag( "m", self.m )

  # more methods needed? will see...

#endclass
# --------------------------------------------------------------------

#
# class for cargo
#

class CargoClass(OreClass):

  #clans    = long(0)
  #supplies = long(0)
  #targetID = 0

  def __init__(self, n=0,t=0,d=0,m=0, clans=0, supplies=0, targetID=0):
    OreClass.__init__(self, n,t,d,m)
    self.clans    = long(clans)
    self.supplies = long(supplies)
    self.targetID = targetID

  def dump(self):
    OreClass.dump(self)
    print "Clans   : ",self.clans
    print "Supplies: ",self.supplies
    print "TargetID: ",self.targetID

  # more methods needed? will see...

  def printXML(self):
    # print "<cargo>"
    OreClass.printXML(self)
    self.printTag( "clans", self.clans );
    self.printTag( "supplies", self.supplies );
    self.printTag( "targetid", self.targetID );
    # print "</cargo>"

#endclass
# --------------------------------------------------------------------

#
# class for data (ships star bases, planets). just a namespace.
#

# do we need this?
class DataSpace:
  nothing = 1
#endclass
# --------------------------------------------------------------------

#
# messages
#

class MessageClass( MyBaseClass ):

  def __init__(self, data=""):

    self.data = ""
    self.typ       = ""
    self.playerID  = -1
    self.objectID  = -1
    self.subject   = ""
    self.is_planet =  0
    self.is_ship   =  0

    if (len( data ) > 0):
      self.initByRSTData( data )
    else:
      print "fehler! data hat nicht > 0 bytes!"

  def printMessage(self):
    print "\nMessage:"
    print "Msg-Typ : ",  self.typ
    print "PlayerID: ",  self.playerID
    print "objectID: ",  self.objectID
    print "subject : ",  self.subject
    print "Komplett:\n", self.data

  def printXML(self):
    print "<message>"
    self.printTag( "msgtype",   self.typ )
    self.printTag( "playerid",  self.playerID )
    self.printTag( "objectid",  self.objectID )
    self.printTag( "firstline",   self.firstline, "string" )
    self.printTag( "subject",   self.subject, "string" )
    self.printTag( "body",      self.data, "string" )
    print "</message>"

  # (-z0047)<<< Sub Space Message >>>  (-> clear to colonize of ship 047, player 0)
  def analyzeFirstLine(self):
    match = re.match("^\(\-(.)(\d)(\d\d\d)\)(.*)$", self.firstline)
    if (len(match.groups()) == 4):
      self.typ      = match.group(1)
      self.playerID = int(match.group(2))
      self.objectID = int(match.group(3))
      self.subject  = match.group(4)

  def initByRSTData(self, data):
    self.data = ""
    self.firstline = ""  # capture the first line (-z0045)<<< ... >>>>
    first = 1
    for i in range(len(data)):
      o = ord(data[i])
      if o >= 13:
        o = o - 13 # decoding
      self.data = self.data + chr(o)

      if (1 == first):
        if (13 != o):
          self.firstline = self.firstline + chr(o)
        else:
          first = 0
      #if
    #for

    self.analyzeFirstLine()
    len_firstline = len(self.firstline)
    self.data = self.data[len_firstline:]
    self.data = self.data.replace(chr(13), chr(10))
    self.data = self.data.replace("<", "&lt;")
    self.data = self.data.replace(">", "&gt;")
    self.subject = self.subject.replace("<", "&lt;")
    self.subject = self.subject.replace(">", "&gt;")
    self.firstline = self.firstline.replace("<", "&lt;")
    self.firstline = self.firstline.replace(">", "&gt;")

  #enddef

#endclass
# --------------------------------------------------------------------


#
# class for native governement
#


class NativeGovernmentClass( MyBaseClass ):
  # govID = -1

  def __init__(self, govID):
   govID = int(govID)
   if govID in range(10):
     self.govID = govID

  def getSPI(self):
    if   self.govID == 0:
      return   0 #     0       none              0%
    elif self.govID == 1:
      return  20 #     1       Anarchy          20%
    elif self.govID == 2:
      return  40 #     2       Pre-Tribal       40%
    elif self.govID == 3:
      return  60 #     3       Early-Tribal     60%
    elif self.govID == 4:
      return  80 #     4       Tribal           80%
    elif self.govID == 5:
      return 100 #     5       Feudal          100%
    elif self.govID == 6:
      return 120 #     6       Monarchy        120%
    elif self.govID == 7:
      return 140 #     7       Representative  140%
    elif self.govID == 8:
      return 160 #     8       Participatory   160%
    elif self.govID == 9:
      return 180 #     9       Unity           180%
    else:
      return 0

  def getID(self):
    return self.govID

  def getName(self):
    if   self.govID == 0:
      return  "none"
    elif self.govID == 1:
      return  "Anarchy"
    elif self.govID == 2:
      return  "Pre-Tribal"
    elif self.govID == 3:
      return  "Early-Tribal"
    elif self.govID == 4:
      return  "Tribal"
    elif self.govID == 5:
      return  "Feudal"
    elif self.govID == 6:
      return  "Monarchy"
    elif self.govID == 7:
      return  "Representative"
    elif self.govID == 8:
      return  "Participatory"
    elif self.govID == 9:
      return  "Unity"
    else:
      return  "[error in NativeGovernementClass]"

#endclass
# --------------------------------------------------------------------


#
# this is the planet class module for python planets:
#

class PlanetClass( MyBaseClass ):

  def __init__(self, data=""):
    self.oldData = DataSpace()  # the TRN is generated by diffing oldData and newData...
    self.newData = DataSpace()

    if (len( data ) == 85):
      self.initByRSTData( data )
    else:
      print "fehler! data hat nicht 85 bytes!"

  def getID(self):
    return self.oldData.id  # id must not change

  def printXML(self):
    print "<planet id='" + str(self.getID()) + "'>"

    self.printTag( "id"        , self.getID() )
    self.printTag( "gov"       , self.oldData.gov.getID()  )
    self.printTag( "gov_name"  , self.oldData.gov.getName()  , "string" )
    self.printTag( "spi"       , self.oldData.gov.getSPI() )
    self.printTag( "race"      , self.oldData.race.getID() )
    self.printTag( "race_name" , self.oldData.race.getName() , "string" )

    # self.printTag( "" , )

    self.printTag( "clans" ,self.oldData.clans     )
    self.printTag( "colonists" ,self.oldData.colonists )
    self.printTag( "supplies" ,self.oldData.supplies  )
    self.printTag( "credits" ,self.oldData.credits   )

    self.printTag( "mines"    , self.oldData.numMines )
    self.printTag( "factories", self.oldData.numFactories )
    self.printTag( "defenses" , self.oldData.numDefenses )

    print "<mined_ore>"
    self.oldData.minedOre.printXML()
    print "</mined_ore>"

    print "<ground_ore>"
    self.oldData.groundOre.printXML()
    print "</ground_ore>"

    print "<ore_density>"
    self.oldData.oreDensity.printXML()
    print "</ore_density>"

# >TODO
    #self.oldData.colonistTax = self.getWORD(data, 65)
    #self.oldData.nativetTax  = self.getWORD(data, 67)
    #self.oldData.colonistHappiness = self.getSignedWORD(data, 69)
    #self.oldData.nativeHappiness   = self.getSignedWORD(data, 71)
    #self.oldData.gov = NativeGovernmentClass( self.getWORD(data, 73) )
    #self.oldData.nativeClans =     self.getDWORD(data, 75)
    #self.oldData.race = RaceClass( self.getWORD( data, 79) )
    #self.oldData.temperature = self.getWORD(data, 81)
    #self.oldData.buildbase    = self.getWORD(data, 83)

    print "</planet>"


  def dump(self):
    self.printXML();
    #print "Dump eines Planeten:"
    #print "ID  : ", self.getID()  # kann sich nicht ändern
    #print "Gov : ", self.oldData.gov.getName()
    #print "SPI : ", self.oldData.gov.getSPI()
    #print "Race: ", self.oldData.race.getName()

  def initByRSTData(self, data):
    self.oldData.owner    = self.getWORD(data, 0)
    self.oldData.id       = self.getWORD(data, 2)
    self.oldData.fCode    = self.getFriendlyCode(data, 4)

    self.oldData.numMines     = self.getWORD(data,  7)# max = CLANS <= 200 ? CLANS : 200+sqrt(CLANS-200)
    self.oldData.numFactories = self.getWORD(data,  9)# max = CLANS <= 100 ? CLANS : 100+sqrt(CLANS-100)
    self.oldData.numDefenses  = self.getWORD(data, 11)# max = CLANS <=  50 ? CLANS :  50+sqrt(CLANS-50)

    self.oldData.minedOre = OreClass( 1,2,3,4 )
    self.oldData.minedOre = OreClass( self.getDWORD(data, 13), self.getDWORD(data, 17), self.getDWORD(data, 21), self.getDWORD(data, 25) )

    self.oldData.clans     = self.getDWORD(data, 29)
    self.oldData.colonists = long(100 * self.oldData.clans)
    self.oldData.supplies  = self.getDWORD(data, 33)
    self.oldData.credits   = self.getDWORD(data, 37)

    self.oldData.groundOre  = OreClass( self.getDWORD(data, 41), self.getDWORD(data, 45), self.getDWORD(data, 49), self.getDWORD(data, 53) )
#                        >4999           abundant
#                        1200..4999      very common
#                        600..1199       common
#                        100..599        rare
#                        1..99           very rare
#                        0               none

    self.oldData.oreDensity = OreClass( self.getWORD(data, 57), self.getWORD(data, 59), self.getWORD(data, 61), self.getWORD(data, 63) )
                        # 70..100 large masses    1 mine extracts 1 kt
                        # 40..69  concentrated    2 mines extract 1 kt
                        # 30..39  dispersed       3 mines extract 1 kt
                        # 10..29  scattered       5 mines extract 1 kt
                        # 0..9    very scattered  10 mines extract 1 kt
                # The amount of minerals extracted is (number of mines) *
                # (density) * (mining rate) / 10000, where "/10000"
                # compensates the fact that the mining rate and density are
                # both percentages. Double the mining rate on Reptilian
                # planets.

    self.oldData.colonistTax = self.getWORD(data, 65)
    self.oldData.nativetTax  = self.getWORD(data, 67)
                # Note that THost blocks taxes above 75% when hisssssing,
                # but allows up to 100% otherwise.

# 999: problem: negative numbers allowed: (method in rawdata.py is incorrect!!!!
    self.oldData.colonistHappiness = self.getSignedWORD(data, 69)
    self.oldData.nativeHappiness   = self.getSignedWORD(data, 71)
                        # 90..100 happy
                        # 70..89  calm
                        # 50..69  unhappy
                        # 40..49  very angry
                        # 20..39  rioting
                        # <20     fighting
                        # Tax collection possible for happiness>=40, population grows
                        # if >=70.


    self.oldData.gov = NativeGovernmentClass( self.getWORD(data, 73) )

    self.oldData.nativeClans =     self.getDWORD(data, 75)
    self.oldData.race = RaceClass( self.getWORD( data, 79) )

    self.oldData.temperature = self.getWORD(data, 81)
    self.oldData.buildbase    = self.getWORD(data, 83)


    self.newData = copy.deepcopy(self.oldData)
#endclass
# --------------------------------------------------------------------

#
# class for a race
#

class RaceClass:

  def __init__(self, raceID):
    self.names = ["none","Humanoid", "Bovinoid","Reptilian","Avian","Amorphous","Insectoid","Amphibian","Ghipsoldal","Siliconoid"]
    raceID = int(raceID)
    if ((raceID > 0) and (raceID <= 9)):
      self.raceID = raceID
    else:
      self.raceID = 0  # none

  def getName(self):
    if ((self.raceID >= 0) and (self.raceID <= 9)):
      return self.names[self.raceID]
    return "[error]"

  def getID(self):
    return self.raceID

  def getMiningFactor( self ):
    if self.names[ self.raceID ] == 'Reptilian':
      return 2
    return 1
#endclass
# --------------------------------------------------------------------


#
# this is the ship class module for python planets:
#

class ShipClass( MyBaseClass ):

  def __init__(self, data=""):
    self.oldData = DataSpace()  # the TRN is generated by diffing oldData and newData...
    self.newData = DataSpace()

    if (len( data ) == 107):
      self.initByRSTData( data )
    else:
      print "fehler! data hat nicht 107 bytes!"

  def getName(self):
    return self.newData.name

  def printXML(self):
    print "<ship id='" + str(self.oldData.id) + "'>"

    self.printTag( "id"   , self.oldData.id    )
    self.printTag( "owner", self.oldData.owner )
    self.printTag( "fcode", self.oldData.fCode, "string" )
    self.printTag( "name" , self.newData.name,  "string" )

    self.printTag( "x", self.oldData.x )
    self.printTag( "y", self.oldData.y )
    self.printTag( "distx", self.oldData.distX )
    self.printTag( "disty", self.oldData.distY )

    print "<shipcargo>"
    self.oldData.cargo.printXML()
    print "</shipcargo>"

    print "<unloadcargo>"
    self.oldData.unloadCargo.printXML()
    print "</unloadcargo>"

    print "<transfercargo>"
    self.oldData.transferCargo.printXML()
    print "</transfercargo>"

    print "</ship>"


  def getID(self):
    return self.oldData.id

  def dump(self):
    self.printXML();
    #print "ID   :", self.oldData.id
    #print "Owner:", self.oldData.owner
    #print "FCode:", self.oldData.fCode
    #print "X Y distX, distY:",self.oldData.x,self.oldData.y,self.oldData.distX,self.oldData.distY
    #print "Cargo on Ship:"
    #self.oldData.cargo.dump()
    #print "Unload Cargo:"
    #self.oldData.unloadCargo.dump()
    #print "Transfer of Cargo to enemy ship:"
    #self.oldData.transferCargo.dump()

  def initByRSTData(self, data):
    self.oldData.id       = self.getWORD(data, 0)
    self.oldData.owner    = self.getWORD(data, 2)
    self.oldData.fCode    = self.getFriendlyCode(data, 4)

    self.oldData.warp  = self.getSignedWORD(data,  7)
    self.oldData.distX = self.getSignedWORD(data,  9) # (destination-X minus X-position)
    self.oldData.distY = self.getSignedWORD(data, 11) # (-3000...3000)

    self.oldData.x = self.getWORD(data, 13)
    self.oldData.y = self.getWORD(data, 15)
                # Usually between 1 and 4000 or 1 and 10000. Ships with (X,Y)
                # = (0,0) should not appear, as this makes problems with the
                # SHIPXYx.DAT file and probably many clients.

    self.oldData.engineType = self.getWORD(data, 17) # (1..9, index into ENGSPEC.DAT)
    self.oldData.hullType   = self.getWORD(data, 19) # (1..105, index into HULLSPEC.DAT)
    self.oldData.beamType   = self.getWORD(data, 21) # (0 for none, 1..10 = index into BEAMSPEC.DAT else). See note
    self.oldData.numBeams   = self.getWORD(data, 23)
    self.oldData.numFighter = self.getWORD(data, 25)
    self.oldData.torpedoLauncherType = self.getWORD(data, 27)
    self.oldData.numTorpedos         = self.getWORD(data, 29)
    self.oldData.numTorpedoLaunchers = self.getWORD(data, 31)

    self.oldData.mission    = self.getWORD(data, 33)

    self.oldData.primaryEnemy = self.getWORD(data, 35)
    self.oldData.shipToTow    = self.getWORD(data, 37)
    self.oldData.damage       = self.getWORD(data, 39)
    self.oldData.crew         = self.getWORD(data, 41)

    self.oldData.name         = self.getString(data, 45,20)

    # CARGO on Ship:
    clans = self.getWORD(  data, 43)  # will go to cargo class...
    n     = self.getWORD(  data, 65 )
    t     = self.getWORD(  data, 67 )
    d     = self.getWORD(  data, 69 )
    m     = self.getWORD(  data, 71 )
    supplies = self.getWORD(  data, 73 )
    self.oldData.cargo = CargoClass( n,t,d,m, clans, supplies )

    # Unload Cargo to planet:
    n     = self.getWORD(  data, 75 )
    t     = self.getWORD(  data, 77 )
    d     = self.getWORD(  data, 79 )
    m     = self.getWORD(  data, 81 )
    clans = self.getWORD(  data, 83 )  # will go to cargo class...
    supplies = self.getWORD(  data, 85 )
    targetID = self.getWORD(  data, 87 )  # will go to cargo class...
    self.oldData.unloadCargo = CargoClass( n,t,d,m, clans, supplies, targetID )

    # Transfer to enemy ship:
    n     = self.getWORD(  data, 89 )
    t     = self.getWORD(  data, 91 )
    d     = self.getWORD(  data, 93 )
    m     = self.getWORD(  data, 95 )
    clans = self.getWORD(  data, 97 )  # will go to cargo class...
    supplies = self.getWORD(  data, 99 )
    targetID = self.getWORD(  data, 101)  # will go to cargo class...
    self.oldData.transferCargo = CargoClass( n,t,d,m, clans, supplies, targetID )

    self.oldData.interceptID = self.getWORD(  data, 103 )

    self.oldData.credits     = self.getWORD(  data, 105 )

    self.newData = copy.deepcopy(self.oldData)

#endclass
# --------------------------------------------------------------------

#
# ship coordinates as defined in rst file
#

class ShipCoordsClass( MyBaseClass ):

  def __init__(self, data="", shipid=-1):
    self.x = 0
    self.y = 0
    self.owner = 0
    self.mass  = 0

    if (len( data ) == 8):
      self.initByRSTData( data, shipid )
    else:
      print "fehler! data hat nicht 8 bytes!"

  def getX(self):
    return self.x

  def getY(self):
    return self.y

  def getOwner(self):
    return self.owner

  def getMass(self):
    return self.mass

  def initByRSTData(self, data, shipid):
    self.shipid    = shipid  # id of the ship
    self.x     = self.getWORD(data, 0)
    self.y     = self.getWORD(data, 2)
    self.owner = self.getWORD(data, 4)
    self.mass  = self.getWORD(data, 6)

  def printXML(self):
    print "<shipcoords shipid='" + str(self.shipid) + "'>"
    self.printTag( "shipid", self.shipid )
    self.printTag( "x" , self.x )
    self.printTag( "y" , self.y )
    self.printTag( "owner", self.owner )
    self.printTag( "mass" , self.mass  )
    print "</shipcoords>"

#endclass
# --------------------------------------------------------------------

#
# this is the star base class module for python planets
#

class StarbaseClass( MyBaseClass ):

  def __init__(self, data=""):
    self.oldData = DataSpace()  # the TRN is generated by diffing oldData and newData...
    self.newData = DataSpace()
    if (len( data ) == 156):
      self.initByRSTData( data )
    else:
      print "fehler! data hat nicht 156 bytes!"

  def getID(self):
    return self.oldData.id

  def initByRSTData(self, data):
    self.oldData.id       = self.getWORD(data, 0) #= Id of planet with this base
    self.oldData.owner    = self.getWORD(data, 2) # 1..11
    self.oldData.defense  = self.getWORD(data, 4) # 0..200
    self.oldData.damage   = self.getWORD(data, 6) # 0..100

    # TECH-LEVELS:
    self.oldData.engineTechLevel  = self.getWORD(data,  8) # 1..10
    self.oldData.hullsTechLevel   = self.getWORD(data, 10) # 1..10
    self.oldData.weaponTechLevel  = self.getWORD(data, 12) # 1..10
    self.oldData.torpedoTechLevel = self.getWORD(data, 14) # 1..10: Tech upgrades: going from Tech N to N+1 costs 100*N mc.

    # STORAGE:
    self.oldData.engineStorage = StorageClass(9)
    for i in range(9):
      self.oldData.engineStorage.setCountAtPos( i+1 , self.getWORD(data, 16+(2*i)) )

    self.oldData.hullStorage = StorageClass(20)
    for i in range(20):
      self.oldData.hullStorage.setCountAtPos( i+1 , self.getWORD(data, 34+(2*i)) )

    self.oldData.beamWeaponStorage = StorageClass(10)
    for i in range(10):
      self.oldData.beamWeaponStorage.setCountAtPos( i+1 , self.getWORD(data, 74+(2*i)) )

    self.oldData.torpedoLauncherStorage = StorageClass(10)
    for i in range(10):
      self.oldData.torpedoLauncherStorage.setCountAtPos( i+1 , self.getWORD(data, 94+(2*i)) )

    self.oldData.torpedoStorage = StorageClass(10)
    for i in range(10):
      self.oldData.torpedoStorage.setCountAtPos( i+1 , self.getWORD(data, 114+(2*i)) )


    self.oldData.numFighters        = self.getWORD(data,134)
    self.oldData.idOfShipToRecycled = self.getWORD(data,136)
    self.oldData.actionForShip      = self.getWORD(data,138) # 0,1,2 = nothing,fix,recycle
    self.oldData.mission       = self.getWORD(data,140) # 1..6: primary order

    self.oldData.shipToBuild   = self.getWORD(data,142) # 0 = no build order

    self.oldData.engineType    = self.getWORD(data,144)
    self.oldData.beamType      = self.getWORD(data,146)
    self.oldData.beamCount     = self.getWORD(data,148)
    self.oldData.torpedoType   = self.getWORD(data,150)
    self.oldData.torpedoCount  = self.getWORD(data,152)

    self.oldData.shouldBeZero      = self.getWORD(data,154)

    # Now copy the data to "newdata". so now the old data is save, and
    # we can create the trn file later:
    self.newData = copy.deepcopy(self.oldData)

  def printXML(self):
      print "<starbase id='" + str(self.oldData.id) + "'>"
      self.printTag( "id",      self.oldData.id )
      self.printTag( "owner",   self.oldData.owner )
      self.printTag( "defense", self.oldData.defense )
      self.printTag( "damage",  self.oldData.damage )

      self.printTag( "engine_tech",  self.oldData.engineTechLevel )
      self.printTag( "hull_tech",    self.oldData.engineTechLevel )
      self.printTag( "weapon_tech",  self.oldData.weaponTechLevel )
      self.printTag( "torpedo_tech", self.oldData.torpedoTechLevel )

      # storage:
      self.oldData.engineStorage.printXML(          "engine_storage"  )
      self.oldData.hullStorage.printXML(            "hull_storage"    )
      self.oldData.beamWeaponStorage.printXML(      "beamweapon_storage"      )
      self.oldData.torpedoLauncherStorage.printXML( "torpedolauncher_storage" )
      self.oldData.torpedoStorage.printXML(         "torpedo_storage" )

      self.printTag( "numfighters"  , self.oldData.numFighters )
      self.printTag( "shiptorecycle", self.oldData.idOfShipToRecycled )
      self.printTag( "actionforship", self.oldData.actionForShip )
      self.printTag( "mission"      , self.oldData.mission )
      self.printTag( "shiptobuild"  , self.oldData.shipToBuild )
      self.printTag( "beamtype"     , self.oldData.engineType )
      self.printTag( "beamcount"    , self.oldData.beamCount )
      self.printTag( "torpedotype"  , self.oldData.torpedoType )
      self.printTag( "torpedocount" , self.oldData.torpedoCount )

      self.printTag( "shouldbezero" , self.oldData.shouldBeZero )

      print "</starbase>"

#endclass
# --------------------------------------------------------------------


#
# Storage class for star bases. something like a array wrapper...
#
# BUT: indices = 1..count
#

class StorageClass( MyBaseClass ):

  def __init__(self, count): # count is the number of objects to store
    self.inStore = [] # index: 1..9 -> engspec.dat
    self.count = count
    for i in range(count + 1):
      # print "-",i,"-"
      #self.inStore[i] = 0
      self.inStore.append(0)

  def dump(self):
    print "Storage (has a size of",self.count,"):"
    for i in range(self.count + 1):
      if (i >= 1):
        print i,": Count=",self.inStore[i]

  def clear(self):
    for i in range(self.count + 1):
      if i > 0:
        self.inStore[i] = 0

  def setCountAtPos( self, id, num ):
    if (id >= 1) and (id <= self.count):
      self.inStore[ id ] = int(num)

  def printXML( self, name_of_storage_device ):
    name = name_of_storage_device
    for i in range(9):
      self.printTag( name, self.inStore[i], "int", "dev='" + str(i) + "'" )

  # more methods needed?

#endclass
# --------------------------------------------------------------------

#
# VCRClass and VCRObject:
#

class VCRObject( MyBaseClass ):
  def __init__(self, data):
    self.name   = self.getString(data,0,20)
    self.damage = self.getWORD(data, 20)
    self.crew   = self.getWORD(data, 22)
    self.id     = self.getWORD(data, 24)
    self.owner  = self.getWORD(data, 26)
    self.picnum = self.getByteInt(data, 28)  # or: getByteChar, getByteOrd  ??

    self.unknown1 = self.getByteInt(data, 29) # Host: zero (Host addresses +28 as a WORD)
                                         # PHost: hull number of ship, 0 for planets
                                         # or in older PHosts.
    self.beamsType    = self.getWORD(data, 30)
    self.numBeams     = self.getWORD(data, 32)
    self.fighterTubes = self.getWORD(data, 34)
    self.torpedoType  = self.getWORD(data, 36)
    self.numFightersTorps     = self.getWORD(data, 38)
    self.numTorpedoLaunchers  = self.getWORD(data, 40)
  #enddef
#endclass
# --------------------------------------------------------------------


class VCRClass( MyBaseClass ):

  def __init__(self, data=""):
    if (len( data ) == 100):
      self.initByRSTData( data )
    else:
      print "fehler! data hat nicht 100 bytes!"
      exit

  def initByRSTData(self, data):
    self.random_init       = self.getWORD(data,  0)
    self.signature         = self.getWORD(data,  2)
    self.temperature_code1 = self.getWORD(data,  4)
    self.battletype        = self.getWORD(data,  6) # 0 = ship:ship, 1=ship:planet
    self.mass_left         = self.getWORD(data,  8)
    self.mass_right        = self.getWORD(data, 10)

    self.leftObject   = VCRObject(self.getString(data, 12, 42))
    self.rightObject  = VCRObject(self.getString(data, 54, 42))

    self.shieldLeft   = self.getWORD(data, 96)
    self.shieldRight  = self.getWORD(data, 98)
  #enddef

  def dump(self):
    if self.battleType == 0:
      right = "Ship"
    else:
      right = "Planet"
    print "Battle with Ship", leftObject.id, "and", right, rightObject.id
  #enddef

#endclass
# --------------------------------------------------------------------

#
# visual contacts as defined in rst file
#

class VisualContactClass( MyBaseClass ):

  def __init__(self, data=""):
    if (len( data ) == 34):
      self.initByRSTData( data )
    else:
      print "fehler! data hat nicht 34 bytes!"

  def getID(self):
    return self.id

  def getOwner(self):
    return self.owner

  def getName(self):
    return self.name

  def getX(self):
    return self.x

  def getY(self):
    return self.y

  def getHullType(self):
    return self.hullType

  def initByRSTData(self, data):
    self.id       = self.getWORD(data, 0)
    self.owner    = self.getWORD(data, 2)
    self.warp     = self.getWORD(data, 4)
    self.x        = self.getWORD(data, 6)
    self.y        = self.getWORD(data, 8)
    self.hullType = self.getWORD(data,10)
    self.heading  = self.getWORD(data,12)
    self.name     = self.getString(data, 14,20)

  def printXML(self):
    self.printTag( "id", self.id )
    self.printTag( "owner", self.owner )
    self.printTag( "warp", self.warp )
    self.printTag( "x", self.x )
    self.printTag( "y", self.y )
    self.printTag( "hulltype", self.hullType )
    self.printTag( "heading", self.heading )
    self.printTag( "name", self.name, "string" )
#endclass
# --------------------------------------------------------------------

#
# class for XYPLAN.DAT / PLANETS.NM - elements:
#
# all instances will be collected in an array[planetID]->xyplanclass
#

class XYPlanClass( MyBaseClass ):

  def __init__(self, data, name):   # xyplan.dat: x,y,owner (6 bytes), planets.nm: string[20]
    self.x   = 0
    self.y   = 0
    # self.owner in xyplan.dat is always zero (0)
    self.name= "<unknown>"

    if (len( data ) == 6):
      self.initByRSTData( data )
      self.name = name
    else:
      print "fehler! data hat nicht 85 bytes!"

  def dump(self):
    print "x y name: ",self.x, self.y, self.name

  def initByRSTData(self, data):
    self.x      = self.getWORD(data, 0)
    self.y      = self.getWORD(data, 2)
    # self.owner = self.getWORD(data, 4) == 0

  #

#endclass
# ----------------------------------------------------------------------------

class MainApp( MyBaseClass ):

  def __init__( self ):
    self.shipVec      = {}
    self.planetVec    = {}
    self.starbaseVec  = {}
    self.shipCoordVec = {}
    self.visualContactVec = {}
    self.battleVec    = {}
    self.xyplanVec    = {}   # xyplant.dat merged with planets.nm

    self.lastObjectType = 1    # 1 = planets, 2 = ships, besser wären Konstanten o.ä.
    self.lastPlanetID = -1   # ID des zuletzt selektierten Planeten
    self.lastShipID   = -1   # selbiges für Schiffe

    #
    # self.loadStaticPlanetsData('dat/xyplan.dat', 'dat/planet.nm')

  # -------------------

  def decodePassword( self, pw ):
    return pw
    tmp = ""
    length = len(pw)
    for i in range(length):
      if (ord(pw[i]) > 50):
        tmp = tmp + chr(ord(pw[i]) - 50)
      else:
        tmp = tmp + pw[i]
    return tmp
  #enddef

  def loadRST(self,filename):
    self.shipVec     = {}
    self.planetVec   = {}
    self.starbaseVec = {}
    self.shipCoordVec = {}
    self.visualContactVec = {}
    self.battleVec    = {}

    print "<rstfile>";
    print "<!-- converted with rst2xml V" + version + " -->"    
    self.printTag( "filename", filename, "string" ) # print "reading RST file \"",filename,"\""
    ptrs = {}
    file = open( filename, "rb" )
    # data ptr auslesen:
    for i in range(8):
      print "<!-- reading pointer ", i, "-->"
      # file.seek(i*4)
      data = file.read(4)
      ptr  = self.getDWORD(data, 0)
      ptrs[i+1] = ptr - 1
    # endfor
    # read more header info:
    file.seek(32)
    self.rstSignature = self.getString(file.read(6), 0, 6)
    self.rstSignature = self.rstSignature + self.getString(file.read(2), 0, 2)
    winplanDataPtr    = self.getDWORD(file.read(4), 0)
    leechxDataPtr     = self.getDWORD(file.read(4), 0)

    # print header dat:
    print "<rst_signature>" + self.rstSignature + "</rst_signature>"
    # print "RST Signature   : ", self.rstSignature
    print "<!-- Winplan Data Ptr: ", winplanDataPtr, "-->"
    print "<!-- LEECHx.DAT   Ptr: ", leechxDataPtr, "-->"
    print "<!-- Section Pointers: -->"
    for i in range(8):
      print "<!-- Ptr to section",i+1,"is",ptrs[i+1], "-->"

    # section 1: reading ship data:
    print "<ships>"
    if (ptrs[1] > 0):
      file.seek(ptrs[1])
      count = self.getWORD(file.read(2), 0)
      self.printTag( "num_ships", count )
      for i in range(count):
        # print "\nreading ship no.",i+1,":"
        ship = ShipClass( file.read(107) )      # create new ship
        ship.printXML()
    else:
      print "Warning: no ship records in RST file?"
    print "</ships>"

    print "<!-- SECTION2; visual contacts -->"  # 999 debug

    # section 2: visual contacts (34 bytes)
    print "<visual_contacts>"
    if (ptrs[2] > 0):
      file.seek(ptrs[2])
      count = self.getWORD(file.read(2), 0)
      self.printTag( "num_visual_contacts", count )
      for i in range(count):
        # print "reading visual contact no.",i+1,": ",
        viscontact = VisualContactClass( file.read(34) )      # create new planet
        print "<visual_contact id='" + str(viscontact.id) + "'>"
        viscontact.printXML()
        print "</visual_contact>"
    else:
      print "Warning: no visual contacts in RST file."
    print "</visual_contacts>"


    print "<!-- SECTION3: planets -->"  # 999 debug

    # section 3: reading planet data:
    print "<planets>"
    if (ptrs[3] > 0):
      file.seek(ptrs[3])
      count = self.getWORD(file.read(2), 0)
      self.printTag( "num_planets", count )
      for i in range(count):
        planet = PlanetClass( file.read(85) )      # create new planet
        planet.printXML()
        self.planetVec[ planet.getID() ] = planet
    else:
      self.printTag( "num_planets", 0 )
      self.printTag( "warning", "Warning: no planet records in RST file."  )
    print "</planets>"


    print "<!-- SECTION4: STARBASES -->"  # 999 debug

    # section 4: starbases
    print "<starbases>"
    if (ptrs[4] > 0):
      file.seek(ptrs[4])
      count = self.getWORD(file.read(2), 0)
      self.printTag( "num_starbases", count )
      for i in range(count):
        # print "reading star base no.",i+1,": ",
        starbase = StarbaseClass( file.read(156) )      # create new star base
        starbase.printXML()
        # print "ID:",starbase.getID(), "(= the planet's ID)"
        # self.starbaseVec[ starbase.getID() ] = starbase
    else:
      self.printTag( "num_starbases", 0 )
      self.printTag( "warning", "Warning: no star bases in RST file.", string )
    print "</starbases>"


    print "<!-- section5: messages -->"

    # section 5: messages (phost dat files?)
    print "<messages>"
    msgptr_hash = {}
    if (ptrs[5] > 0):
      self.printTag( "warning", "messages not fully implemented yet.", "string" )
      file.seek(ptrs[5])
      count = self.getWORD(file.read(2), 0)
      self.printTag( "num_messages", count ) # print "\nthere are",count, "messages..."
      for i in range(count):
        # print "reading message no.",i+1,": ",
        data = file.read(6)
        msg_ptr = self.getDWORD(data,0) - 1
        msg_len = self.getWORD(data,4)
        msgptr_hash[ msg_ptr ] = msg_len
        # print "Message at",msg_ptr,", len=",msg_len
    else:
      self.printTag( "num_messages", 0 )
      self.printTag( "warning", "Warning: no messages in RST file.", string )

    for msg_ptr in msgptr_hash.keys():
      msg_len =msgptr_hash[ msg_ptr ]
      file.seek( msg_ptr )
      data = file.read( msg_len )
      msg = MessageClass(data)
      msg.printXML()  # msg.printMessage()
    #endfor
    print "</messages>"

    print


    # section 6: ship coordinates
    print "<shipcoordinates>"
    if (ptrs[6] > 0):
      file.seek(ptrs[6])
      if ((ptrs[7] - ptrs[6]) == 4000):
        count = 500
      else:
        count = 999
      self.printTag( "num_shipcoordinates", count )
      for i in range(count):
        #print "reading ship coords no.",i+1,":"
        ship_id   = i + 1
        shipcoord = ShipCoordsClass( file.read(8), ship_id )  # create new ship coords instance
        if ((shipcoord.x != 0) and (shipcoord.y != 0)):
          shipcoord.printXML();
          # print "ship coords: id x y owner mass:",ship_id,shipcoord.x,shipcoord.y,shipcoord.owner,shipcoord.mass
          # self.shipCoordVec[ ship_id ] = shipcoord
    else:
      self.printTag( "num_shipcoordinates", 0 )
      self.printTag( "warning", "Warning: no ship coords in RST file.", "string" )
    print "</shipcoordinates>"


    # section 7: GENx.DAT
    if (ptrs[7] > 0):
      file.seek(ptrs[7])
      data = file.read(144)
      self.RST_TimeStamp    = self.getString(data,   0, 18)
      self.RST_ScoreRecords = self.getString(data,  18, 88)
      self.RST_PlayerID     = self.getWORD(  data, 106)
      self.RST_Password     = self.decodePassword( self.getString(data, 108, 20) )

      self.RST_CheckShipBlock     = self.getDWORD(data, 128)
      self.RST_CheckPlanetBlock   = self.getDWORD(data, 132)
      self.RST_CheckStarbaseBlock = self.getDWORD(data, 136)
      self.RST_TurnNumber         = self.getWORD( data, 140)
      self.RST_CheckTimeStamp     = self.getWORD( data, 142)

      self.printTag( "timestamp"  ,self.RST_TimeStamp , "string")
      self.printTag( "playerid"   ,self.RST_PlayerID            )
      pw = self.RST_Password.replace(">", "&gt;")
      pw = pw.replace("<", "&lt;")
      self.printTag( "password"   ,pw  , "string")
      self.printTag( "turnnumber" ,self.RST_TurnNumber          )
    else:
      self.PrintTag( "warning", "Warning: no GENx.DAT record in RST file.", "string" )


    # section 8: VCRs  - untested! 999 missing: VCRObjects !?
    if (ptrs[8] <= 0):
      print "<!-- No VCR records in RST file -->"
    else:
      print "<!-- reading VCRs: -->"
      file.seek(ptrs[8])
      count = self.getWORD(file.read(2), 0)
      print "<!-- ",count,"VRCs to read.","-->"
      for i in range(count):
        print "<!-- reading VCR",i,"-->"
        data = file.read(100)
        vcr  = VCRClass(data)
        vcr.dump()
        self.battleVec[i] = vcr

    # other stuff:?

    file.close
    print "</rstfile>";

#endclass

app = MainApp()
app.loadRST( rst_filename )

